mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
add allDefined util
This commit is contained in:
@@ -8,6 +8,30 @@ Web Awesome components are just regular HTML elements, or [custom elements](http
|
||||
|
||||
If you're new to custom elements, often referred to as "web components," this section will familiarize you with how to use them.
|
||||
|
||||
## Awaiting Registration
|
||||
|
||||
Unlike traditional frameworks, custom elements don't have a centralized initialization phase. This means you need to verify that a custom element has been properly registered before attempting to interact with its properties or methods.
|
||||
|
||||
You can use the [`customElements.whenDefined()`](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined) method to ensure a specific component is ready:
|
||||
|
||||
```ts
|
||||
await customElements.whenDefined('wa-button');
|
||||
|
||||
// <wa-button> is ready to use!
|
||||
const button = document.querySelector('wa-button');
|
||||
```
|
||||
|
||||
When working with multiple components, checking each one individually can become tedious. For convenience, Web Awesome provides the `allDefined()` function which automatically detects and waits for all Web Awesome components in the DOM to be initialized before resolving.
|
||||
|
||||
```ts
|
||||
import { allDefined } from '/dist/webawesome.js';
|
||||
|
||||
// Waits for all Web Awesome components in the DOM to be registered
|
||||
await allDefined();
|
||||
|
||||
// All Web Awesome components on the page are ready!
|
||||
```
|
||||
|
||||
## Attributes & Properties
|
||||
|
||||
Many components have properties that can be set using attributes. For example, buttons accept a `size` attribute that maps to the `size` property which dictates the button's size.
|
||||
|
||||
64
src/utilities/defined.ts
Normal file
64
src/utilities/defined.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
interface AllDefinedOptions {
|
||||
/**
|
||||
* A callback that accepts a custom element tag name and returns `true` if the custom element should be defined before
|
||||
* resolving or `false` to skip it. The tag name is always in lowercase.
|
||||
*/
|
||||
match: (tagName: string) => boolean;
|
||||
|
||||
/**
|
||||
* To wait for additional custom elements that may not be on the page when the function is called, provide them here.
|
||||
*/
|
||||
additionalElements: string | string[];
|
||||
|
||||
/**
|
||||
* The root in which to look for custom elements. Defaults to `document`. By design, shadow roots are not traversed,
|
||||
* but you can call this function and set `root` to a custom element's shadow root if needed.
|
||||
*/
|
||||
root: Document | ShadowRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for custom elements that are currently on the page to be registered before resolving. This is sugar for
|
||||
* awaiting `customElements.whenDefined()` multiple times. By default, the function waits for all undefined Web Awesome
|
||||
* elements, but you can pass a custom match function to wait for other custom elements instead.
|
||||
*
|
||||
* The function returns with `Promise.all()`, so any loading errors will cause it to reject. Make sure you handle errors
|
||||
* accordingly using a try/catch block or a `.catch()`.
|
||||
*
|
||||
* @example
|
||||
* // Wait for Web Awesome elements
|
||||
* await allDefined();
|
||||
*
|
||||
* // Wait for all custom elements that start with `foo-` as well as the `<bar-button>` element
|
||||
* await allDefined({
|
||||
* match: tagName => tagName.startsWith('foo-'),
|
||||
* additionalElements: ['bar-button', 'baz-dialog']
|
||||
* });
|
||||
*/
|
||||
export async function allDefined(options?: Partial<AllDefinedOptions>) {
|
||||
const opts: AllDefinedOptions = {
|
||||
match: tagName => tagName.startsWith('wa-'),
|
||||
additionalElements: [],
|
||||
root: document,
|
||||
...options,
|
||||
};
|
||||
|
||||
// Ensure additional elements is an array
|
||||
const additionalElements = Array.isArray(opts.additionalElements)
|
||||
? opts.additionalElements
|
||||
: [opts.additionalElements];
|
||||
|
||||
// Discover undefined elements in the document
|
||||
const undefinedElements = [...opts.root.querySelectorAll(':not(:defined)')]
|
||||
.map(el => el.localName)
|
||||
.filter((tag, index, arr) => arr.indexOf(tag) === index) // make it unique
|
||||
.filter(tag => opts.match(tag)); // make sure it matches
|
||||
|
||||
const tagsToAwait = [...undefinedElements, ...additionalElements];
|
||||
|
||||
// Wait for all to be registered
|
||||
await Promise.all(tagsToAwait.map(tag => customElements.whenDefined(tag)));
|
||||
|
||||
// Wait a cycle for the first update
|
||||
await new Promise(requestAnimationFrame);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export { registerIconLibrary, unregisterIconLibrary } from './components/icon/library.js';
|
||||
export { discover, preventTurboFouce, startLoader, stopLoader } from './utilities/autoloader.js';
|
||||
export { getBasePath, getKitCode, setBasePath, setKitCode } from './utilities/base-path.js';
|
||||
export { allDefined } from './utilities/defined.js';
|
||||
export { registerTranslation } from './utilities/localize.js';
|
||||
|
||||
// Utilities
|
||||
|
||||
Reference in New Issue
Block a user