diff --git a/public/assets/scaffolding-min.js b/public/assets/scaffolding-min.js index 075e2ad..6a87bcf 100644 --- a/public/assets/scaffolding-min.js +++ b/public/assets/scaffolding-min.js @@ -30,7 +30,7 @@ String.prototype.toPath = function () { var font = { size: 0 }; -var copyColourFallback = function copyColourFallback(copyInfo, attr) { +var copyFallback = function copyFallback(copyInfo, attr) { console.log("fallback"); var textArea = document.createElement('textarea'); textArea.value = copyInfo; @@ -135,7 +135,6 @@ module.exports = { oneClickSelect: function oneClickSelect(e) { var t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : e.currentTarget; // In here, "this" is the element - alert("here"); var range, selection; if (window.getSelection) { selection = window.getSelection(); @@ -157,10 +156,10 @@ module.exports = { navigator.clipboard.writeText(c).then(function () { showMessage("Copied ".concat(w, ".")); }, function (e) { - copyColourFallback(c, w); + copyFallback(c, w); }); } else { - copyColourFallback(c, w); + copyFallback(c, w); } }, positionTooltip: function positionTooltip() { @@ -179,6 +178,21 @@ module.exports = { var b = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; return window.getComputedStyle(el, null).getPropertyValue(prop); }, + getTabPath: function getTabPath(t) { + var url = window.location.toString(); + url = url.indexOf("?") > 0 ? url.substring(0, url.indexOf("?")) : url; + url = "".concat(url, "?p=").concat(t.closest("article").getAttribute("data-path"), "#").concat(t.getAttribute("id")); + var type = "URL"; + if (navigator.clipboard) { + navigator.clipboard.writeText(url).then(function () { + showMessage("Copied ".concat(type, ".")); + }, function (e) { + copyFallback(url, type); + }); + } else { + copyFallback(url, type); + } + }, init: function init() { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var url = getURLVars(); @@ -3193,10 +3207,30 @@ var click = new MouseEvent('click', { bubbles: false, cancelable: true }); +var waitForElement = function waitForElement(selector) { + return new Promise(function (resolve) { + if (document.querySelector(selector)) { + return resolve(document.querySelector(selector)); + } + var observer = new MutationObserver(function (mutations) { + if (document.querySelector(selector)) { + observer.disconnect(); + resolve(document.querySelector(selector)); + } + }); + + // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336 + observer.observe(document.body, { + childList: true, + subtree: true + }); + }); +}; function init() { - var p = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; - var s = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - p.querySelectorAll(".tab-group, tabset").forEach(function (tabGroup) { + var container = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; + var spacer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + var args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + container.querySelectorAll(".tab-group, tabset").forEach(function (tabGroup) { if (tabGroup.querySelector("[role=tablist]") === null) { var tabgroup = tabGroup.getAttribute("id"); var tablist = ""; @@ -3219,23 +3253,31 @@ function init() { }); var ul = document.createElement('ul'); ul.setAttribute("role", "tablist"); - ul.innerHTML = s != true ? "".concat(tablist) : "".concat(tablist, "
  • "); + ul.innerHTML = spacer != true ? "".concat(tablist) : "".concat(tablist, "
  • "); tabGroup.insertBefore(ul, tabGroup.firstChild); tabGroup.querySelectorAll('[role="tab"]').forEach(function (tab) { - tab.addEventListener("click", function () { - var siblings = Array.from(tab.parentNode.children); - siblings.forEach(function (sibling) { - return sibling.classList.remove("selected"); - }); - tab.classList.add("selected"); - var tabPanels = Array.from(tab.parentNode.parentNode.children).filter(function (child) { - return child.getAttribute("role") === "tabpanel"; - }); - tabPanels.forEach(function (panel) { - return panel.classList.remove("open"); - }); - var tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel"); - document.getElementById(tabPanelId).classList.add("open"); + tab.addEventListener("click", function (e) { + if (e.altKey && typeof args.altModifier == "function") { + args.altModifier(tab); + } else if (e.shiftKey && typeof args.shiftModifier == "function") { + args.shiftModifier(tab); + } else if (e.metaKey && typeof args.metaModifier == "function") { + args.metaModifier(tab); + } else { + var siblings = Array.from(tab.parentNode.children); + siblings.forEach(function (sibling) { + return sibling.classList.remove("selected"); + }); + tab.classList.add("selected"); + var tabPanels = Array.from(tab.parentNode.parentNode.children).filter(function (child) { + return child.getAttribute("role") === "tabpanel"; + }); + tabPanels.forEach(function (panel) { + return panel.classList.remove("open"); + }); + var tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel"); + document.getElementById(tabPanelId).classList.add("open"); + } }); tab.addEventListener("keypress", function (e) { e.preventDefault(); @@ -3246,7 +3288,10 @@ function init() { }); } if (document.location.hash != "" && document.location.hash.substring(0, 5) == "#tab-") { - document.querySelector(document.location.hash).dispatchEvent(click); + waitForElement(document.location.hash).then(function (el) { + el.scrollIntoView(); + el.dispatchEvent(click); + }); } }); } @@ -3369,10 +3414,12 @@ __webpack_require__.r(__webpack_exports__); -// init core +// init c _core_core_js__WEBPACK_IMPORTED_MODULE_0__.init({ success: function success(a) { - _pg_patterns_core_tabs_tabs_js__WEBPACK_IMPORTED_MODULE_9__.init(a); + _pg_patterns_core_tabs_tabs_js__WEBPACK_IMPORTED_MODULE_9__.init(a, true, { + shiftModifier: _core_core_js__WEBPACK_IMPORTED_MODULE_0__.getTabPath + }); _pg_patterns_core_switch_switch_js__WEBPACK_IMPORTED_MODULE_8__.init(a); _pg_patterns_core_sticky_note_sticky_note_js__WEBPACK_IMPORTED_MODULE_7__.init(a); }, diff --git a/public/patterns/core/tabs/index.html b/public/patterns/core/tabs/index.html index d09d9a9..c0a3d12 100644 --- a/public/patterns/core/tabs/index.html +++ b/public/patterns/core/tabs/index.html @@ -24,6 +24,14 @@

    Users should not need to see content of multiple tabs simultaneously and the user should be able to easily recognise where they are within the content.

    How to use it

    The structure of the tab set is defined in html. There are two forms supported. Adding a class of .tab-group to the container element will work in place of the tabset tag, and the tab panels can be defined using either tab="" or data-tab="". Passing an optional element to the init function will initialise tabs within that element.

    +

    The tab initalize function now takes a new function parameter in the form of an third argument is an object that can take the following callbacks:

    + +

    You can use these callbacks to create a custom event to get the tab information.

    +

    Note: There is a new core function (core.getTabPath) that will generate the query string and url hash for use in DS2 Core. You may wish to update your scaffolding.js file to make use of this functionality.

             
    @@ -176,9 +184,31 @@ const click = new MouseEvent('click', {
     	cancelable: true
     });
     
    -export function init(p = document, s = true) { 
    +const waitForElement = (selector) => {
    +    return new Promise(resolve => {
    +        if (document.querySelector(selector)) {
    +            return resolve(document.querySelector(selector));
    +        }
     
    -	p.querySelectorAll(".tab-group, tabset").forEach(tabGroup => {
    +        const observer = new MutationObserver(mutations => {
    +            if (document.querySelector(selector)) {
    +                observer.disconnect();
    +                resolve(document.querySelector(selector));
    +            }
    +        });
    +
    +        // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
    +        observer.observe(document.body, {
    +            childList: true,
    +            subtree: true
    +        });
    +    });
    +}
    +
    +
    +export function init(container = document, spacer = true, args = {}) { 
    +
    +	container.querySelectorAll(".tab-group, tabset").forEach(tabGroup => {
     
     		if (tabGroup.querySelector("[role=tablist]") === null) {
     			const tabgroup = tabGroup.getAttribute("id");
    @@ -204,21 +234,29 @@ export function init(p = document, s = true) {
     
     			const ul = document.createElement('ul');
     			ul.setAttribute("role", "tablist");
    -			ul.innerHTML = s != true ? `${tablist}` : `${tablist}`;
    +			ul.innerHTML = spacer != true ? `${tablist}` : `${tablist}`;
     			tabGroup.insertBefore(ul, tabGroup.firstChild);
     
     			tabGroup.querySelectorAll('[role="tab"]').forEach(tab => {
    -				tab.addEventListener("click", () => {
    -					const siblings = Array.from(tab.parentNode.children);
    -					siblings.forEach(sibling => sibling.classList.remove("selected"));
    -					tab.classList.add("selected");
    +				tab.addEventListener("click", (e) => {
    +					if (e.altKey && typeof args.altModifier == "function") {
    +						args.altModifier(tab);
    +					} else if (e.shiftKey && typeof args.shiftModifier == "function") {
    +						args.shiftModifier(tab);
    +					} else if (e.metaKey && typeof args.metaModifier == "function") {
    +						args.metaModifier(tab);
    +					} else {
    +						const siblings = Array.from(tab.parentNode.children);
    +						siblings.forEach(sibling => sibling.classList.remove("selected"));
    +						tab.classList.add("selected");
     
    -					const tabPanels = Array.from(tab.parentNode.parentNode.children)
    -										   .filter(child => child.getAttribute("role") === "tabpanel");
    -					tabPanels.forEach(panel => panel.classList.remove("open"));
    +						const tabPanels = Array.from(tab.parentNode.parentNode.children)
    +											   .filter(child => child.getAttribute("role") === "tabpanel");
    +						tabPanels.forEach(panel => panel.classList.remove("open"));
     
    -					const tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel");
    -					document.getElementById(tabPanelId).classList.add("open");
    +						const tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel");
    +						document.getElementById(tabPanelId).classList.add("open");
    +					}
     				});
     
     
    @@ -232,7 +270,11 @@ export function init(p = document, s = true) {
     		}
     
     		if (document.location.hash != "" && document.location.hash.substring(0,5) == "#tab-") {
    -			document.querySelector(document.location.hash).dispatchEvent(click);
    +			waitForElement(document.location.hash).then((el) => {
    +				el.scrollIntoView();
    +				el.dispatchEvent(click);
    +			});
    +
     		}
     	});
     }
    diff --git a/src/js/core/_core.js b/src/js/core/_core.js
    index dd0a64d..9f6e5fa 100644
    --- a/src/js/core/_core.js
    +++ b/src/js/core/_core.js
    @@ -21,7 +21,7 @@ const font = {
     	size: 0
     }
     
    -const copyColourFallback = (copyInfo, attr) => {
    +const copyFallback = (copyInfo, attr) => {
     	console.log("fallback")
     	var textArea = document.createElement('textarea');
     	textArea.value = copyInfo;
    @@ -157,10 +157,10 @@ module.exports = {
     				navigator.clipboard.writeText(c).then(function() {
     					showMessage(`Copied ${w}.`);
     				}, function(e) {
    -					copyColourFallback(c,w);
    +					copyFallback(c,w);
     				});
     			} else {
    -				copyColourFallback(c, w);
    +				copyFallback(c, w);
     			}
     		},
     
    @@ -178,6 +178,23 @@ module.exports = {
     	},
     	getCSS: (el, prop, b = false) => {
     		return window.getComputedStyle(el, null).getPropertyValue(prop);
    +	},
    +	getTabPath: (t) => {
    +		let url = window.location.toString();
    +		url = url.indexOf("?") > 0 ? url.substring(0,url.indexOf("?")) : url;
    +		url = `${url}?p=${t.closest("article").getAttribute("data-path")}#${t.getAttribute("id")}`
    +		let type = "URL";
    +		if (navigator.clipboard) {
    +			navigator.clipboard.writeText(url).then(function() {
    +				showMessage(`Copied ${type}.`);
    +			}, function(e) {
    +				copyFallback(url,type);
    +			});
    +		} else {
    +			copyFallback(url, type);
    +		}
    +
    +
     	},
     	init: (args = {}) => {
     		const url = getURLVars();
    diff --git a/src/js/scaffolding.js b/src/js/scaffolding.js
    index 36d6421..2d46d0d 100644
    --- a/src/js/scaffolding.js
    +++ b/src/js/scaffolding.js
    @@ -12,10 +12,12 @@ import * as stickynote from "../pg/patterns/core/sticky-note/_sticky-note.js";
     import * as swtch      from "../pg/patterns/core/switch/_switch.js";
     import * as tabs       from "../pg/patterns/core/tabs/_tabs.js";
     
    -// init core
    +// init c
     core.init({
     	success: (a) => { 
    -		tabs.init(a);
    +		tabs.init(a, true, {
    +			shiftModifier: core.getTabPath,
    +		});
     		swtch.init(a);
     		stickynote.init(a);
     	},
    diff --git a/src/pg/patterns/core/tabs/_tabs.js b/src/pg/patterns/core/tabs/_tabs.js
    index 7086231..0e4558b 100644
    --- a/src/pg/patterns/core/tabs/_tabs.js
    +++ b/src/pg/patterns/core/tabs/_tabs.js
    @@ -9,9 +9,31 @@ const click = new MouseEvent('click', {
     	cancelable: true
     });
     
    -export function init(p = document, s = true) { 
    +const waitForElement = (selector) => {
    +    return new Promise(resolve => {
    +        if (document.querySelector(selector)) {
    +            return resolve(document.querySelector(selector));
    +        }
     
    -	p.querySelectorAll(".tab-group, tabset").forEach(tabGroup => {
    +        const observer = new MutationObserver(mutations => {
    +            if (document.querySelector(selector)) {
    +                observer.disconnect();
    +                resolve(document.querySelector(selector));
    +            }
    +        });
    +
    +        // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
    +        observer.observe(document.body, {
    +            childList: true,
    +            subtree: true
    +        });
    +    });
    +}
    +
    +
    +export function init(container = document, spacer = true, args = {}) { 
    +
    +	container.querySelectorAll(".tab-group, tabset").forEach(tabGroup => {
     
     		if (tabGroup.querySelector("[role=tablist]") === null) {
     			const tabgroup = tabGroup.getAttribute("id");
    @@ -37,21 +59,29 @@ export function init(p = document, s = true) {
     
     			const ul = document.createElement('ul');
     			ul.setAttribute("role", "tablist");
    -			ul.innerHTML = s != true ? `${tablist}` : `${tablist}`;
    +			ul.innerHTML = spacer != true ? `${tablist}` : `${tablist}`;
     			tabGroup.insertBefore(ul, tabGroup.firstChild);
     
     			tabGroup.querySelectorAll('[role="tab"]').forEach(tab => {
    -				tab.addEventListener("click", () => {
    -					const siblings = Array.from(tab.parentNode.children);
    -					siblings.forEach(sibling => sibling.classList.remove("selected"));
    -					tab.classList.add("selected");
    +				tab.addEventListener("click", (e) => {
    +					if (e.altKey && typeof args.altModifier == "function") {
    +						args.altModifier(tab);
    +					} else if (e.shiftKey && typeof args.shiftModifier == "function") {
    +						args.shiftModifier(tab);
    +					} else if (e.metaKey && typeof args.metaModifier == "function") {
    +						args.metaModifier(tab);
    +					} else {
    +						const siblings = Array.from(tab.parentNode.children);
    +						siblings.forEach(sibling => sibling.classList.remove("selected"));
    +						tab.classList.add("selected");
     
    -					const tabPanels = Array.from(tab.parentNode.parentNode.children)
    -										   .filter(child => child.getAttribute("role") === "tabpanel");
    -					tabPanels.forEach(panel => panel.classList.remove("open"));
    +						const tabPanels = Array.from(tab.parentNode.parentNode.children)
    +											   .filter(child => child.getAttribute("role") === "tabpanel");
    +						tabPanels.forEach(panel => panel.classList.remove("open"));
     
    -					const tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel");
    -					document.getElementById(tabPanelId).classList.add("open");
    +						const tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel");
    +						document.getElementById(tabPanelId).classList.add("open");
    +					}
     				});
     
     
    @@ -65,7 +95,11 @@ export function init(p = document, s = true) {
     		}
     
     		if (document.location.hash != "" && document.location.hash.substring(0,5) == "#tab-") {
    -			document.querySelector(document.location.hash).dispatchEvent(click);
    +			waitForElement(document.location.hash).then((el) => {
    +				el.scrollIntoView();
    +				el.dispatchEvent(click);
    +			});
    +
     		}
     	});
     }
    diff --git a/src/pg/patterns/core/tabs/index.pug b/src/pg/patterns/core/tabs/index.pug
    index 16df388..22ead93 100644
    --- a/src/pg/patterns/core/tabs/index.pug
    +++ b/src/pg/patterns/core/tabs/index.pug
    @@ -22,6 +22,15 @@ block content
     	+h(2)
     	p The structure of the tab set is defined in html. There are two forms supported. Adding a class of #[code.inline .tab-group] to the container element will work in place of the #[code.inline tabset] tag, and the tab panels can be defined using either #[code.inline tab=""] or #[code.inline data-tab=""]. Passing an optional element to the init function will initialise tabs within that element. 
     
    +	p The tab initalize function now takes a new function parameter in the form of an third argument is an object that can take the following callbacks: 
    +	ul
    +		li altModifer (When the altKey is used)
    +		li shiftModifier (When the shift key is used)
    +		li metaModifier (When the Windows key or Apple key is used)
    +	p You can use these callbacks to create a custom event to get the tab information. 
    +
    +	p Note: There is a new core function (core.getTabPath) that will generate the query string and url hash for use in DS2 Core. You may wish to update your scaffolding.js file to make use of this functionality. 
    +
     
     	tabset#tabs
     		pre.language-html(tab="html")