From e197f6c4896f8c45a2a45e54de378b312fa8ede9 Mon Sep 17 00:00:00 2001 From: Alex Zappa Date: Mon, 29 Apr 2024 04:41:57 +0200 Subject: [PATCH 1/3] Add new "tabs" module to theme This update adds a new "tabs" module to the theme, enabling the creation and management of tabular content. The tabs.module includes fields.json, meta.json, and module.html for full functionality. This will enable users to add content to a variety of tab structures within landing pages, site pages, blog listings, and blog posts. --- package-lock.json | 26 + package.json | 1 + source/js/main.js | 2 + theme/css/tailwind.css | 56 ++ theme/js/main.js | 1031 +++++++++++++++++++- theme/modules/accordion.module/module.html | 6 - theme/modules/tabs.module/fields.json | 49 + theme/modules/tabs.module/meta.json | 17 + theme/modules/tabs.module/module.css | 0 theme/modules/tabs.module/module.html | 55 ++ theme/modules/tabs.module/module.js | 0 11 files changed, 1232 insertions(+), 11 deletions(-) create mode 100644 theme/modules/tabs.module/fields.json create mode 100644 theme/modules/tabs.module/meta.json create mode 100644 theme/modules/tabs.module/module.css create mode 100644 theme/modules/tabs.module/module.html create mode 100644 theme/modules/tabs.module/module.js diff --git a/package-lock.json b/package-lock.json index ae3f246..a97bf18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "GPL-3.0-or-later", "devDependencies": { "@alpinejs/collapse": "^3.13.10", + "@alpinejs/focus": "^3.13.10", "@alpinejs/intersect": "^3.13.10", "@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/forms": "^0.5.7", @@ -52,6 +53,16 @@ "integrity": "sha512-D4CRrwXdhuJdQX8gJvYBajMj90I85huf395eB8E+PfGy2QQZOxZxd92TWzaMLnMQlFHPkZ+chZmFuewjZ7y52Q==", "dev": true }, + "node_modules/@alpinejs/focus": { + "version": "3.13.10", + "resolved": "https://registry.npmjs.org/@alpinejs/focus/-/focus-3.13.10.tgz", + "integrity": "sha512-4W4AvVtqq3Ozosa+PEopIJqAIoyWOll24t/LClNmQPPl3exsns84sfN+V5SVPLfbBH+uoaCkxEq2rae6U7/MBA==", + "dev": true, + "dependencies": { + "focus-trap": "^6.9.4", + "tabbable": "^5.3.3" + } + }, "node_modules/@alpinejs/intersect": { "version": "3.13.10", "resolved": "https://registry.npmjs.org/@alpinejs/intersect/-/intersect-3.13.10.tgz", @@ -1807,6 +1818,15 @@ "integrity": "sha512-UJaPOARxOcRv9NoiRh6PpNnTetSCldrh8SJ7+0X4PRjeJZbNNo5lzTYEg39fCw2RUrxK6Y0GPuiL0D1N5z+yTw==", "dev": true }, + "node_modules/focus-trap": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.4.tgz", + "integrity": "sha512-v2NTsZe2FF59Y+sDykKY+XjqZ0cPfhq/hikWVL88BqLivnNiEffAsac6rP6H45ff9wG9LL5ToiDqrLEP9GX9mw==", + "dev": true, + "dependencies": { + "tabbable": "^5.3.3" + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4528,6 +4548,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", + "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==", + "dev": true + }, "node_modules/tailwindcss": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", diff --git a/package.json b/package.json index 5e7e343..5aa83d4 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "homepage": "https://github.com/freshjuice-dev/freshjuice-hubspot-theme#readme", "devDependencies": { "@alpinejs/collapse": "^3.13.10", + "@alpinejs/focus": "^3.13.10", "@alpinejs/intersect": "^3.13.10", "@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/forms": "^0.5.7", diff --git a/source/js/main.js b/source/js/main.js index 0a06f2e..6aafdb8 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -6,6 +6,7 @@ import flyingPages from "flying-pages-module"; import Alpine from "alpinejs"; import intersect from "@alpinejs/intersect"; import collapse from "@alpinejs/collapse"; +import focus from "@alpinejs/focus"; import dataDOM from "./modules/Alpine.data/DOM"; // The window.Alpine = Alpine bit is optional, but is nice to have for @@ -28,6 +29,7 @@ function domReady(callback) { // Add plugins to Alpine Alpine.plugin(intersect); Alpine.plugin(collapse); +Alpine.plugin(focus); Alpine.data("xDOM", dataDOM); diff --git a/theme/css/tailwind.css b/theme/css/tailwind.css index a477d83..439d0bb 100644 --- a/theme/css/tailwind.css +++ b/theme/css/tailwind.css @@ -1902,6 +1902,14 @@ lite-youtube.lyt-activated > .lty-playbtn{ margin-top:2rem; } +.fj-mt-5{ + margin-top:1.25rem; +} + +.-fj-mb-px{ + margin-bottom:-1px; +} + .fj-line-clamp-3{ overflow:hidden; display:-webkit-box; @@ -2126,6 +2134,10 @@ lite-youtube.lyt-activated > .lty-playbtn{ align-items:center; } +.fj-items-stretch{ + align-items:stretch; +} + .fj-justify-end{ justify-content:flex-end; } @@ -2261,10 +2273,33 @@ lite-youtube.lyt-activated > .lty-playbtn{ border-top-right-radius:0.25rem; } +.fj-rounded-b-md{ + border-bottom-right-radius:0.375rem; + border-bottom-left-radius:0.375rem; +} + +.fj-rounded-t-md{ + border-top-left-radius:0.375rem; + border-top-right-radius:0.375rem; +} + +.fj-rounded-r-md{ + border-top-right-radius:0.375rem; + border-bottom-right-radius:0.375rem; +} + .fj-rounded-bl-none{ border-bottom-left-radius:0px; } +.fj-rounded-tr-md{ + border-top-right-radius:0.375rem; +} + +.fj-rounded-tl-none{ + border-top-left-radius:0px; +} + .fj-border{ border-width:1px; } @@ -2277,6 +2312,14 @@ lite-youtube.lyt-activated > .lty-playbtn{ border-top-width:1px; } +.fj-border-l{ + border-left-width:1px; +} + +.fj-border-r{ + border-right-width:1px; +} + .fj-border-gray-100{ --fj-border-opacity:1; border-color:rgb(243 244 246 / var(--fj-border-opacity)); @@ -2296,6 +2339,10 @@ lite-youtube.lyt-activated > .lty-playbtn{ border-color:rgb(28 25 23 / 0.1); } +.fj-border-transparent{ + border-color:transparent; +} + .fj-bg-amber-100{ --fj-bg-opacity:1; background-color:rgb(254 243 199 / var(--fj-bg-opacity)); @@ -2420,6 +2467,10 @@ lite-youtube.lyt-activated > .lty-playbtn{ padding:1rem; } +.fj-p-8{ + padding:2rem; +} + .\!fj-py-0{ padding-top:0px !important; padding-bottom:0px !important; @@ -2664,6 +2715,11 @@ lite-youtube.lyt-activated > .lty-playbtn{ color:rgb(28 25 23 / var(--fj-text-opacity)); } +.fj-text-gray-500{ + --fj-text-opacity:1; + color:rgb(107 114 128 / var(--fj-text-opacity)); +} + .\!fj-no-underline{ text-decoration-line:none !important; } diff --git a/theme/js/main.js b/theme/js/main.js index e0ad75c..b992289 100644 --- a/theme/js/main.js +++ b/theme/js/main.js @@ -1219,7 +1219,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); let wantsScale = wantsAll || modifiers.includes("scale"); let opacityValue = wantsOpacity ? 0 : 1; let scaleValue = wantsScale ? modifierValue(modifiers, "scale", 95) / 100 : 1; - let delay = modifierValue(modifiers, "delay", 0) / 1e3; + let delay3 = modifierValue(modifiers, "delay", 0) / 1e3; let origin = modifierValue(modifiers, "origin", "center"); let property = "opacity, transform"; let durationIn = modifierValue(modifiers, "duration", 150) / 1e3; @@ -1228,7 +1228,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); if (transitioningIn) { el._x_transition.enter.during = { transformOrigin: origin, - transitionDelay: `${delay}s`, + transitionDelay: `${delay3}s`, transitionProperty: property, transitionDuration: `${durationIn}s`, transitionTimingFunction: easing @@ -1245,7 +1245,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); if (transitioningOut) { el._x_transition.leave.during = { transformOrigin: origin, - transitionDelay: `${delay}s`, + transitionDelay: `${delay3}s`, transitionProperty: property, transitionDuration: `${durationOut}s`, transitionTimingFunction: easing @@ -1402,7 +1402,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); if (interrupted) return; let duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, "").replace("s", "")) * 1e3; - let delay = Number(getComputedStyle(el).transitionDelay.replace(/,.*/, "").replace("s", "")) * 1e3; + let delay3 = Number(getComputedStyle(el).transitionDelay.replace(/,.*/, "").replace("s", "")) * 1e3; if (duration === 0) duration = Number(getComputedStyle(el).animationDuration.replace("s", "")) * 1e3; mutateDom(() => { @@ -1416,7 +1416,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); stages.end(); }); releaseNextTicks(); - setTimeout(el._x_transitioning.finish, duration + delay); + setTimeout(el._x_transitioning.finish, duration + delay3); reachedEnd = true; }); }); @@ -3641,6 +3641,1008 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } var module_default3 = src_default3; + // node_modules/@alpinejs/focus/dist/module.esm.js + var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]:not(slot)", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])', "details>summary:first-of-type", "details"]; + var candidateSelector = /* @__PURE__ */ candidateSelectors.join(","); + var NoElement = typeof Element === "undefined"; + var matches = NoElement ? function() { + } : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; + var getRootNode = !NoElement && Element.prototype.getRootNode ? function(element) { + return element.getRootNode(); + } : function(element) { + return element.ownerDocument; + }; + var getCandidates = function getCandidates2(el, includeContainer, filter) { + var candidates = Array.prototype.slice.apply(el.querySelectorAll(candidateSelector)); + if (includeContainer && matches.call(el, candidateSelector)) { + candidates.unshift(el); + } + candidates = candidates.filter(filter); + return candidates; + }; + var getCandidatesIteratively = function getCandidatesIteratively2(elements, includeContainer, options) { + var candidates = []; + var elementsToCheck = Array.from(elements); + while (elementsToCheck.length) { + var element = elementsToCheck.shift(); + if (element.tagName === "SLOT") { + var assigned = element.assignedElements(); + var content = assigned.length ? assigned : element.children; + var nestedCandidates = getCandidatesIteratively2(content, true, options); + if (options.flatten) { + candidates.push.apply(candidates, nestedCandidates); + } else { + candidates.push({ + scope: element, + candidates: nestedCandidates + }); + } + } else { + var validCandidate = matches.call(element, candidateSelector); + if (validCandidate && options.filter(element) && (includeContainer || !elements.includes(element))) { + candidates.push(element); + } + var shadowRoot = element.shadowRoot || // check for an undisclosed shadow + typeof options.getShadowRoot === "function" && options.getShadowRoot(element); + var validShadowRoot = !options.shadowRootFilter || options.shadowRootFilter(element); + if (shadowRoot && validShadowRoot) { + var _nestedCandidates = getCandidatesIteratively2(shadowRoot === true ? element.children : shadowRoot.children, true, options); + if (options.flatten) { + candidates.push.apply(candidates, _nestedCandidates); + } else { + candidates.push({ + scope: element, + candidates: _nestedCandidates + }); + } + } else { + elementsToCheck.unshift.apply(elementsToCheck, element.children); + } + } + } + return candidates; + }; + var getTabindex = function getTabindex2(node, isScope) { + if (node.tabIndex < 0) { + if ((isScope || /^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || node.isContentEditable) && isNaN(parseInt(node.getAttribute("tabindex"), 10))) { + return 0; + } + } + return node.tabIndex; + }; + var sortOrderedTabbables = function sortOrderedTabbables2(a, b) { + return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex; + }; + var isInput = function isInput2(node) { + return node.tagName === "INPUT"; + }; + var isHiddenInput = function isHiddenInput2(node) { + return isInput(node) && node.type === "hidden"; + }; + var isDetailsWithSummary = function isDetailsWithSummary2(node) { + var r = node.tagName === "DETAILS" && Array.prototype.slice.apply(node.children).some(function(child) { + return child.tagName === "SUMMARY"; + }); + return r; + }; + var getCheckedRadio = function getCheckedRadio2(nodes, form) { + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].checked && nodes[i].form === form) { + return nodes[i]; + } + } + }; + var isTabbableRadio = function isTabbableRadio2(node) { + if (!node.name) { + return true; + } + var radioScope = node.form || getRootNode(node); + var queryRadios = function queryRadios2(name) { + return radioScope.querySelectorAll('input[type="radio"][name="' + name + '"]'); + }; + var radioSet; + if (typeof window !== "undefined" && typeof window.CSS !== "undefined" && typeof window.CSS.escape === "function") { + radioSet = queryRadios(window.CSS.escape(node.name)); + } else { + try { + radioSet = queryRadios(node.name); + } catch (err) { + console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s", err.message); + return false; + } + } + var checked = getCheckedRadio(radioSet, node.form); + return !checked || checked === node; + }; + var isRadio = function isRadio2(node) { + return isInput(node) && node.type === "radio"; + }; + var isNonTabbableRadio = function isNonTabbableRadio2(node) { + return isRadio(node) && !isTabbableRadio(node); + }; + var isZeroArea = function isZeroArea2(node) { + var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height; + return width === 0 && height === 0; + }; + var isHidden = function isHidden2(node, _ref) { + var displayCheck = _ref.displayCheck, getShadowRoot = _ref.getShadowRoot; + if (getComputedStyle(node).visibility === "hidden") { + return true; + } + var isDirectSummary = matches.call(node, "details>summary:first-of-type"); + var nodeUnderDetails = isDirectSummary ? node.parentElement : node; + if (matches.call(nodeUnderDetails, "details:not([open]) *")) { + return true; + } + var nodeRootHost = getRootNode(node).host; + var nodeIsAttached = (nodeRootHost === null || nodeRootHost === void 0 ? void 0 : nodeRootHost.ownerDocument.contains(nodeRootHost)) || node.ownerDocument.contains(node); + if (!displayCheck || displayCheck === "full") { + if (typeof getShadowRoot === "function") { + var originalNode = node; + while (node) { + var parentElement = node.parentElement; + var rootNode = getRootNode(node); + if (parentElement && !parentElement.shadowRoot && getShadowRoot(parentElement) === true) { + return isZeroArea(node); + } else if (node.assignedSlot) { + node = node.assignedSlot; + } else if (!parentElement && rootNode !== node.ownerDocument) { + node = rootNode.host; + } else { + node = parentElement; + } + } + node = originalNode; + } + if (nodeIsAttached) { + return !node.getClientRects().length; + } + } else if (displayCheck === "non-zero-area") { + return isZeroArea(node); + } + return false; + }; + var isDisabledFromFieldset = function isDisabledFromFieldset2(node) { + if (/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(node.tagName)) { + var parentNode = node.parentElement; + while (parentNode) { + if (parentNode.tagName === "FIELDSET" && parentNode.disabled) { + for (var i = 0; i < parentNode.children.length; i++) { + var child = parentNode.children.item(i); + if (child.tagName === "LEGEND") { + return matches.call(parentNode, "fieldset[disabled] *") ? true : !child.contains(node); + } + } + return true; + } + parentNode = parentNode.parentElement; + } + } + return false; + }; + var isNodeMatchingSelectorFocusable = function isNodeMatchingSelectorFocusable2(options, node) { + if (node.disabled || isHiddenInput(node) || isHidden(node, options) || // For a details element with a summary, the summary element gets the focus + isDetailsWithSummary(node) || isDisabledFromFieldset(node)) { + return false; + } + return true; + }; + var isNodeMatchingSelectorTabbable = function isNodeMatchingSelectorTabbable2(options, node) { + if (isNonTabbableRadio(node) || getTabindex(node) < 0 || !isNodeMatchingSelectorFocusable(options, node)) { + return false; + } + return true; + }; + var isValidShadowRootTabbable = function isValidShadowRootTabbable2(shadowHostNode) { + var tabIndex = parseInt(shadowHostNode.getAttribute("tabindex"), 10); + if (isNaN(tabIndex) || tabIndex >= 0) { + return true; + } + return false; + }; + var sortByOrder = function sortByOrder2(candidates) { + var regularTabbables = []; + var orderedTabbables = []; + candidates.forEach(function(item, i) { + var isScope = !!item.scope; + var element = isScope ? item.scope : item; + var candidateTabindex = getTabindex(element, isScope); + var elements = isScope ? sortByOrder2(item.candidates) : element; + if (candidateTabindex === 0) { + isScope ? regularTabbables.push.apply(regularTabbables, elements) : regularTabbables.push(element); + } else { + orderedTabbables.push({ + documentOrder: i, + tabIndex: candidateTabindex, + item, + isScope, + content: elements + }); + } + }); + return orderedTabbables.sort(sortOrderedTabbables).reduce(function(acc, sortable) { + sortable.isScope ? acc.push.apply(acc, sortable.content) : acc.push(sortable.content); + return acc; + }, []).concat(regularTabbables); + }; + var tabbable = function tabbable2(el, options) { + options = options || {}; + var candidates; + if (options.getShadowRoot) { + candidates = getCandidatesIteratively([el], options.includeContainer, { + filter: isNodeMatchingSelectorTabbable.bind(null, options), + flatten: false, + getShadowRoot: options.getShadowRoot, + shadowRootFilter: isValidShadowRootTabbable + }); + } else { + candidates = getCandidates(el, options.includeContainer, isNodeMatchingSelectorTabbable.bind(null, options)); + } + return sortByOrder(candidates); + }; + var focusable = function focusable2(el, options) { + options = options || {}; + var candidates; + if (options.getShadowRoot) { + candidates = getCandidatesIteratively([el], options.includeContainer, { + filter: isNodeMatchingSelectorFocusable.bind(null, options), + flatten: true, + getShadowRoot: options.getShadowRoot + }); + } else { + candidates = getCandidates(el, options.includeContainer, isNodeMatchingSelectorFocusable.bind(null, options)); + } + return candidates; + }; + var isTabbable = function isTabbable2(node, options) { + options = options || {}; + if (!node) { + throw new Error("No node provided"); + } + if (matches.call(node, candidateSelector) === false) { + return false; + } + return isNodeMatchingSelectorTabbable(options, node); + }; + var focusableCandidateSelector = /* @__PURE__ */ candidateSelectors.concat("iframe").join(","); + var isFocusable = function isFocusable2(node, options) { + options = options || {}; + if (!node) { + throw new Error("No node provided"); + } + if (matches.call(node, focusableCandidateSelector) === false) { + return false; + } + return isNodeMatchingSelectorFocusable(options, node); + }; + function ownKeys2(object, enumerableOnly) { + var keys = Object.keys(object); + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + enumerableOnly && (symbols = symbols.filter(function(sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + })), keys.push.apply(keys, symbols); + } + return keys; + } + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = null != arguments[i] ? arguments[i] : {}; + i % 2 ? ownKeys2(Object(source), true).forEach(function(key) { + _defineProperty(target, key, source[key]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys2(Object(source)).forEach(function(key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + return target; + } + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; + } + var activeFocusTraps = /* @__PURE__ */ function() { + var trapQueue = []; + return { + activateTrap: function activateTrap(trap) { + if (trapQueue.length > 0) { + var activeTrap = trapQueue[trapQueue.length - 1]; + if (activeTrap !== trap) { + activeTrap.pause(); + } + } + var trapIndex = trapQueue.indexOf(trap); + if (trapIndex === -1) { + trapQueue.push(trap); + } else { + trapQueue.splice(trapIndex, 1); + trapQueue.push(trap); + } + }, + deactivateTrap: function deactivateTrap(trap) { + var trapIndex = trapQueue.indexOf(trap); + if (trapIndex !== -1) { + trapQueue.splice(trapIndex, 1); + } + if (trapQueue.length > 0) { + trapQueue[trapQueue.length - 1].unpause(); + } + } + }; + }(); + var isSelectableInput = function isSelectableInput2(node) { + return node.tagName && node.tagName.toLowerCase() === "input" && typeof node.select === "function"; + }; + var isEscapeEvent = function isEscapeEvent2(e) { + return e.key === "Escape" || e.key === "Esc" || e.keyCode === 27; + }; + var isTabEvent = function isTabEvent2(e) { + return e.key === "Tab" || e.keyCode === 9; + }; + var delay = function delay2(fn) { + return setTimeout(fn, 0); + }; + var findIndex = function findIndex2(arr, fn) { + var idx = -1; + arr.every(function(value, i) { + if (fn(value)) { + idx = i; + return false; + } + return true; + }); + return idx; + }; + var valueOrHandler = function valueOrHandler2(value) { + for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + params[_key - 1] = arguments[_key]; + } + return typeof value === "function" ? value.apply(void 0, params) : value; + }; + var getActualTarget = function getActualTarget2(event) { + return event.target.shadowRoot && typeof event.composedPath === "function" ? event.composedPath()[0] : event.target; + }; + var createFocusTrap = function createFocusTrap2(elements, userOptions) { + var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document; + var config = _objectSpread2({ + returnFocusOnDeactivate: true, + escapeDeactivates: true, + delayInitialFocus: true + }, userOptions); + var state = { + // containers given to createFocusTrap() + // @type {Array} + containers: [], + // list of objects identifying tabbable nodes in `containers` in the trap + // NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap + // is active, but the trap should never get to a state where there isn't at least one group + // with at least one tabbable node in it (that would lead to an error condition that would + // result in an error being thrown) + // @type {Array<{ + // container: HTMLElement, + // tabbableNodes: Array, // empty if none + // focusableNodes: Array, // empty if none + // firstTabbableNode: HTMLElement|null, + // lastTabbableNode: HTMLElement|null, + // nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined + // }>} + containerGroups: [], + // same order/length as `containers` list + // references to objects in `containerGroups`, but only those that actually have + // tabbable nodes in them + // NOTE: same order as `containers` and `containerGroups`, but __not necessarily__ + // the same length + tabbableGroups: [], + nodeFocusedBeforeActivation: null, + mostRecentlyFocusedNode: null, + active: false, + paused: false, + // timer ID for when delayInitialFocus is true and initial focus in this trap + // has been delayed during activation + delayInitialFocusTimer: void 0 + }; + var trap; + var getOption = function getOption2(configOverrideOptions, optionName, configOptionName) { + return configOverrideOptions && configOverrideOptions[optionName] !== void 0 ? configOverrideOptions[optionName] : config[configOptionName || optionName]; + }; + var findContainerIndex = function findContainerIndex2(element) { + return state.containerGroups.findIndex(function(_ref) { + var container = _ref.container, tabbableNodes = _ref.tabbableNodes; + return container.contains(element) || // fall back to explicit tabbable search which will take into consideration any + // web components if the `tabbableOptions.getShadowRoot` option was used for + // the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't + // look inside web components even if open) + tabbableNodes.find(function(node) { + return node === element; + }); + }); + }; + var getNodeForOption = function getNodeForOption2(optionName) { + var optionValue = config[optionName]; + if (typeof optionValue === "function") { + for (var _len2 = arguments.length, params = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + params[_key2 - 1] = arguments[_key2]; + } + optionValue = optionValue.apply(void 0, params); + } + if (optionValue === true) { + optionValue = void 0; + } + if (!optionValue) { + if (optionValue === void 0 || optionValue === false) { + return optionValue; + } + throw new Error("`".concat(optionName, "` was specified but was not a node, or did not return a node")); + } + var node = optionValue; + if (typeof optionValue === "string") { + node = doc.querySelector(optionValue); + if (!node) { + throw new Error("`".concat(optionName, "` as selector refers to no known node")); + } + } + return node; + }; + var getInitialFocusNode = function getInitialFocusNode2() { + var node = getNodeForOption("initialFocus"); + if (node === false) { + return false; + } + if (node === void 0) { + if (findContainerIndex(doc.activeElement) >= 0) { + node = doc.activeElement; + } else { + var firstTabbableGroup = state.tabbableGroups[0]; + var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode; + node = firstTabbableNode || getNodeForOption("fallbackFocus"); + } + } + if (!node) { + throw new Error("Your focus-trap needs to have at least one focusable element"); + } + return node; + }; + var updateTabbableNodes = function updateTabbableNodes2() { + state.containerGroups = state.containers.map(function(container) { + var tabbableNodes = tabbable(container, config.tabbableOptions); + var focusableNodes = focusable(container, config.tabbableOptions); + return { + container, + tabbableNodes, + focusableNodes, + firstTabbableNode: tabbableNodes.length > 0 ? tabbableNodes[0] : null, + lastTabbableNode: tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : null, + /** + * Finds the __tabbable__ node that follows the given node in the specified direction, + * in this container, if any. + * @param {HTMLElement} node + * @param {boolean} [forward] True if going in forward tab order; false if going + * in reverse. + * @returns {HTMLElement|undefined} The next tabbable node, if any. + */ + nextTabbableNode: function nextTabbableNode(node) { + var forward = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true; + var nodeIdx = focusableNodes.findIndex(function(n) { + return n === node; + }); + if (nodeIdx < 0) { + return void 0; + } + if (forward) { + return focusableNodes.slice(nodeIdx + 1).find(function(n) { + return isTabbable(n, config.tabbableOptions); + }); + } + return focusableNodes.slice(0, nodeIdx).reverse().find(function(n) { + return isTabbable(n, config.tabbableOptions); + }); + } + }; + }); + state.tabbableGroups = state.containerGroups.filter(function(group) { + return group.tabbableNodes.length > 0; + }); + if (state.tabbableGroups.length <= 0 && !getNodeForOption("fallbackFocus")) { + throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times"); + } + }; + var tryFocus = function tryFocus2(node) { + if (node === false) { + return; + } + if (node === doc.activeElement) { + return; + } + if (!node || !node.focus) { + tryFocus2(getInitialFocusNode()); + return; + } + node.focus({ + preventScroll: !!config.preventScroll + }); + state.mostRecentlyFocusedNode = node; + if (isSelectableInput(node)) { + node.select(); + } + }; + var getReturnFocusNode = function getReturnFocusNode2(previousActiveElement) { + var node = getNodeForOption("setReturnFocus", previousActiveElement); + return node ? node : node === false ? false : previousActiveElement; + }; + var checkPointerDown = function checkPointerDown2(e) { + var target = getActualTarget(e); + if (findContainerIndex(target) >= 0) { + return; + } + if (valueOrHandler(config.clickOutsideDeactivates, e)) { + trap.deactivate({ + // if, on deactivation, we should return focus to the node originally-focused + // when the trap was activated (or the configured `setReturnFocus` node), + // then assume it's also OK to return focus to the outside node that was + // just clicked, causing deactivation, as long as that node is focusable; + // if it isn't focusable, then return focus to the original node focused + // on activation (or the configured `setReturnFocus` node) + // NOTE: by setting `returnFocus: false`, deactivate() will do nothing, + // which will result in the outside click setting focus to the node + // that was clicked, whether it's focusable or not; by setting + // `returnFocus: true`, we'll attempt to re-focus the node originally-focused + // on activation (or the configured `setReturnFocus` node) + returnFocus: config.returnFocusOnDeactivate && !isFocusable(target, config.tabbableOptions) + }); + return; + } + if (valueOrHandler(config.allowOutsideClick, e)) { + return; + } + e.preventDefault(); + }; + var checkFocusIn = function checkFocusIn2(e) { + var target = getActualTarget(e); + var targetContained = findContainerIndex(target) >= 0; + if (targetContained || target instanceof Document) { + if (targetContained) { + state.mostRecentlyFocusedNode = target; + } + } else { + e.stopImmediatePropagation(); + tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode()); + } + }; + var checkTab = function checkTab2(e) { + var target = getActualTarget(e); + updateTabbableNodes(); + var destinationNode = null; + if (state.tabbableGroups.length > 0) { + var containerIndex = findContainerIndex(target); + var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : void 0; + if (containerIndex < 0) { + if (e.shiftKey) { + destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode; + } else { + destinationNode = state.tabbableGroups[0].firstTabbableNode; + } + } else if (e.shiftKey) { + var startOfGroupIndex = findIndex(state.tabbableGroups, function(_ref2) { + var firstTabbableNode = _ref2.firstTabbableNode; + return target === firstTabbableNode; + }); + if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) { + startOfGroupIndex = containerIndex; + } + if (startOfGroupIndex >= 0) { + var destinationGroupIndex = startOfGroupIndex === 0 ? state.tabbableGroups.length - 1 : startOfGroupIndex - 1; + var destinationGroup = state.tabbableGroups[destinationGroupIndex]; + destinationNode = destinationGroup.lastTabbableNode; + } + } else { + var lastOfGroupIndex = findIndex(state.tabbableGroups, function(_ref3) { + var lastTabbableNode = _ref3.lastTabbableNode; + return target === lastTabbableNode; + }); + if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) { + lastOfGroupIndex = containerIndex; + } + if (lastOfGroupIndex >= 0) { + var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1; + var _destinationGroup = state.tabbableGroups[_destinationGroupIndex]; + destinationNode = _destinationGroup.firstTabbableNode; + } + } + } else { + destinationNode = getNodeForOption("fallbackFocus"); + } + if (destinationNode) { + e.preventDefault(); + tryFocus(destinationNode); + } + }; + var checkKey = function checkKey2(e) { + if (isEscapeEvent(e) && valueOrHandler(config.escapeDeactivates, e) !== false) { + e.preventDefault(); + trap.deactivate(); + return; + } + if (isTabEvent(e)) { + checkTab(e); + return; + } + }; + var checkClick = function checkClick2(e) { + var target = getActualTarget(e); + if (findContainerIndex(target) >= 0) { + return; + } + if (valueOrHandler(config.clickOutsideDeactivates, e)) { + return; + } + if (valueOrHandler(config.allowOutsideClick, e)) { + return; + } + e.preventDefault(); + e.stopImmediatePropagation(); + }; + var addListeners = function addListeners2() { + if (!state.active) { + return; + } + activeFocusTraps.activateTrap(trap); + state.delayInitialFocusTimer = config.delayInitialFocus ? delay(function() { + tryFocus(getInitialFocusNode()); + }) : tryFocus(getInitialFocusNode()); + doc.addEventListener("focusin", checkFocusIn, true); + doc.addEventListener("mousedown", checkPointerDown, { + capture: true, + passive: false + }); + doc.addEventListener("touchstart", checkPointerDown, { + capture: true, + passive: false + }); + doc.addEventListener("click", checkClick, { + capture: true, + passive: false + }); + doc.addEventListener("keydown", checkKey, { + capture: true, + passive: false + }); + return trap; + }; + var removeListeners = function removeListeners2() { + if (!state.active) { + return; + } + doc.removeEventListener("focusin", checkFocusIn, true); + doc.removeEventListener("mousedown", checkPointerDown, true); + doc.removeEventListener("touchstart", checkPointerDown, true); + doc.removeEventListener("click", checkClick, true); + doc.removeEventListener("keydown", checkKey, true); + return trap; + }; + trap = { + get active() { + return state.active; + }, + get paused() { + return state.paused; + }, + activate: function activate(activateOptions) { + if (state.active) { + return this; + } + var onActivate = getOption(activateOptions, "onActivate"); + var onPostActivate = getOption(activateOptions, "onPostActivate"); + var checkCanFocusTrap = getOption(activateOptions, "checkCanFocusTrap"); + if (!checkCanFocusTrap) { + updateTabbableNodes(); + } + state.active = true; + state.paused = false; + state.nodeFocusedBeforeActivation = doc.activeElement; + if (onActivate) { + onActivate(); + } + var finishActivation = function finishActivation2() { + if (checkCanFocusTrap) { + updateTabbableNodes(); + } + addListeners(); + if (onPostActivate) { + onPostActivate(); + } + }; + if (checkCanFocusTrap) { + checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation); + return this; + } + finishActivation(); + return this; + }, + deactivate: function deactivate(deactivateOptions) { + if (!state.active) { + return this; + } + var options = _objectSpread2({ + onDeactivate: config.onDeactivate, + onPostDeactivate: config.onPostDeactivate, + checkCanReturnFocus: config.checkCanReturnFocus + }, deactivateOptions); + clearTimeout(state.delayInitialFocusTimer); + state.delayInitialFocusTimer = void 0; + removeListeners(); + state.active = false; + state.paused = false; + activeFocusTraps.deactivateTrap(trap); + var onDeactivate = getOption(options, "onDeactivate"); + var onPostDeactivate = getOption(options, "onPostDeactivate"); + var checkCanReturnFocus = getOption(options, "checkCanReturnFocus"); + var returnFocus = getOption(options, "returnFocus", "returnFocusOnDeactivate"); + if (onDeactivate) { + onDeactivate(); + } + var finishDeactivation = function finishDeactivation2() { + delay(function() { + if (returnFocus) { + tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)); + } + if (onPostDeactivate) { + onPostDeactivate(); + } + }); + }; + if (returnFocus && checkCanReturnFocus) { + checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation); + return this; + } + finishDeactivation(); + return this; + }, + pause: function pause() { + if (state.paused || !state.active) { + return this; + } + state.paused = true; + removeListeners(); + return this; + }, + unpause: function unpause() { + if (!state.paused || !state.active) { + return this; + } + state.paused = false; + updateTabbableNodes(); + addListeners(); + return this; + }, + updateContainerElements: function updateContainerElements(containerElements) { + var elementsAsArray = [].concat(containerElements).filter(Boolean); + state.containers = elementsAsArray.map(function(element) { + return typeof element === "string" ? doc.querySelector(element) : element; + }); + if (state.active) { + updateTabbableNodes(); + } + return this; + } + }; + trap.updateContainerElements(elements); + return trap; + }; + function src_default4(Alpine2) { + let lastFocused; + let currentFocused; + window.addEventListener("focusin", () => { + lastFocused = currentFocused; + currentFocused = document.activeElement; + }); + Alpine2.magic("focus", (el) => { + let within = el; + return { + __noscroll: false, + __wrapAround: false, + within(el2) { + within = el2; + return this; + }, + withoutScrolling() { + this.__noscroll = true; + return this; + }, + noscroll() { + this.__noscroll = true; + return this; + }, + withWrapAround() { + this.__wrapAround = true; + return this; + }, + wrap() { + return this.withWrapAround(); + }, + focusable(el2) { + return isFocusable(el2); + }, + previouslyFocused() { + return lastFocused; + }, + lastFocused() { + return lastFocused; + }, + focused() { + return currentFocused; + }, + focusables() { + if (Array.isArray(within)) + return within; + return focusable(within, { displayCheck: "none" }); + }, + all() { + return this.focusables(); + }, + isFirst(el2) { + let els = this.all(); + return els[0] && els[0].isSameNode(el2); + }, + isLast(el2) { + let els = this.all(); + return els.length && els.slice(-1)[0].isSameNode(el2); + }, + getFirst() { + return this.all()[0]; + }, + getLast() { + return this.all().slice(-1)[0]; + }, + getNext() { + let list = this.all(); + let current = document.activeElement; + if (list.indexOf(current) === -1) + return; + if (this.__wrapAround && list.indexOf(current) === list.length - 1) { + return list[0]; + } + return list[list.indexOf(current) + 1]; + }, + getPrevious() { + let list = this.all(); + let current = document.activeElement; + if (list.indexOf(current) === -1) + return; + if (this.__wrapAround && list.indexOf(current) === 0) { + return list.slice(-1)[0]; + } + return list[list.indexOf(current) - 1]; + }, + first() { + this.focus(this.getFirst()); + }, + last() { + this.focus(this.getLast()); + }, + next() { + this.focus(this.getNext()); + }, + previous() { + this.focus(this.getPrevious()); + }, + prev() { + return this.previous(); + }, + focus(el2) { + if (!el2) + return; + setTimeout(() => { + if (!el2.hasAttribute("tabindex")) + el2.setAttribute("tabindex", "0"); + el2.focus({ preventScroll: this.__noscroll }); + }); + } + }; + }); + Alpine2.directive("trap", Alpine2.skipDuringClone( + (el, { expression, modifiers }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => { + let evaluator = evaluateLater2(expression); + let oldValue = false; + let options = { + escapeDeactivates: false, + allowOutsideClick: true, + fallbackFocus: () => el + }; + if (modifiers.includes("noautofocus")) { + options.initialFocus = false; + } else { + let autofocusEl = el.querySelector("[autofocus]"); + if (autofocusEl) + options.initialFocus = autofocusEl; + } + let trap = createFocusTrap(el, options); + let undoInert = () => { + }; + let undoDisableScrolling = () => { + }; + const releaseFocus = () => { + undoInert(); + undoInert = () => { + }; + undoDisableScrolling(); + undoDisableScrolling = () => { + }; + trap.deactivate({ + returnFocus: !modifiers.includes("noreturn") + }); + }; + effect3(() => evaluator((value) => { + if (oldValue === value) + return; + if (value && !oldValue) { + if (modifiers.includes("noscroll")) + undoDisableScrolling = disableScrolling(); + if (modifiers.includes("inert")) + undoInert = setInert(el); + setTimeout(() => { + trap.activate(); + }, 15); + } + if (!value && oldValue) { + releaseFocus(); + } + oldValue = !!value; + })); + cleanup2(releaseFocus); + }, + // When cloning, we only want to add aria-hidden attributes to the + // DOM and not try to actually trap, as trapping can mess with the + // live DOM and isn't just isolated to the cloned DOM. + (el, { expression, modifiers }, { evaluate: evaluate2 }) => { + if (modifiers.includes("inert") && evaluate2(expression)) + setInert(el); + } + )); + } + function setInert(el) { + let undos = []; + crawlSiblingsUp(el, (sibling) => { + let cache = sibling.hasAttribute("aria-hidden"); + sibling.setAttribute("aria-hidden", "true"); + undos.push(() => cache || sibling.removeAttribute("aria-hidden")); + }); + return () => { + while (undos.length) + undos.pop()(); + }; + } + function crawlSiblingsUp(el, callback) { + if (el.isSameNode(document.body) || !el.parentNode) + return; + Array.from(el.parentNode.children).forEach((sibling) => { + if (sibling.isSameNode(el)) { + crawlSiblingsUp(el.parentNode, callback); + } else { + callback(sibling); + } + }); + } + function disableScrolling() { + let overflow = document.documentElement.style.overflow; + let paddingRight = document.documentElement.style.paddingRight; + let scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; + document.documentElement.style.overflow = "hidden"; + document.documentElement.style.paddingRight = `${scrollbarWidth}px`; + return () => { + document.documentElement.style.overflow = overflow; + document.documentElement.style.paddingRight = paddingRight; + }; + } + var module_default4 = src_default4; + // source/js/modules/Alpine.data/DOM.js var import_debugLog = __toESM(require_debugLog()); var import_loadScript = __toESM(require_loadScript()); @@ -3672,6 +4674,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } module_default.plugin(module_default2); module_default.plugin(module_default3); + module_default.plugin(module_default4); module_default.data("xDOM", DOM_default); if (navigator.userAgent.indexOf("Safari") != -1 && navigator.userAgent.indexOf("Chrome") == -1) { (0, import_loadScript2.default)("//cdn.jsdelivr.net/npm/balance-text@3.3.1/balancetext.min.js", "async", () => { @@ -3689,3 +4692,21 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); }); })(); +/*! Bundled license information: + +@alpinejs/focus/dist/module.esm.js: + (*! Bundled license information: + + tabbable/dist/index.esm.js: + (*! + * tabbable 5.3.3 + * @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE + *) + + focus-trap/dist/focus-trap.esm.js: + (*! + * focus-trap 6.9.4 + * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE + *) + *) +*/ diff --git a/theme/modules/accordion.module/module.html b/theme/modules/accordion.module/module.html index 48e7bdc..f57f914 100644 --- a/theme/modules/accordion.module/module.html +++ b/theme/modules/accordion.module/module.html @@ -1,9 +1,3 @@ -{#{% for item in module.items %}#} -{# {% inline_text field="question" value="{{ item.question }}" %}#} -{# {% inline_rich_text field="answer" value="{{ item.answer }}" %}#} -{#{% endfor %}#} - - {% if module.enable_faq_schema %}