UNPKG

@hydrogen-design-system/system

Version:

Hydrogen's full design system and component library.

365 lines (299 loc) 15.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.h2DialogOpenHandler = h2DialogOpenHandler; exports.h2DialogCloseHandler = h2DialogCloseHandler; exports.h2DialogSizeCheckHandler = h2DialogSizeCheckHandler; exports.h2DialogEscapeKeyEvent = h2DialogEscapeKeyEvent; exports.h2DialogResizeOnClickEvent = h2DialogResizeOnClickEvent; exports.h2DialogTabEvent = h2DialogTabEvent; exports.h2DialogTabLoopHandler = h2DialogTabLoopHandler; exports.h2DialogToggleEvent = h2DialogToggleEvent; exports.h2DialogResizeOnViewport = h2DialogResizeOnViewport; exports.h2DialogTrigger = h2DialogTrigger; exports.h2DialogEnableOpenDialogs = h2DialogEnableOpenDialogs; // Hydrogen / Component / Module JS // This file is designed to be the location in which all scripting for the component's functionality exists. This file should export all relevant functionality so that it can be imported by Hydrogen and other systems. // Development Notes: // - please ensure all functions are defined with "" at the end of their name. // - please ensure all references to the parent component's selector also include "" (e.g. [data-h2-accordion]) // This is so that when the component is built, it produces a version-locked set of code that can me manually imported to override newer versions. // - please ensure that when event listeners are added to a trigger, that the script is checking for the system variable (see an example below). // System Check Script function h2DialogSystemCheck(version, target, targetComponentType, targetComponentTrigger, errorMessage) { var triggers = []; if (target == "all") { // Determine where the module is being loaded from. If the module is being loaded from the system, the event should only be applied to the component when it exists within the system's enabler selector (data-h2-system). This check ensures that any code that is loaded by the system is instanced and can be overridden by previous versions if need be. if (version == "latest") { if (targetComponentTrigger != null) { triggers = document.querySelectorAll(targetComponentType + " " + targetComponentTrigger); return triggers; } else { triggers = document.querySelectorAll(targetComponentType); return triggers; } } else if (version == null || version == false || version == "") { console.log("Hydrogen (" + errorMessage + "): no version value has been specified. Please specify \"latest\" or a version number."); return false; } else { var components = []; if (targetComponentTrigger != null) { components = document.querySelectorAll(targetComponentType + " " + targetComponentTrigger); } else { components = document.querySelectorAll(targetComponentType); } components.forEach(function (component) { if (component.closest("[data-h2-system]").getAttribute("data-h2-system") == version) { triggers.push(component); } }); if (triggers.length == 0) { console.log("Hydrogen (" + errorMessage + "): no components were found within the system version you've specified. Please double check the version value being passed matches the version of the system installation on your project."); return false; } else { return triggers; } } } else if (target == null || target == false || target == "") { console.log("Hydrogen (" + errorMessage + "): no target has been specified. Please pass an HTMLelement object, a NodeList of elements, or \"all\"."); return false; } else { // Check to see if the target is an HTMLelement object. if (target.nodeType == Node.ELEMENT_NODE) { if (targetComponentTrigger != null) { triggers.push(target.querySelector(targetComponentTrigger)); return triggers; } else { triggers.push(target); return triggers; } } else { // Check to see if the target is a NodeList. if (NodeList.prototype.isPrototypeOf(target) == true) { target.forEach(function (trigger) { if (targetComponentTrigger != null) { triggers.push(trigger.querySelector(targetComponentTrigger)); } else { triggers.push(trigger); } }); return triggers; } else { // The target isn't a usable value. console.log("Hydrogen (" + errorMessage + "): you've passed an invalid target format. Targets must be a valid HTMLelement object or NodeList of elements."); return false; } } } } // Development Test Scripts // Accordion Toggle // function h2DialogAccordionHelper(e) { // e.preventDefault(); // var trigger = e.target; // var parent = trigger.closest("[data-h2-accordion]"); // if (parent.querySelector("[data-h2-accordion-content]").classList.contains("active")) { // parent.querySelector("[data-h2-accordion-content]").classList.remove("active"); // parent.querySelector("[data-h2-accordion-content]").style.display = "none"; // } // else { // parent.querySelector("[data-h2-accordion-content]").classList.add("active"); // parent.querySelector("[data-h2-accordion-content]").style.display = "block"; // } // } // var accordionTriggers = document.querySelectorAll("[data-h2-accordion-trigger]"); // accordionTriggers.forEach(function(trigger) { // trigger.addEventListener("click", h2DialogAccordionHelper); // }); // Functional Scripts // Open a dialog. function h2DialogOpenHandler(dialog) { // Find the dialog overlay. var overlay = document.querySelector("[data-h2-dialog-overlay]"); // Lock the body to prevent scrolling. document.querySelector("body").style.overflow = "hidden"; // Activate the overlay. overlay.classList.add("h2-active"); // Activate the dialog. h2DialogSizeCheckHandler(dialog); // Assign any accordion triggers a resize click event. var dialogAccordions = dialog.querySelectorAll("[data-h2-accordion-trigger]"); dialogAccordions.forEach(function (accordion) { accordion.removeEventListener("click", h2DialogResizeOnClickEvent); accordion.addEventListener("click", h2DialogResizeOnClickEvent); }); // Set accessibility tags. dialog.setAttribute("aria-hidden", "false"); dialog.setAttribute("tabindex", 0); // Check to see if a manual focus target exists, and if it doesn't target the first focusable item in the dialog instead. var focusTarget = ""; if (dialog.querySelector("[data-h2-focus]") != null) { focusTarget = dialog.querySelector("[data-h2-focus]"); } else { var focusableItems = dialog.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"); focusTarget = focusableItems[0]; } // Ensure that the object manually set to be focusable is in fact focusable. focusTarget.setAttribute("tabindex", "0"); // Set all focusable items in the dialog to be tab enabled. var dialogFocusableItems = dialog.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"); dialogFocusableItems.forEach(function (item) { item.setAttribute("tabindex", 0); }); // Set up and activate the tab loop to ensure you can tab through the dialog and not the page. if (focusTarget != null) { focusTarget.focus(); } h2DialogTabLoopHandler(dialogFocusableItems); // Make the dialog escapable. document.removeEventListener("keyup", h2DialogEscapeKeyEvent); document.addEventListener("keyup", h2DialogEscapeKeyEvent); } // Close a dialog. function h2DialogCloseHandler(dialog) { var overlay = document.querySelector("[data-h2-dialog-overlay]"); // Unlock the body. document.querySelector("body").style.overflow = "visible"; // Disable the overlay. overlay.classList.remove("h2-active"); // Remove Accordion Trigger Event Listener var dialogAccordions = dialog.querySelectorAll("[data-h2-accordion-trigger]"); dialogAccordions.forEach(function (accordion) { accordion.removeEventListener("click", h2DialogResizeOnClickEvent); }); // Close the dialog. dialog.classList.remove("h2-dialog-overflowing"); dialog.classList.remove("h2-dialog-contained"); dialog.classList.remove("h2-active"); dialog.setAttribute("aria-hidden", "true"); dialog.setAttribute("tabindex", -1); // Remove all focusable items from the tab order so that they can't be tabbed to while the dialog is closed. var dialogFocusableItems = dialog.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"); dialogFocusableItems.forEach(function (item) { item.setAttribute("tabindex", -1); }); // Remove the escape listener. document.removeEventListener("keyup", h2DialogEscapeKeyEvent); // Check to see if an ancestor exists and focus it, otherwise focus the first element on the page. var dialogAncestor = document.querySelector("[data-h2-dialog-ancestor]"); if (dialogAncestor == false || dialogAncestor == null) { var pageItems = document.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"); pageItems[0].focus(); } else { dialogAncestor.focus(); } } // Determine the height and position the dialog. // This function compares the dialog's height to the viewport height to determine how the dialog should be positioned so that its content is visible. function h2DialogSizeCheckHandler(dialog) { dialog.classList.add("h2-dialog-height"); // Get viewport and dialog heights. var viewportHeight = window.innerHeight; var dialogHeight = dialog.querySelector("[data-h2-dialog-wrapper]").offsetHeight + 50; // Resize the dialog based on what's taller. if (dialogHeight > viewportHeight) { dialog.classList.remove("h2-dialog-contained"); dialog.classList.add("h2-active"); dialog.classList.add("h2-dialog-overflowing"); } else { dialog.classList.remove("h2-dialog-overflowing"); dialog.classList.add("h2-active"); dialog.classList.add("h2-dialog-contained"); } } // Tab Loop Handler function h2DialogTabLoopHandler(items) { items.forEach(function (item) { item.removeEventListener("keydown", h2DialogTabEvent); item.addEventListener("keydown", h2DialogTabEvent); }); } // Event Scripts // Allow the dialog to be closed with the Escape key. function h2DialogEscapeKeyEvent(e) { var keyCode = e.keyCode || e.which; if (keyCode == 27) { var dialogs = document.querySelectorAll("[data-h2-dialog]"); dialogs.forEach(function (dialog) { h2DialogCloseHandler(dialog); }); } } // Resize dialogs when an accordion is clicked. // This function ensures that when content is expanded inside of the dialog that the dialog checks to see if the content has exceeded the height of the viewport. function h2DialogResizeOnClickEvent(e) { e.preventDefault(); var trigger = e.target; var dialog = trigger.closest("[data-h2-dialog]"); h2DialogSizeCheckHandler(dialog); } // Loop tab key. // These functions ensure that the tab focus is looped so that the user cannot tab outside of the dialog while it's open. function h2DialogTabEvent(e) { var keyCode = e.keyCode || e.which; var dialog = e.currentTarget.closest("[data-h2-dialog]"); var items = dialog.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"); var firstItem = items[0]; var lastItem = items[items.length - 1]; if (keyCode == 9 && !e.shiftKey) { if (lastItem == document.activeElement) { e.preventDefault(); firstItem.focus(); } } else if (keyCode == 9 && e.shiftKey) { if (firstItem == document.activeElement) { e.preventDefault(); lastItem.focus(); } } } // Trigger a dialog when a button is clicked. function h2DialogToggleEvent(e) { // Prevent default actions on the event. e.preventDefault(); // Get the dialog trigger. var trigger = e.currentTarget; // Get the dialog ID and locate the dialog. var dialogValue = trigger.getAttribute("data-h2-dialog-trigger"); var dialog = document.querySelector("[data-h2-dialog*='" + dialogValue + "']"); // Check to see if the dialog is active or not. if (dialog.classList.contains("h2-active")) { if (dialog.hasAttribute("data-h2-no-js") == false) { // If the dialog is open, close it. h2DialogCloseHandler(dialog); } } else { if (dialog.hasAttribute("data-h2-no-js") == false) { // If the dialog is closed, open it. h2DialogOpenHandler(dialog); // Remove the ancestor attribute from all other triggers. var ancestors = document.querySelectorAll("[data-h2-dialog-ancestor]"); ancestors.forEach(function (item) { item.removeAttribute("data-h2-dialog-ancestor"); }); // Set the new trigger as the ancestor so that it can be focused when the dialog is closed. trigger.setAttribute("data-h2-dialog-ancestor", ""); } } } // Page Load Scripts // Resize open dialogs on window resize. // This function ensures that any open dialogs will check if they are too big as a window is resized to ensure that they are appropriately spaced. function h2DialogResizeOnViewport(targetDialog) { var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "latest"; // Check for window resize events. window.onresize = function (e) { // Prevent default actions from occuring. e.preventDefault(); // Set the system check values. var targetComponentType = "[data-h2-dialog]"; var targetComponentTrigger = null; var errorMessage = "Dialog / Check Dialog Size on Viewport Change"; // Get dialogs. var triggers = h2DialogSystemCheck(version, targetDialog, targetComponentType, targetComponentTrigger, errorMessage); // Loop through dialogs and check to see if any are active. if (triggers.length > 0 && triggers != false) { triggers.forEach(function (dialog) { if (dialog.classList.contains("h2-active")) { if (dialog.hasAttribute("data-h2-no-js") == false) { h2DialogSizeCheckHandler(dialog); } } }); } }; } // Enable dialog triggers. function h2DialogTrigger(targetTrigger) { var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "latest"; // Set the system check values. var targetComponentType = "[data-h2-dialog-trigger]"; var targetComponentTrigger = null; var errorMessage = "Dialog / Enable Dialog Buttons"; // Get the buttons. var triggers = h2DialogSystemCheck(version, targetTrigger, targetComponentType, targetComponentTrigger, errorMessage); // Loop through the buttons and add the event listener. if (triggers.length > 0 && triggers != false) { triggers.forEach(function (trigger) { trigger.removeEventListener("click", h2DialogToggleEvent); trigger.addEventListener("click", h2DialogToggleEvent); }); } } // Enable open dialogs. function h2DialogEnableOpenDialogs(targetDialog) { var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "latest"; // Set the system check values. var targetComponentType = "[data-h2-dialog]"; var targetComponentTrigger = null; var errorMessage = "Dialog / Enable Open Dialogs"; // Get the dialogs. var dialogs = h2DialogSystemCheck(version, targetDialog, targetComponentType, targetComponentTrigger, errorMessage); // Loop through available dialogs to see if they are open by default. if (dialogs.length > 0 && dialogs != false) { dialogs.forEach(function (dialog) { if (dialog.classList.contains("h2-active")) { h2DialogOpenHandler(dialog); } }); } } // Exports