From 68df8860ba53567566b6212e6807e80abcb81ded Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sun, 5 Jun 2022 12:22:52 +0930 Subject: [PATCH] chore: add prettier --- .editorconfig | 15 ++ .prettierignore | 4 + README.md | 20 +- demo/demo.ts | 12 +- demo/index.html | 574 +++++++++++++++++++++-------------------- demo/rollup.config.js | 22 +- package-lock.json | 22 ++ package.json | 102 ++++---- rollup.config.js | 31 +-- src/alerts/README.md | 56 ++-- src/alerts/alerts.ts | 44 ++-- src/menu/README.md | 30 +-- src/menu/menu.ts | 26 +- src/modal/README.md | 130 +++++----- src/modal/modal.ts | 34 ++- src/popup/README.md | 54 ++-- src/popup/popup.ts | 119 +++++---- src/toolbar/README.md | 12 +- src/toolbar/toolbar.ts | 14 +- src/tooltip/README.md | 52 ++-- src/tooltip/tooltip.ts | 35 ++- tsconfig.json | 32 +-- 22 files changed, 781 insertions(+), 659 deletions(-) create mode 100644 .editorconfig create mode 100644 .prettierignore diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b59b24d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{html,less,css,yml}] +indent_size = 2 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..89c86f6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist +package-lock.json +CHANGELOG.md +demo/demo.js diff --git a/README.md b/README.md index adb0134..794a1d0 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ Basic interactive UI patterns like popup menus, tooltips, and modals are surpris `inclusive-elements` provides those basic behaviors in the form of Custom Elements, without any of the cruft. They are: -- **๐Ÿฆฎ Accessible.** Designed following the [WAI-ARIA Authoring Practices](https://w3c.github.io/aria-practices) so everyone can use them. -- **๐ŸŒณ Lightweight.** 3kB gzipped with minimal dependencies. Less if you tree-shake only the elements you need. -- **๐ŸŽจ Unstyled.** Easily integrate with your design bottom-up without having to fight defaults. -- **๐Ÿš€ Easy to use.** Simple API, works with any framework that supports Custom Elements. +- **๐Ÿฆฎ Accessible.** Designed following the [WAI-ARIA Authoring Practices](https://w3c.github.io/aria-practices) so everyone can use them. +- **๐ŸŒณ Lightweight.** 3kB gzipped with minimal dependencies. Less if you tree-shake only the elements you need. +- **๐ŸŽจ Unstyled.** Easily integrate with your design bottom-up without having to fight defaults. +- **๐Ÿš€ Easy to use.** Simple API, works with any framework that supports Custom Elements. ## Installation @@ -23,12 +23,12 @@ npm install inclusive-elements --save Detailed descriptions and usage instructions are contained within each element: -- [Alerts](src/alerts) -- [Menu](src/menu) -- [Modal](src/modal) -- [Popup](src/popup) -- [Toolbar](src/toolbar) -- [Tooltip](src/tooltip) +- [Alerts](src/alerts) +- [Menu](src/menu) +- [Modal](src/modal) +- [Popup](src/popup) +- [Toolbar](src/toolbar) +- [Tooltip](src/tooltip) ## Contributing diff --git a/demo/demo.ts b/demo/demo.ts index 867f240..fefd0e6 100644 --- a/demo/demo.ts +++ b/demo/demo.ts @@ -1,10 +1,10 @@ import { - PopupElement, - TooltipElement, - MenuElement, - ModalElement, - AlertsElement, - ToolbarElement + PopupElement, + TooltipElement, + MenuElement, + ModalElement, + AlertsElement, + ToolbarElement, } from '../dist/index'; window.customElements.define('ui-popup', PopupElement); diff --git a/demo/index.html b/demo/index.html index cb1d700..bc17c30 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,299 +1,315 @@ - + - - - - inclusive-elements demo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @media (prefers-reduced-motion: no-preference) { + .alert.enter-active, + .alert.leave-active { + transition: opacity 0.5s, transform 0.5s; + } + + .alert.enter-from, + .alert.leave-to { + opacity: 0; + transform: translateY(-100%); + } + + .alert.move { + transition: transform 0.5s; + } + } + + ui-toolbar { + display: block; + border: 2px solid #ccc; + padding: 10px; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/rollup.config.js b/demo/rollup.config.js index fa6443e..78a2b59 100644 --- a/demo/rollup.config.js +++ b/demo/rollup.config.js @@ -3,15 +3,15 @@ import { terser } from 'rollup-plugin-terser'; import { nodeResolve } from '@rollup/plugin-node-resolve'; export default { - input: __dirname + '/demo.ts', - output: { - file: __dirname + '/demo.js', - }, - plugins: [ - nodeResolve({ - exportConditions: ['production'], - }), - typescript(), - terser(), - ] + input: __dirname + '/demo.ts', + output: { + file: __dirname + '/demo.js', + }, + plugins: [ + nodeResolve({ + exportConditions: ['production'], + }), + typescript(), + terser(), + ], }; diff --git a/package-lock.json b/package-lock.json index 5c78df5..85dec11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@release-it/keep-a-changelog": "^2.3.0", "@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-typescript": "^8.3.0", + "prettier": "^2.6.2", "release-it": "^14.12.4", "rollup": "^2.66.1", "rollup-plugin-filesize": "^9.1.2", @@ -3792,6 +3793,21 @@ "node": ">=4" } }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -8104,6 +8120,12 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", diff --git a/package.json b/package.json index 40c34df..d0a3f32 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,58 @@ { - "name": "inclusive-elements", - "version": "0.1.2", - "description": "Accessible, lightweight, unstyled implementations of common UI controls.", - "main": "dist/index.js", - "module": "dist/index.js", - "type": "module", - "types": "dist/index.d.ts", - "license": "MIT", - "repository": "tobyzerner/inclusive-elements", - "files": [ - "dist", - "README.md" - ], - "sideEffects": false, - "scripts": { - "build": "tsc && rollup -c && npm run build:demo", - "build:watch": "rollup -cw", - "build:demo": "rollup -c demo/rollup.config.js", - "release": "release-it --npm.tag=latest" - }, - "dependencies": { - "@floating-ui/dom": "^0.5.2", - "focus-trap": "^6.7.2", - "hello-goodbye": "^0.1.0-beta.5", - "tabbable": "^5.2.1" - }, - "devDependencies": { - "@release-it/keep-a-changelog": "^2.3.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-typescript": "^8.3.0", - "release-it": "^14.12.4", - "rollup": "^2.66.1", - "rollup-plugin-filesize": "^9.1.2", - "rollup-plugin-terser": "^7.0.2", - "tslib": "^2.3.1", - "typescript": "^4.5.5" - }, - "release-it": { - "github": { - "release": true + "name": "inclusive-elements", + "version": "0.1.2", + "description": "Accessible, lightweight, unstyled implementations of common UI controls.", + "main": "dist/index.js", + "module": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "license": "MIT", + "repository": "tobyzerner/inclusive-elements", + "files": [ + "dist", + "README.md" + ], + "sideEffects": false, + "scripts": { + "build": "tsc && rollup -c && npm run build:demo", + "build:watch": "rollup -cw", + "build:demo": "rollup -c demo/rollup.config.js", + "release": "release-it --npm.tag=latest" }, - "plugins": { - "@release-it/keep-a-changelog": { - "filename": "CHANGELOG.md", - "addUnreleased": true, - "addVersionUrl": true - } + "dependencies": { + "@floating-ui/dom": "^0.5.2", + "focus-trap": "^6.7.2", + "hello-goodbye": "^0.1.0-beta.5", + "tabbable": "^5.2.1" }, - "hooks": { - "after:bump": "npm run build" + "devDependencies": { + "@release-it/keep-a-changelog": "^2.3.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-typescript": "^8.3.0", + "prettier": "^2.6.2", + "release-it": "^14.12.4", + "rollup": "^2.66.1", + "rollup-plugin-filesize": "^9.1.2", + "rollup-plugin-terser": "^7.0.2", + "tslib": "^2.3.1", + "typescript": "^4.5.5" + }, + "release-it": { + "github": { + "release": true + }, + "plugins": { + "@release-it/keep-a-changelog": { + "filename": "CHANGELOG.md", + "addUnreleased": true, + "addVersionUrl": true + } + }, + "hooks": { + "after:bump": "npm run build" + } + }, + "prettier": { + "singleQuote": true } - } } diff --git a/rollup.config.js b/rollup.config.js index 787bd82..bdf4c8b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,22 +5,17 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'; import filesize from 'rollup-plugin-filesize'; export default { - input: 'src/index.ts', - output: { - file: pkg.main, - }, - external: [ - '@floating-ui/dom', - 'focus-trap', - 'hello-goodbye', - 'tabbable', - ], - plugins: [ - nodeResolve({ - exportConditions: ['production'], - }), - typescript(), - terser(), - filesize(), - ] + input: 'src/index.ts', + output: { + file: pkg.main, + }, + external: ['@floating-ui/dom', 'focus-trap', 'hello-goodbye', 'tabbable'], + plugins: [ + nodeResolve({ + exportConditions: ['production'], + }), + typescript(), + terser(), + filesize(), + ], }; diff --git a/src/alerts/README.md b/src/alerts/README.md index bc0d621..3d36b0d 100644 --- a/src/alerts/README.md +++ b/src/alerts/README.md @@ -21,10 +21,10 @@ document.body.appendChild(alerts); // 2. Create your own alert template function createAlert(type, message) { - const el = document.createElement('div'); - el.className = type; - el.textContent = message; - return el; + const el = document.createElement('div'); + el.className = type; + el.textContent = message; + return el; } // 3. Show an alert @@ -36,7 +36,7 @@ alerts.speak('There was an error'); ## Behavior -- The `` container is given the attributes `role="status"`, `aria-live="polite"` and `aria-relevant="additions"` so that any content additions will be announced. +- The `` container is given the attributes `role="status"`, `aria-live="polite"` and `aria-relevant="additions"` so that any content additions will be announced. ## API @@ -49,7 +49,7 @@ const alerts = document.querySelector('ui-alerts'); // Show an alert alerts.show( - el: HTMLElement, + el: HTMLElement, options?: AlertOptions ); @@ -75,34 +75,34 @@ alerts.speak(message: string); ```css /* Basic styles for the alert container */ ui-alerts { - position: fixed; - top: 0; - right: 0; - display: flex; - flex-direction: column-reverse; - align-items: flex-end; + position: fixed; + top: 0; + right: 0; + display: flex; + flex-direction: column-reverse; + align-items: flex-end; } /* Transitions can be applied to alerts using hello-goodbye */ @media (prefers-reduced-motion: no-preference) { - ui-alerts > .enter-active, - ui-alerts > .leave-active, - ui-alerts > .move { - transition: all .5s; - } - - ui-alerts > .enter-from { - transform: translateY(-100%); - } - - ui-alerts > .leave-to { - opacity: 0; - } + ui-alerts > .enter-active, + ui-alerts > .leave-active, + ui-alerts > .move { + transition: all 0.5s; + } + + ui-alerts > .enter-from { + transform: translateY(-100%); + } + + ui-alerts > .leave-to { + opacity: 0; + } } ``` ## Further Reading -- [WAI-ARIA Authoring Practices: Alert](https://w3c.github.io/aria-practices/#alert) -- [Scott O'Hara: A toast to an accessible toast...](https://www.scottohara.me/blog/2019/07/08/a-toast-to-a11y-toasts.html) -- [Adrian Roselli: Defining 'Toast' Messages](https://adrianroselli.com/2020/01/defining-toast-messages.html) +- [WAI-ARIA Authoring Practices: Alert](https://w3c.github.io/aria-practices/#alert) +- [Scott O'Hara: A toast to an accessible toast...](https://www.scottohara.me/blog/2019/07/08/a-toast-to-a11y-toasts.html) +- [Adrian Roselli: Defining 'Toast' Messages](https://adrianroselli.com/2020/01/defining-toast-messages.html) diff --git a/src/alerts/alerts.ts b/src/alerts/alerts.ts index fed6fbc..0e6878f 100644 --- a/src/alerts/alerts.ts +++ b/src/alerts/alerts.ts @@ -1,9 +1,9 @@ import { hello, goodbye, move } from 'hello-goodbye'; export type AlertOptions = { - key?: string, - duration?: number, -} + key?: string; + duration?: number; +}; export default class AlertsElement extends HTMLElement { public static duration: number = 10000; @@ -12,15 +12,15 @@ export default class AlertsElement extends HTMLElement { private index: number = 0; public connectedCallback(): void { - if (! this.hasAttribute('role')) { + if (!this.hasAttribute('role')) { this.setAttribute('role', 'status'); } - if (! this.hasAttribute('aria-live')) { + if (!this.hasAttribute('aria-live')) { this.setAttribute('aria-live', 'polite'); } - if (! this.hasAttribute('aria-relevant')) { + if (!this.hasAttribute('aria-relevant')) { this.setAttribute('aria-relevant', 'additions'); } } @@ -37,7 +37,10 @@ export default class AlertsElement extends HTMLElement { hello(el); }); - const duration = typeof options.duration !== 'undefined' ? Number(options.duration) : AlertsElement.duration; + const duration = + typeof options.duration !== 'undefined' + ? Number(options.duration) + : AlertsElement.duration; if (duration > 0) { this.startTimeout(el, duration); @@ -45,8 +48,14 @@ export default class AlertsElement extends HTMLElement { el.addEventListener('mouseenter', this.clearTimeout.bind(this, el)); el.addEventListener('focusin', this.clearTimeout.bind(this, el)); - el.addEventListener('mouseleave', this.startTimeout.bind(this, el, duration)); - el.addEventListener('focusout', this.startTimeout.bind(this, el, duration)); + el.addEventListener( + 'mouseleave', + this.startTimeout.bind(this, el, duration) + ); + el.addEventListener( + 'focusout', + this.startTimeout.bind(this, el, duration) + ); } return key; @@ -56,7 +65,9 @@ export default class AlertsElement extends HTMLElement { public dismiss(key: string): void; public dismiss(elOrKey: HTMLElement | string): void { if (typeof elOrKey === 'string') { - this.querySelectorAll(`[data-key="${elOrKey}"]`).forEach(el => { + this.querySelectorAll( + `[data-key="${elOrKey}"]` + ).forEach((el) => { this.dismiss(el); }); return; @@ -64,7 +75,7 @@ export default class AlertsElement extends HTMLElement { move(this.children, () => { goodbye(elOrKey, { - finish: () => this.removeChild(elOrKey) + finish: () => this.removeChild(elOrKey), }); }); @@ -86,7 +97,7 @@ export default class AlertsElement extends HTMLElement { overflow: 'hidden', position: 'absolute', whiteSpace: 'nowrap', - width: '1px' + width: '1px', }); el.textContent = message; this.show(el); @@ -94,9 +105,12 @@ export default class AlertsElement extends HTMLElement { private startTimeout(el: HTMLElement, duration: number) { this.clearTimeout(el); - this.timeouts.set(el, window.setTimeout(() => { - this.dismiss(el); - }, duration)); + this.timeouts.set( + el, + window.setTimeout(() => { + this.dismiss(el); + }, duration) + ); } private clearTimeout(el: HTMLElement) { diff --git a/src/menu/README.md b/src/menu/README.md index 77ebe66..e8eaec3 100644 --- a/src/menu/README.md +++ b/src/menu/README.md @@ -17,36 +17,34 @@ window.customElements.define('ui-menu', MenuElement); ```html - - - + + + ``` ## Behavior -- The `` element will be given a role of `menu`. +- The `` element will be given a role of `menu`. -- While the menu has focus, the Up/Down Arrow keys can be used to cycle focus through child elements with a role starting with `menuitem`. These elements are given a `tabindex` of `-1` so that they cannot be reached with the Tab key. +- While the menu has focus, the Up/Down Arrow keys can be used to cycle focus through child elements with a role starting with `menuitem`. These elements are given a `tabindex` of `-1` so that they cannot be reached with the Tab key. -- While the menu has focus, typing a string will move focus to the first item which contains text beginning with that string. The search string is cleared after a configurable delay. +- While the menu has focus, typing a string will move focus to the first item which contains text beginning with that string. The search string is cleared after a configurable delay. ## API ```ts -// The number of milliseconds that must pass without a key press +// The number of milliseconds that must pass without a key press // before the search string is cleared. MenuElement.searchDelay = 800; ``` ## Further Reading -- [WAI-ARIA Authoring Practices: Menu](https://w3c.github.io/aria-practices/#menu) -- [Inclusive Components: Menus & Menu Buttons](https://inclusive-components.design/menus-menu-buttons/) -- [Adrian Roselli: Donโ€™t Use ARIA Menu Roles for Site Nav](https://adrianroselli.com/2017/10/dont-use-aria-menu-roles-for-site-nav.html) +- [WAI-ARIA Authoring Practices: Menu](https://w3c.github.io/aria-practices/#menu) +- [Inclusive Components: Menus & Menu Buttons](https://inclusive-components.design/menus-menu-buttons/) +- [Adrian Roselli: Donโ€™t Use ARIA Menu Roles for Site Nav](https://adrianroselli.com/2017/10/dont-use-aria-menu-roles-for-site-nav.html) diff --git a/src/menu/menu.ts b/src/menu/menu.ts index 43431a2..a3863ea 100644 --- a/src/menu/menu.ts +++ b/src/menu/menu.ts @@ -7,16 +7,16 @@ export default class MenuElement extends HTMLElement { private searchTimeout?: number; public connectedCallback(): void { - if (! this.hasAttribute('role')) { + if (!this.hasAttribute('role')) { this.setAttribute('role', 'menu'); } - if (! this.hasAttribute('tabindex')) { + if (!this.hasAttribute('tabindex')) { this.setAttribute('tabindex', '-1'); } - this.items.forEach(el => { - if (! el.hasAttribute('tabindex')) { + this.items.forEach((el) => { + if (!el.hasAttribute('tabindex')) { el.setAttribute('tabindex', '-1'); } }); @@ -29,8 +29,9 @@ export default class MenuElement extends HTMLElement { } private get items() { - return Array.from(this.querySelectorAll('[role^=menuitem]')) - .filter(item => isFocusable(item)); + return Array.from( + this.querySelectorAll('[role^=menuitem]') + ).filter((item) => isFocusable(item)); } private onKeyDown = (e: KeyboardEvent): void => { @@ -59,17 +60,22 @@ export default class MenuElement extends HTMLElement { this.search = ''; }, MenuElement.searchDelay); - this.items.some(el => { - if (el.textContent?.trim().toLowerCase().indexOf(this.search) === 0) { + this.items.some((el) => { + if ( + el.textContent?.trim().toLowerCase().indexOf(this.search) === 0 + ) { el.focus(); return true; } }); - } + }; private navigate(step: number) { const items = this.items; - let index = (document.activeElement instanceof HTMLElement ? items.indexOf(document.activeElement) : -1) + step; + let index = + (document.activeElement instanceof HTMLElement + ? items.indexOf(document.activeElement) + : -1) + step; if (index < 0) { index = items.length - 1; } diff --git a/src/modal/README.md b/src/modal/README.md index 9ebb226..94afbae 100644 --- a/src/modal/README.md +++ b/src/modal/README.md @@ -14,110 +14,118 @@ window.customElements.define('ui-modal', ModalElement); ```html ``` ## Behavior -- The `` element should contain a single child with the role `dialog` or `alertdialog` and an appropriate label and description. The `aria-modal` attribute will be automatically set to `true`. +- The `` element should contain a single child with the role `dialog` or `alertdialog` and an appropriate label and description. The `aria-modal` attribute will be automatically set to `true`. -- Set the `open` property of the `` element to `true` to open the dialog. +- Set the `open` property of the `` element to `true` to open the dialog. -- Upon opening: - - The `hidden` attribute on the dialog is removed. - - Focus will be moved to the first element inside the dialog that has the `autofocus` attribute. If none is found, focus will be moved to the dialog element itself. - - A focus trap is activated, such that Tab and Shift + Tab do not move focus outside the dialog. +- Upon opening: -- The dialog will be closed if: - - The Escape key is pressed. - - The backdrop is clicked, unless the `` element has the `static` attribute. - - The `open` attribute is removed, or the `close()` method is called. + - The `hidden` attribute on the dialog is removed. + - Focus will be moved to the first element inside the dialog that has the `autofocus` attribute. If none is found, focus will be moved to the dialog element itself. + - A focus trap is activated, such that Tab and Shift + Tab do not move focus outside the dialog. -- Upon closing: - - The `hidden` attribute on the dialog is reinstated. - - Focus will be returned to the element that was focused before the dialog was opened. - - The focus trap is deactivated. +- The dialog will be closed if: + + - The Escape key is pressed. + - The backdrop is clicked, unless the `` element has the `static` attribute. + - The `open` attribute is removed, or the `close()` method is called. + +- Upon closing: + - The `hidden` attribute on the dialog is reinstated. + - Focus will be returned to the element that was focused before the dialog was opened. + - The focus trap is deactivated. ## API ```ts -// Do something to call attention to the modal. This is called when the backdrop +// Do something to call attention to the modal. This is called when the backdrop // is clicked on a `static` modal. By default, the follow animation is used. -ModalElement.attention = (el: Element) => el.animate([ - { transform: 'scale(1)' }, - { transform: 'scale(1.1)' }, - { transform: 'scale(1)' } -], 300); +ModalElement.attention = (el: Element) => + el.animate( + [ + { transform: 'scale(1)' }, + { transform: 'scale(1.1)' }, + { transform: 'scale(1)' }, + ], + 300 + ); const modal = document.querySelector('ui-modal'); // Open the modal. modal.open = true; -// Close the modal. You can also set open = false, +// Close the modal. You can also set open = false, // but this will bypass any `beforeclose` listeners. modal.close(); modal.addEventListener('open', callback); -modal.addEventListener('beforeclose', e => e.preventDefault()); +modal.addEventListener('beforeclose', (e) => e.preventDefault()); modal.addEventListener('close', callback); ``` ```css /* Style the modal container */ ui-modal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; } /* Style the backdrop */ ui-modal::part(backdrop) { - background: rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, 0.5); } /* Transitions can be applied to the modal and its parts using hello-goodbye */ @media (prefers-reduced-motion: no-preference) { - ui-modal.enter-active, - ui-modal.leave-active { - transition: opacity .5s; - } - - ui-modal.enter-from, - ui-modal.leave-to { - opacity: 0; - } - - ui-modal.enter-active::part(content), - ui-modal.leave-active::part(content) { - transition: transform .5s; - } - - ui-modal.enter-from::part(content), - ui-modal.leave-to::part(content) { - transform: scale(0.5); - } + ui-modal.enter-active, + ui-modal.leave-active { + transition: opacity 0.5s; + } + + ui-modal.enter-from, + ui-modal.leave-to { + opacity: 0; + } + + ui-modal.enter-active::part(content), + ui-modal.leave-active::part(content) { + transition: transform 0.5s; + } + + ui-modal.enter-from::part(content), + ui-modal.leave-to::part(content) { + transform: scale(0.5); + } } ``` ## Further Reading -- [WAI-ARIA Authoring Practices: Dialog (Modal)](https://w3c.github.io/aria-practices/#dialog_modal) +- [WAI-ARIA Authoring Practices: Dialog (Modal)](https://w3c.github.io/aria-practices/#dialog_modal) diff --git a/src/modal/modal.ts b/src/modal/modal.ts index 49be7af..5d6d0d3 100644 --- a/src/modal/modal.ts +++ b/src/modal/modal.ts @@ -2,11 +2,15 @@ import { createFocusTrap, FocusTrap } from 'focus-trap'; import { goodbye, hello } from 'hello-goodbye'; export default class ModalElement extends HTMLElement { - public static attention: (el: Element) => void = el => el.animate([ - { transform: 'scale(1)' }, - { transform: 'scale(1.1)' }, - { transform: 'scale(1)' } - ], 300); + public static attention: (el: Element) => void = (el) => + el.animate( + [ + { transform: 'scale(1)' }, + { transform: 'scale(1.1)' }, + { transform: 'scale(1)' }, + ], + 300 + ); static get observedAttributes() { return ['open']; @@ -44,19 +48,19 @@ export default class ModalElement extends HTMLElement { public connectedCallback(): void { this.connected = true; - if (! this.content?.hasAttribute('role')) { + if (!this.content?.hasAttribute('role')) { this.content?.setAttribute('role', 'dialog'); } - if (! this.content?.hasAttribute('aria-modal')) { + if (!this.content?.hasAttribute('aria-modal')) { this.content?.setAttribute('aria-modal', 'true'); } - if (! this.content?.hasAttribute('tabindex')) { + if (!this.content?.hasAttribute('tabindex')) { this.content?.setAttribute('tabindex', '-1'); } - this.addEventListener('keydown', e => { + this.addEventListener('keydown', (e) => { if (e.key === 'Escape' && this.open) { e.preventDefault(); e.stopPropagation(); @@ -86,7 +90,7 @@ export default class ModalElement extends HTMLElement { } public close() { - if (! this.open) return; + if (!this.open) return; const event = new Event('beforeclose', { cancelable: true }); @@ -95,8 +99,12 @@ export default class ModalElement extends HTMLElement { } } - public attributeChangedCallback(name: string, oldValue: string, newValue: string): void { - if (name !== 'open' || ! this.connected) return; + public attributeChangedCallback( + name: string, + oldValue: string, + newValue: string + ): void { + if (name !== 'open' || !this.connected) return; if (newValue !== null) { this.wasOpened(); @@ -120,7 +128,7 @@ export default class ModalElement extends HTMLElement { this.focusTrap.deactivate(); goodbye(this, { - finish: () => this.hidden = true, + finish: () => (this.hidden = true), }); this.dispatchEvent(new Event('close')); diff --git a/src/popup/README.md b/src/popup/README.md index 43606b8..a7718be 100644 --- a/src/popup/README.md +++ b/src/popup/README.md @@ -4,8 +4,8 @@ Popup is [an ill-defined term](https://adrianroselli.com/2021/07/stop-using-pop-up.html), but this popup element is intended to fulfill two specific use-cases: -- A **disclosure widget** where the disclosed content "pops up" as an overlay positioned adjacent to the trigger. -- A **menu button** where the popup content is a [Menu](../menu) element. +- A **disclosure widget** where the disclosed content "pops up" as an overlay positioned adjacent to the trigger. +- A **menu button** where the popup content is a [Menu](../menu) element. ## Example @@ -17,26 +17,24 @@ window.customElements.define('ui-popup', PopupElement); ```html - - + + ``` ## Behavior -- The first descendant that is a ` - - + + + ``` ## Behavior -- The `` element will be given a role of `toolbar`. +- The `` element will be given a role of `toolbar`. -- Focus management is implemented so the keyboard tab sequence includes one stop for the toolbar, and the Left Arrow, Right Arrow, Home, and End keys move focus among the controls in the toolbar. +- Focus management is implemented so the keyboard tab sequence includes one stop for the toolbar, and the Left Arrow, Right Arrow, Home, and End keys move focus among the controls in the toolbar. ## Further Reading -- [WAI-ARIA Authoring Practices: Toolbar](https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar) +- [WAI-ARIA Authoring Practices: Toolbar](https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar) diff --git a/src/toolbar/toolbar.ts b/src/toolbar/toolbar.ts index f0193d9..82093b2 100644 --- a/src/toolbar/toolbar.ts +++ b/src/toolbar/toolbar.ts @@ -2,7 +2,7 @@ import { focusable } from 'tabbable'; export default class ToolbarElement extends HTMLElement { connectedCallback(): void { - if (! this.hasAttribute('role')) { + if (!this.hasAttribute('role')) { this.setAttribute('role', 'toolbar'); } @@ -18,10 +18,16 @@ export default class ToolbarElement extends HTMLElement { private onInitialFocus = (e: FocusEvent): void => { this.removeAttribute('tabindex'); this.focusControlAtIndex(0); - } + }; private onKeyDown = (e: KeyboardEvent): void => { - if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft' && e.key !== 'Home' && e.key !== 'End') return; + if ( + e.key !== 'ArrowRight' && + e.key !== 'ArrowLeft' && + e.key !== 'Home' && + e.key !== 'End' + ) + return; const controls = this.controls; const length = this.controls.length; @@ -38,7 +44,7 @@ export default class ToolbarElement extends HTMLElement { this.focusControlAtIndex(n); e.preventDefault(); - } + }; private focusControlAtIndex(index: number): void { this.controls.forEach((control, i) => { diff --git a/src/tooltip/README.md b/src/tooltip/README.md index 0b3adfd..b3afb94 100644 --- a/src/tooltip/README.md +++ b/src/tooltip/README.md @@ -15,21 +15,23 @@ window.customElements.define('ui-tooltip', TooltipElement); ```html ``` ## Behavior -- When the parent element (`