UNPKG

govuk-frontend

Version:

GOV.UK Frontend contains the code you need to start building a user interface for government platforms and services.

230 lines (220 loc) 7.7 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = global.GOVUKFrontend || {})); })(this, (function (exports) { 'use strict'; function getBreakpoint(name) { const property = `--govuk-frontend-breakpoint-${name}`; const value = window.getComputedStyle(document.documentElement).getPropertyValue(property); return { property, value: value || undefined }; } function isInitialised($root, moduleName) { return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`); } /** * Checks if GOV.UK Frontend is supported on this page * * Some browsers will load and run our JavaScript but GOV.UK Frontend * won't be supported. * * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support * @returns {boolean} Whether GOV.UK Frontend is supported on this page */ function isSupported($scope = document.body) { if (!$scope) { return false; } return $scope.classList.contains('govuk-frontend-supported'); } function formatErrorMessage(Component, message) { return `${Component.moduleName}: ${message}`; } /** * @typedef ComponentWithModuleName * @property {string} moduleName - Name of the component */ class GOVUKFrontendError extends Error { constructor(...args) { super(...args); this.name = 'GOVUKFrontendError'; } } class SupportError extends GOVUKFrontendError { /** * Checks if GOV.UK Frontend is supported on this page * * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support */ constructor($scope = document.body) { const supportMessage = 'noModule' in HTMLScriptElement.prototype ? 'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet' : 'GOV.UK Frontend is not supported in this browser'; super($scope ? supportMessage : 'GOV.UK Frontend initialised without `<script type="module">`'); this.name = 'SupportError'; } } class ElementError extends GOVUKFrontendError { constructor(messageOrOptions) { let message = typeof messageOrOptions === 'string' ? messageOrOptions : ''; if (typeof messageOrOptions === 'object') { const { component, identifier, element, expectedType } = messageOrOptions; message = identifier; message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found'; message = formatErrorMessage(component, message); } super(message); this.name = 'ElementError'; } } class InitError extends GOVUKFrontendError { constructor(componentOrMessage) { const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`); super(message); this.name = 'InitError'; } } /** * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName */ class GOVUKFrontendComponent { /** * Returns the root element of the component * * @protected * @returns {RootElementType} - the root element of component */ get $root() { return this._$root; } constructor($root) { this._$root = void 0; const childConstructor = this.constructor; if (typeof childConstructor.moduleName !== 'string') { throw new InitError(`\`moduleName\` not defined in component`); } if (!($root instanceof childConstructor.elementType)) { throw new ElementError({ element: $root, component: childConstructor, identifier: 'Root element (`$root`)', expectedType: childConstructor.elementType.name }); } else { this._$root = $root; } childConstructor.checkSupport(); this.checkInitialised(); const moduleName = childConstructor.moduleName; this.$root.setAttribute(`data-${moduleName}-init`, ''); } checkInitialised() { const constructor = this.constructor; const moduleName = constructor.moduleName; if (moduleName && isInitialised(this.$root, moduleName)) { throw new InitError(constructor); } } static checkSupport() { if (!isSupported()) { throw new SupportError(); } } } /** * @typedef ChildClass * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component */ /** * @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor */ GOVUKFrontendComponent.elementType = HTMLElement; /** * Header component * * @preserve */ class Header extends GOVUKFrontendComponent { /** * Apply a matchMedia for desktop which will trigger a state sync if the * browser viewport moves between states. * * @param {Element | null} $root - HTML element to use for header */ constructor($root) { super($root); this.$menuButton = void 0; this.$menu = void 0; this.menuIsOpen = false; this.mql = null; const $menuButton = this.$root.querySelector('.govuk-js-header-toggle'); if (!$menuButton) { return this; } const menuId = $menuButton.getAttribute('aria-controls'); if (!menuId) { throw new ElementError({ component: Header, identifier: 'Navigation button (`<button class="govuk-js-header-toggle">`) attribute (`aria-controls`)' }); } const $menu = document.getElementById(menuId); if (!$menu) { throw new ElementError({ component: Header, element: $menu, identifier: `Navigation (\`<ul id="${menuId}">\`)` }); } this.$menu = $menu; this.$menuButton = $menuButton; this.setupResponsiveChecks(); this.$menuButton.addEventListener('click', () => this.handleMenuButtonClick()); } setupResponsiveChecks() { const breakpoint = getBreakpoint('desktop'); if (!breakpoint.value) { throw new ElementError({ component: Header, identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\`` }); } this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`); if ('addEventListener' in this.mql) { this.mql.addEventListener('change', () => this.checkMode()); } else { this.mql.addListener(() => this.checkMode()); } this.checkMode(); } checkMode() { if (!this.mql || !this.$menu || !this.$menuButton) { return; } if (this.mql.matches) { this.$menu.removeAttribute('hidden'); this.$menuButton.setAttribute('hidden', ''); } else { this.$menuButton.removeAttribute('hidden'); this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen.toString()); if (this.menuIsOpen) { this.$menu.removeAttribute('hidden'); } else { this.$menu.setAttribute('hidden', ''); } } } handleMenuButtonClick() { this.menuIsOpen = !this.menuIsOpen; this.checkMode(); } } Header.moduleName = 'govuk-header'; exports.Header = Header; })); //# sourceMappingURL=header.bundle.js.map