-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add reactive controllers package
- Loading branch information
Showing
18 changed files
with
843 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
packages/**/*.d.ts | ||
packages/*/lib/**/* | ||
packages/*/node_modules/**/* | ||
packages/*/node_modules/**/* | ||
tools/**/*.d.ts | ||
tools/*/node_modules/**/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
{ | ||
"root": true, | ||
"env": { | ||
"browser": true, | ||
"node": false, | ||
"es6": true | ||
}, | ||
"parser": "@typescript-eslint/parser", | ||
"plugins": ["@typescript-eslint", "notice", "spectrum-web-components"], | ||
"extends": [ | ||
"plugin:@typescript-eslint/recommended", | ||
"prettier", | ||
"plugin:lit-a11y/recommended" | ||
], | ||
"rules": { | ||
"no-debugger": 2, | ||
"no-console": ["error", { "allow": ["warn", "error"] }], | ||
"spectrum-web-components/prevent-argument-names": [ | ||
"error", | ||
["e", "ev", "evt", "err"] | ||
], | ||
"spectrum-web-components/document-active-element": ["error"], | ||
"notice/notice": [ | ||
"error", | ||
{ | ||
"mustMatch": "Copyright [0-9]{0,4} Adobe. All rights reserved.", | ||
"templateFile": "config/license.js" | ||
} | ||
], | ||
"@typescript-eslint/explicit-function-return-type": [ | ||
1, | ||
{ | ||
"allowExpressions": true | ||
} | ||
], | ||
"sort-imports": [ | ||
"error", | ||
{ | ||
"ignoreCase": true, | ||
"ignoreDeclarationSort": true, | ||
"ignoreMemberSort": false, | ||
"allowSeparatedGroups": false | ||
} | ||
], | ||
"lit-a11y/click-events-have-key-events": [ | ||
"error", | ||
{ | ||
"allowList": [ | ||
"sp-button", | ||
"sp-action-button", | ||
"sp-checkbox", | ||
"sp-radio", | ||
"sp-switch", | ||
"sp-menu-item", | ||
"sp-clear-button", | ||
"sp-underlay" | ||
] | ||
} | ||
] | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": ["*.test.ts", "*.stories.ts", "**/benchmark/*.ts"], | ||
"rules": { | ||
"spectrum-web-components/document-active-element": ["off"], | ||
"lit-a11y/no-autofocus": ["off"], | ||
"lit-a11y/tabindex-no-positive": ["off"] | ||
} | ||
}, | ||
{ | ||
"files": ["**/icons/*.ts", "**/src/elements/*.ts"], | ||
"rules": { | ||
"sort-imports": ["off"] | ||
} | ||
}, | ||
{ | ||
"files": ["*.stories.ts"], | ||
"rules": { | ||
"no-console": ["off"] | ||
} | ||
}, | ||
{ | ||
"files": ["Picker.ts", "SplitButton.ts"], | ||
"rules": { | ||
"lit-a11y/click-events-have-key-events": [ | ||
"error", | ||
{ | ||
"allowList": [ | ||
"sp-button", | ||
"sp-action-button", | ||
"sp-checkbox", | ||
"sp-radio", | ||
"sp-switch", | ||
"sp-menu-item", | ||
"sp-clear-button", | ||
"sp-underlay", | ||
"sp-popover" | ||
] | ||
} | ||
] | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
stories | ||
test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
## Description | ||
|
||
[Reactive controllers](https://lit.dev/docs/composition/controllers/) are a tool for code reuse and composition within [Lit](https://lit.dev), a core dependency of Spectrum Web Components. Reactive controllers can be shared across components to reduce both code complexity and size, and to deliver a consistent user experience. These reactive controllers are used by the Spectrum Web Components library and are published to NPM for you to leverage in your projects as well. | ||
|
||
### Reactive controllers | ||
|
||
- [RovingTabindexController](../roving-tab-index) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"name": "@spectrum-web-components/reactive-controllers", | ||
"version": "0.0.1", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"description": "ReactiveControllers for powering common UI patterns", | ||
"license": "Apache-2.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/adobe/spectrum-web-components.git", | ||
"directory": "packages/reactive-controllers" | ||
}, | ||
"author": "", | ||
"homepage": "https://adobe.github.io/spectrum-web-components/components/reactive-controllers", | ||
"bugs": { | ||
"url": "https://github.com/adobe/spectrum-web-components/issues" | ||
}, | ||
"main": "src/index.js", | ||
"module": "src/index.js", | ||
"type": "module", | ||
"exports": { | ||
".": "./src/index.js", | ||
"./src/*": "./src/*", | ||
"./package.json": "./package.json" | ||
}, | ||
"scripts": { | ||
"test": "echo \"Error: run tests from mono-repo root.\" && exit 1" | ||
}, | ||
"files": [ | ||
"**/*.d.ts", | ||
"**/*.js", | ||
"**/*.js.map", | ||
"!stories/", | ||
"!test/" | ||
], | ||
"keywords": [ | ||
"spectrum css", | ||
"web components", | ||
"lit-element", | ||
"lit-html", | ||
"reactive controllers" | ||
], | ||
"dependencies": { | ||
"lit": "^2.0.2", | ||
"tslib": "^2.0.0" | ||
}, | ||
"types": "./src/index.d.ts" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
## Description | ||
|
||
[Roving tabindex](https://www.w3.org/TR/wai-aria-practices-1.2/#kbd_roving_tabindex) in a pattern whereby multiple focusable elements are represented by a single `tabindex=0` element, while the individual elements maintain `tabindex=-1` and are made accessible via arrow keys after the entry element if focused. This allows keyboard users to quickly tab through a page without having to stop on every element in a large collection. Attaching a `RovingTabindexController` to your custom element will manage the supplied `elements` via this pattern. | ||
|
||
### Usage | ||
|
||
[![See it on NPM!](https://img.shields.io/npm/v/@spectrum-web-components/reactive-controllers?style=for-the-badge)](https://www.npmjs.com/package/@spectrum-web-components/reactive-controllers) | ||
[![How big is this package in your project?](https://img.shields.io/bundlephobia/minzip/@spectrum-web-components/reactive-controllers?style=for-the-badge)](https://bundlephobia.com/result?p=@spectrum-web-components/reactive-controllers) | ||
|
||
``` | ||
yarn add @spectrum-web-components/reactive-controllers | ||
``` | ||
|
||
Import the `RovingTabindexController` via: | ||
|
||
``` | ||
import { RovingTabindexController } from '@spectrum-web-components/reactive-controllers/RovingTabindex.js'; | ||
``` | ||
|
||
## Example | ||
|
||
A `Container` element that manages a collection of `<sp-button>` elements that are slotted into it from outside might look like the following: | ||
|
||
```js | ||
import { html, LitElement } from 'lit'; | ||
import { RovingTabindexController } from '@spectrum-web-components/reactive-controllers/RovingTabindex.js'; | ||
import type { Button } from '@spectrum-web-components/button'; | ||
|
||
class Container extends LitElement { | ||
rovingTabindexController = | ||
new RovingTabindexController() < | ||
Button > | ||
(this, | ||
{ | ||
elements: () => [...this.querySelectorAll('sp-button')], | ||
}); | ||
|
||
render() { | ||
return html` | ||
<slot></slot> | ||
`; | ||
} | ||
} | ||
``` | ||
|
||
The above will default to entering the `Container` element via the first `<sp-button>` element every time while making all slotted `<sp-button>` elements accessible via the the arrow key (`ArrowLeft`, `ArrowRight`, `ArrowUp`, and `ArrowDown`) managed tab order. | ||
|
||
## Options | ||
|
||
A `Container` can further customize the implementation of the `RovingTabindexController` with the following options: | ||
|
||
- `direction` to customize how and which arrow keys manage what element is to be focused and accepts a either a string of `both`, `vertical`, `horizontal`, or `grid` or a method returning one of those strings | ||
- `elementEnterAction` enacts actions other than `focus` on the entered element which accepts a method with a signature of `(el: T) => void` | ||
- `elements` provides the elements that will have their `tabindex` managed via a method with a signature of `() => T[]` | ||
- `focusInIndex` to control what element will recieve `tabindex=0` while focus is outside of the `Container` and accepts a method with a signature of `(_elements: T[]) => number` | ||
- `isFocusableElement` describes the state an element much be in to receive `focus` via a method with a signature of `(el: T) => boolean` | ||
- `listenerScope` outlines which parts on a container's DOM when listening for arrow key presses via an element reference or a method returning an element reference with the signature `() => HTMLElement` | ||
|
||
## Advanced usage | ||
|
||
These options can be combined to form various interfaces from the more default that we saw above to the very complex. Below is another `Container` that manages slotted `<sp-button>` elements via the `RovingTabindexController`. The options provided ensure: | ||
|
||
- the first focused `<sp-button>` is the one `selected` by the container | ||
- the elements are only focused via the `ArrowLeft` and `ArrowRight` keys | ||
- when an element is focused it becomes the `selected` element | ||
- only enabled elements are focusable | ||
|
||
```js | ||
import { html, LitElement } from 'lit'; | ||
import { RovingTabindexController } from '@spectrum-web-components/reactive-controllers/RovingTabindex.js'; | ||
import type { Button } from '@spectrum-web-components/button'; | ||
|
||
class Container extends LitElement { | ||
rovingTabindexController = new RovingTabindexController<Button>(this, { | ||
focusInIndex: (buttons) => return this.selected | ||
? buttons.indexOf(this.selected) | ||
: 0, | ||
direction: 'horizontal', | ||
elementEnterAction: (button) => this.selected = button, | ||
elements: () => [...this.querySelectorAll('sp-button')], | ||
isFocusableElement: (button) => !button.disabled, | ||
}); | ||
|
||
selected!: Button; | ||
|
||
render() { | ||
return html`<slot></slot>`; | ||
} | ||
} | ||
``` | ||
|
||
The above usage is very close to what can be seen in the [`<sp-radio-group>` element](../components/radio). |
Oops, something went wrong.