nano-skeleton 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +18 -18
- package/dist/constants.d.ts +3 -4
- package/dist/constants.js +12 -12
- package/dist/constants.js.map +1 -1
- package/dist/{ContextRequestEvent.js → context-request-event.js} +1 -1
- package/dist/context-request-event.js.map +1 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{SkeletonProvider.d.ts → skeleton-provider.d.ts} +2 -4
- package/dist/{SkeletonProvider.js → skeleton-provider.js} +2 -2
- package/dist/skeleton-provider.js.map +1 -0
- package/dist/{Skeleton.d.ts → skeleton.d.ts} +7 -7
- package/dist/skeleton.js +2 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/styles.js +1 -1
- package/dist/styles.js.map +1 -1
- package/package.json +5 -5
- package/dist/ContextRequestEvent.js.map +0 -1
- package/dist/Skeleton.js +0 -2
- package/dist/Skeleton.js.map +0 -1
- package/dist/SkeletonProvider.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- chore: add oxlint
|
|
8
|
+
|
|
9
|
+
## 0.5.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- refactor: get rid of data attributes
|
|
14
|
+
|
|
15
|
+
BREAKING CHANGES:
|
|
16
|
+
|
|
17
|
+
- use `inline` attribute instead of `text` attribute
|
|
18
|
+
- `data-text` attribute is no more
|
|
19
|
+
- `data-loading` attribute is no more as well
|
|
20
|
+
|
|
3
21
|
## 0.4.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -103,9 +103,9 @@ all of its skeletons at once.
|
|
|
103
103
|
A `<nano-skeleton>` without a provider simply renders its content as usual.
|
|
104
104
|
|
|
105
105
|
`<nano-skeleton>` works in one of two modes, picked automatically from its
|
|
106
|
-
content (
|
|
106
|
+
content (inline mode can also be forced with the `inline` attribute).
|
|
107
107
|
|
|
108
|
-
###
|
|
108
|
+
### Inline mode
|
|
109
109
|
|
|
110
110
|
If the skeleton directly contains non-empty text, it renders inline and the text
|
|
111
111
|
itself becomes the placeholder. Each line of wrapped text gets its own rounded
|
|
@@ -136,11 +136,11 @@ placeholder text first and replace it with real data later:
|
|
|
136
136
|
|
|
137
137
|
Automatic detection only looks at text placed directly inside the skeleton.
|
|
138
138
|
When the text is wrapped in an inline element, like a link or `<strong>`, add
|
|
139
|
-
`
|
|
139
|
+
`inline` to force inline mode:
|
|
140
140
|
|
|
141
141
|
```html
|
|
142
142
|
<p>
|
|
143
|
-
<nano-skeleton
|
|
143
|
+
<nano-skeleton inline>
|
|
144
144
|
<a href="/posts/42">Read the full post</a>
|
|
145
145
|
</nano-skeleton>
|
|
146
146
|
</p>
|
|
@@ -155,7 +155,7 @@ placeholder still reads as a link. To hide them too, wrap the link's content in
|
|
|
155
155
|
a `<span>`:
|
|
156
156
|
|
|
157
157
|
```html
|
|
158
|
-
<nano-skeleton
|
|
158
|
+
<nano-skeleton inline>
|
|
159
159
|
<a href="/posts/42"><span>Read the full post</span></a>
|
|
160
160
|
</nano-skeleton>
|
|
161
161
|
```
|
|
@@ -304,8 +304,8 @@ React 19 passes properties to custom elements, so `loading` can be bound
|
|
|
304
304
|
directly:
|
|
305
305
|
|
|
306
306
|
```tsx
|
|
307
|
-
import { init } from "nano-skeleton";
|
|
308
307
|
import { useQuery } from "@tanstack/react-query";
|
|
308
|
+
import { init } from "nano-skeleton";
|
|
309
309
|
|
|
310
310
|
init();
|
|
311
311
|
|
|
@@ -359,7 +359,7 @@ declare module "react" {
|
|
|
359
359
|
Skeleton
|
|
360
360
|
> & {
|
|
361
361
|
interactive?: boolean;
|
|
362
|
-
|
|
362
|
+
inline?: boolean;
|
|
363
363
|
};
|
|
364
364
|
[PROVIDER_NAME]: DetailedHTMLProps<
|
|
365
365
|
HTMLAttributes<SkeletonProvider>,
|
|
@@ -390,18 +390,19 @@ While loading, the provider is exposed to assistive technology as
|
|
|
390
390
|
|
|
391
391
|
### `<nano-skeleton>`
|
|
392
392
|
|
|
393
|
-
| Attribute / property | Type | Description
|
|
394
|
-
| -------------------- | --------- |
|
|
395
|
-
| `interactive` | `boolean` | Keeps the content focusable and clickable while loading. See [Interactive skeletons](#interactive-skeletons).
|
|
396
|
-
| `
|
|
393
|
+
| Attribute / property | Type | Description |
|
|
394
|
+
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
395
|
+
| `interactive` | `boolean` | Keeps the content focusable and clickable while loading. See [Interactive skeletons](#interactive-skeletons). |
|
|
396
|
+
| `inline` | `boolean` | Forces inline mode, e.g. for text inside a link. See [Text wrapped in elements](#text-wrapped-in-elements). |
|
|
397
397
|
|
|
398
|
-
|
|
398
|
+
The component also manages these attributes itself, which you can use as
|
|
399
|
+
styling hooks:
|
|
399
400
|
|
|
400
|
-
| Attribute
|
|
401
|
-
|
|
|
402
|
-
| `
|
|
403
|
-
| `
|
|
404
|
-
| `inert`
|
|
401
|
+
| Attribute | Set when |
|
|
402
|
+
| --------- | ------------------------------------------------------------------------------------- |
|
|
403
|
+
| `loading` | The nearest provider is loading. |
|
|
404
|
+
| `inline` | The skeleton directly contains text, or `inline` is set (inline mode). |
|
|
405
|
+
| `inert` | The nearest provider is loading and `interactive` isn't set. Blocks focus and clicks. |
|
|
405
406
|
|
|
406
407
|
### Exports
|
|
407
408
|
|
|
@@ -424,7 +425,6 @@ The look is controlled by CSS custom properties:
|
|
|
424
425
|
| `--nano-skeleton-background-image` | A light/dark-aware horizontal gradient |
|
|
425
426
|
| `--nano-skeleton-background-size` | `300%` |
|
|
426
427
|
| `--nano-skeleton-animation` | `shimmer 2s infinite ease-in-out` |
|
|
427
|
-
| `--nano-skeleton-text-border-radius` | `0.375rem` (text mode only) |
|
|
428
428
|
|
|
429
429
|
Each `<nano-skeleton>` inherits these from its ancestors, so set them on a
|
|
430
430
|
parent element (or on `:root` to change all skeletons at once), not on the
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
//#endregion
|
|
1
|
+
declare const COMPONENT_NAME: "nano-skeleton";
|
|
2
|
+
declare const PROVIDER_NAME: "nano-skeleton-provider";
|
|
3
|
+
export { COMPONENT_NAME, PROVIDER_NAME };
|
|
5
4
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
|
@@ -15,7 +15,7 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
15
15
|
);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
:scope:not([
|
|
18
|
+
:scope:not([inline]) {
|
|
19
19
|
display: contents;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -24,14 +24,14 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
24
24
|
* as layout is intact. Hidden visually only: focus and accessibility are
|
|
25
25
|
* handled by inert, so interactive skeletons keep both.
|
|
26
26
|
*/
|
|
27
|
-
:scope[
|
|
27
|
+
:scope[loading] > * > * {
|
|
28
28
|
opacity: 0 !important;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Skeleton's content is just an inline text
|
|
33
33
|
*/
|
|
34
|
-
:scope[
|
|
34
|
+
:scope[loading][inline] {
|
|
35
35
|
display: inline;
|
|
36
36
|
border-radius: var(
|
|
37
37
|
--nano-skeleton-text-border-radius,
|
|
@@ -55,18 +55,18 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
55
55
|
* Children and grandchildren need to inherit these styles
|
|
56
56
|
* for consistent outlines
|
|
57
57
|
*/
|
|
58
|
-
:scope[
|
|
59
|
-
:scope[
|
|
58
|
+
:scope[loading][inline] > *,
|
|
59
|
+
:scope[loading][inline] > * > * {
|
|
60
60
|
border-radius: inherit;
|
|
61
61
|
text-box: inherit;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
:scope[
|
|
64
|
+
:scope[loading] > :is(video, img):only-child {
|
|
65
65
|
content: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") !important;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
:scope[
|
|
69
|
-
:scope[
|
|
68
|
+
:scope[loading][inline],
|
|
69
|
+
:scope[loading]:not([inline]) > * {
|
|
70
70
|
background-image: var(
|
|
71
71
|
--nano-skeleton-background-image,
|
|
72
72
|
var(--nano-skeleton-background-image-default)
|
|
@@ -78,8 +78,8 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
@media not (prefers-reduced-motion: reduce) {
|
|
81
|
-
:scope[
|
|
82
|
-
:scope[
|
|
81
|
+
:scope[loading][inline],
|
|
82
|
+
:scope[loading]:not([inline]) > * {
|
|
83
83
|
animation: var(
|
|
84
84
|
--nano-skeleton-animation,
|
|
85
85
|
var(--nano-skeleton-animation-default)
|
|
@@ -90,8 +90,8 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
90
90
|
/**
|
|
91
91
|
* The shimmer follows the reading direction
|
|
92
92
|
*/
|
|
93
|
-
:scope[
|
|
94
|
-
:scope[
|
|
93
|
+
:scope[loading][inline]:dir(rtl),
|
|
94
|
+
:scope[loading]:not([inline]) > :dir(rtl) {
|
|
95
95
|
--nano-skeleton-shimmer-from: 0%;
|
|
96
96
|
--nano-skeleton-shimmer-to: 100%;
|
|
97
97
|
}
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":["COMPONENT_NAME","PROVIDER_NAME","CSS_CONTENT","SKELETON_CONTEXT"],"sources":["../src/constants.ts"],"sourcesContent":["const isDev = import.meta.env.MODE !== \"production\";\n\
|
|
1
|
+
{"version":3,"file":"constants.js","names":["COMPONENT_NAME","PROVIDER_NAME","CSS_CONTENT","SKELETON_CONTEXT"],"sources":["../src/constants.ts"],"sourcesContent":["const isDev = import.meta.env.MODE !== \"production\";\n\nconst COMPONENT_NAME = \"nano-skeleton\" as const;\n\nconst PROVIDER_NAME = `${COMPONENT_NAME}-provider` as const;\n\nconst { default: CSS_CONTENT } = isDev\n ? // oxlint-disable-next-line node/no-top-level-await -- this is fine for dev mode\n await import(\"./styles.css?inline\")\n : { default: `__CSS_CONTENT__` };\n\nconst SKELETON_CONTEXT = Symbol.for(`${COMPONENT_NAME}-context`);\n\nexport { COMPONENT_NAME, PROVIDER_NAME, CSS_CONTENT, SKELETON_CONTEXT };\n"],"mappings":"AAEA,MAAMA,EAAiB,gBAEjBC,EAAgB,GAAGD,EAAe,WAElC,CAAE,QAASE,GAGb,CAAE,QAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Gb,EAEIC,EAAmB,OAAO,IAAI,GAAGH,EAAe,SAAS,E"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var e=class extends Event{context;callback;subscribe;constructor(e,t,n=!1){super(`context-request`,{bubbles:!0,composed:!0}),this.context=e,this.callback=t,this.subscribe=n}};export{e as ContextRequestEvent};
|
|
2
|
-
//# sourceMappingURL=
|
|
2
|
+
//# sourceMappingURL=context-request-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-request-event.js","names":["ContextRequestEvent","context","callback","subscribe"],"sources":["../src/context-request-event.ts"],"sourcesContent":["import type { LoadingCallback } from \"#root/types.js\";\n\n/**\n * Follows the community Context Protocol (@see\n * https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md)\n */\nexport class ContextRequestEvent extends Event {\n public readonly context: unknown;\n public readonly callback: LoadingCallback;\n public readonly subscribe: boolean;\n\n public constructor(\n context: unknown,\n callback: LoadingCallback,\n subscribe = false,\n ) {\n /** Composed, so skeletons inside shadow roots can reach a provider outside */\n super(\"context-request\", { bubbles: true, composed: true });\n this.context = context;\n this.callback = callback;\n this.subscribe = subscribe;\n }\n}\n"],"mappings":"AAMA,IAAaA,EAAb,cAAyC,KAAM,CAC7C,QACA,SACA,UAEA,YACE,EACA,EACA,EAAY,GACZ,CAEA,MAAM,kBAAmB,CAAE,QAAS,GAAM,SAAU,EAAK,CAAC,EAC1D,KAAK,QAAUC,EACf,KAAK,SAAWC,EAChB,KAAK,UAAYC,CACnB,CACF,E"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { COMPONENT_NAME, PROVIDER_NAME } from "./constants.js";
|
|
2
|
-
import { Skeleton } from "./
|
|
3
|
-
import { SkeletonProvider } from "./
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
//#endregion
|
|
7
|
-
export { COMPONENT_NAME, PROVIDER_NAME, Skeleton, SkeletonProvider };
|
|
2
|
+
import { Skeleton } from "./skeleton.js";
|
|
3
|
+
import { SkeletonProvider } from "./skeleton-provider.js";
|
|
4
|
+
declare function init(): void;
|
|
5
|
+
export { COMPONENT_NAME, PROVIDER_NAME, Skeleton, SkeletonProvider, init };
|
|
8
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{COMPONENT_NAME as e,PROVIDER_NAME as t}from"./constants.js";import{
|
|
1
|
+
import{COMPONENT_NAME as e,PROVIDER_NAME as t}from"./constants.js";import{SkeletonProvider as n}from"./skeleton-provider.js";import{Skeleton as r}from"./skeleton.js";function i(){customElements.get(`nano-skeleton-provider`)||customElements.define(t,n),customElements.get(`nano-skeleton`)||customElements.define(e,r)}export{e as COMPONENT_NAME,t as PROVIDER_NAME,r as Skeleton,n as SkeletonProvider,i as init};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["init","PROVIDER_NAME","SkeletonProvider","COMPONENT_NAME","Skeleton"],"sources":["../src/index.ts"],"sourcesContent":["import { PROVIDER_NAME, COMPONENT_NAME } from \"#root/constants.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"index.js","names":["init","PROVIDER_NAME","SkeletonProvider","COMPONENT_NAME","Skeleton"],"sources":["../src/index.ts"],"sourcesContent":["import { PROVIDER_NAME, COMPONENT_NAME } from \"#root/constants.js\";\nimport { SkeletonProvider } from \"#root/skeleton-provider.js\";\nimport { Skeleton } from \"#root/skeleton.js\";\n\nfunction init() {\n if (!customElements.get(PROVIDER_NAME)) {\n customElements.define(PROVIDER_NAME, SkeletonProvider);\n }\n if (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, Skeleton);\n }\n}\n\nexport { PROVIDER_NAME, COMPONENT_NAME } from \"#root/constants.js\";\nexport { Skeleton } from \"#root/skeleton.js\";\nexport { SkeletonProvider } from \"#root/skeleton-provider.js\";\nexport { init };\n"],"mappings":"A,sKAIA,SAASA,GAAO,CACT,eAAe,IAAA,wBAAiB,GACnC,eAAe,OAAOC,EAAeC,CAAgB,EAElD,eAAe,IAAA,eAAkB,GACpC,eAAe,OAAOC,EAAgBC,CAAQ,CAElD,C"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
//#region src/SkeletonProvider.d.ts
|
|
2
1
|
export declare class SkeletonProvider extends HTMLElement {
|
|
3
2
|
#private;
|
|
4
|
-
static observedAttributes:
|
|
3
|
+
static observedAttributes: readonly ["loading"];
|
|
5
4
|
constructor();
|
|
6
5
|
get loading(): boolean;
|
|
7
6
|
set loading(value: boolean);
|
|
8
7
|
attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
|
|
9
8
|
}
|
|
10
|
-
//#
|
|
11
|
-
//# sourceMappingURL=SkeletonProvider.d.ts.map
|
|
9
|
+
//# sourceMappingURL=skeleton-provider.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SKELETON_CONTEXT as e}from"./constants.js";var t=class extends HTMLElement{static observedAttributes=[`loading`];#e=/* @__PURE__ */ new Set;#t=this.attachInternals();constructor(){super(),this.addEventListener(`context-request`,t=>{if(t.context!==e||typeof t.callback!=`function`)return;t.stopPropagation();let{callback:n}=t;t.subscribe?(this.#e.add(n),n(this.loading,()=>this.#e.delete(n))):n(this.loading)})}get loading(){return this.hasAttribute(`loading`)}set loading(e){this.toggleAttribute(`loading
|
|
2
|
-
//# sourceMappingURL=
|
|
1
|
+
import{SKELETON_CONTEXT as e}from"./constants.js";var t=class extends HTMLElement{static observedAttributes=[`loading`];#e=/* @__PURE__ */ new Set;#t=this.attachInternals();constructor(){super(),this.addEventListener(`context-request`,t=>{if(t.context!==e||typeof t.callback!=`function`)return;t.stopPropagation();let{callback:n}=t;t.subscribe?(this.#e.add(n),n(this.loading,()=>{this.#e.delete(n)})):n(this.loading)})}get loading(){return this.hasAttribute(`loading`)}set loading(e){this.toggleAttribute(`loading`,e)}attributeChangedCallback(e,t,n){if(t!==n){this.#t.ariaBusy=this.loading?`true`:null;for(let e of this.#e)e(this.loading)}}};export{t as SkeletonProvider};
|
|
2
|
+
//# sourceMappingURL=skeleton-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skeleton-provider.js","names":["SkeletonProvider","event","SKELETON_CONTEXT","subscriber","#subscribers","value","oldValue","newValue","#internals"],"sources":["../src/skeleton-provider.ts"],"sourcesContent":["import type { ContextRequestEvent } from \"#root/context-request-event.js\";\nimport type { LoadingCallback } from \"#root/types.js\";\n\nimport { SKELETON_CONTEXT } from \"#root/constants.js\";\n\nexport class SkeletonProvider extends HTMLElement {\n public static observedAttributes = [\"loading\"] as const;\n\n readonly #subscribers = new Set<LoadingCallback>();\n readonly #internals = this.attachInternals();\n\n public constructor() {\n super();\n /**\n * Adding listener from the constructor so the provider is ready before any\n * descendant connects and asks for context.\n */\n this.addEventListener(\n \"context-request\",\n (event: Readonly<ContextRequestEvent>) => {\n if (\n event.context !== SKELETON_CONTEXT ||\n typeof event.callback !== \"function\"\n ) {\n return;\n }\n\n /** The nearest provider wins */\n event.stopPropagation();\n\n const { callback: subscriber } = event;\n\n if (event.subscribe) {\n this.#subscribers.add(subscriber);\n subscriber(this.loading, () => {\n this.#subscribers.delete(subscriber);\n });\n } else {\n subscriber(this.loading);\n }\n },\n );\n }\n\n public get loading() {\n return this.hasAttribute(\"loading\");\n }\n\n public set loading(value: boolean) {\n this.toggleAttribute(\"loading\", value);\n }\n\n public attributeChangedCallback(\n _name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) {\n return;\n }\n /**\n * Via internals, so an aria-busy attribute set by the user still takes\n * precedence.\n */\n // oxlint-disable-next-line unicorn/no-null -- ariaBusy accepts either string or null\n this.#internals.ariaBusy = this.loading ? \"true\" : null;\n for (const subscriber of this.#subscribers) {\n subscriber(this.loading);\n }\n }\n}\n"],"mappings":"A,kDAKA,IAAaA,EAAb,cAAsC,WAAY,CAChD,OAAc,mBAAqB,CAAC,SAAS,EAE7C,mBAAwB,IAAI,IAC5B,GAAsB,KAAK,gBAAgB,EAE3C,aAAqB,CACnB,MAAM,EAKN,KAAK,iBACH,kBACC,GAAyC,CACxC,GACEC,EAAM,UAAYC,GAClB,OAAOD,EAAM,UAAa,WAE1B,OAIFA,EAAM,gBAAgB,EAEtB,GAAM,CAAE,SAAUE,GAAeF,EAE7BA,EAAM,WACR,KAAKG,GAAa,IAAID,CAAU,EAChCA,EAAW,KAAK,YAAe,CAC7B,KAAKC,GAAa,OAAOD,CAAU,CACrC,CAAC,GAEDA,EAAW,KAAK,OAAO,CAE3B,CACF,CACF,CAEA,IAAW,SAAU,CACnB,OAAO,KAAK,aAAa,SAAS,CACpC,CAEA,IAAW,QAAQ,EAAgB,CACjC,KAAK,gBAAgB,UAAWE,CAAK,CACvC,CAEA,yBACE,EACA,EACA,EACA,CACA,GAAIC,IAAaC,EAQjB,MAAKC,GAAW,SAAW,KAAK,QAAU,OAAS,KACnD,IAAK,IAAML,KAAc,KAAKC,GAC5BD,EAAW,KAAK,OAAO,CAF0B,CAIrD,CACF,E"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
//#region src/Skeleton.d.ts
|
|
2
1
|
export declare class Skeleton extends HTMLElement {
|
|
3
2
|
#private;
|
|
4
|
-
static observedAttributes: string[];
|
|
3
|
+
static readonly observedAttributes: string[];
|
|
4
|
+
get loading(): boolean;
|
|
5
|
+
set loading(value: boolean);
|
|
5
6
|
/** Keeps the content focusable and clickable while loading */
|
|
6
7
|
get interactive(): boolean;
|
|
7
8
|
set interactive(value: boolean);
|
|
8
9
|
/** Forces text mode, even when the text is wrapped in elements */
|
|
9
|
-
get
|
|
10
|
-
set
|
|
10
|
+
get inline(): boolean;
|
|
11
|
+
set inline(value: boolean);
|
|
11
12
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
12
|
-
connectedCallback(): void
|
|
13
|
+
connectedCallback(): Promise<void>;
|
|
13
14
|
disconnectedCallback(): void;
|
|
14
15
|
}
|
|
15
|
-
//#
|
|
16
|
-
//# sourceMappingURL=Skeleton.d.ts.map
|
|
16
|
+
//# sourceMappingURL=skeleton.d.ts.map
|
package/dist/skeleton.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{SKELETON_CONTEXT as e}from"./constants.js";import{ContextRequestEvent as t}from"./context-request-event.js";import{adoptStyles as n}from"./styles.js";function r(e){return`adoptedStyleSheets`in e}var i=class extends HTMLElement{static observedAttributes=[`interactive`,`inline`];#e;#t=new MutationObserver(()=>{this.#n()});get loading(){return this.hasAttribute(`loading`)}set loading(e){this.toggleAttribute(`loading`,e)}get interactive(){return this.hasAttribute(`interactive`)}set interactive(e){this.toggleAttribute(`interactive`,e)}get inline(){return this.hasAttribute(`inline`)}set inline(e){this.toggleAttribute(`inline`,e)}attributeChangedCallback(e,t,n){t!==n&&(e===`inline`?this.#n():this.#r(this.loading))}async connectedCallback(){let i=this.getRootNode();r(i)&&await n(i),this.#n(),this.#r(!1),this.dispatchEvent(new t(e,(e,t)=>{t&&(this.#e=t),this.#r(e)},!0)),this.#t.observe(this,{childList:!0,characterData:!0,subtree:!0})}disconnectedCallback(){this.#e?.(),this.#e=void 0,this.#t.disconnect()}#n(){let e=this.inline;for(let t of this.childNodes){if(e)break;t instanceof Text&&t.data.trim()!==``&&(e=!0)}this.toggleAttribute(`inline`,e)}#r(e){this.toggleAttribute(`loading`,e),this.toggleAttribute(`inert`,e&&!this.interactive)}};export{i as Skeleton};
|
|
2
|
+
//# sourceMappingURL=skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skeleton.js","names":["isDocumentOrShadowRoot","node","Skeleton","#update","value","oldValue","newValue","name","#render","root","adoptStyles","ContextRequestEvent","SKELETON_CONTEXT","unsubscribe","#unsubscribe","loading","#observer","isInline"],"sources":["../src/skeleton.ts"],"sourcesContent":["import type { Unsubscribe } from \"#root/types.js\";\n\nimport { SKELETON_CONTEXT } from \"#root/constants.js\";\nimport { ContextRequestEvent } from \"#root/context-request-event.js\";\nimport { adoptStyles } from \"#root/styles.js\";\n\nfunction isDocumentOrShadowRoot(\n node: Node | Document | ShadowRoot,\n): node is Document | ShadowRoot {\n return \"adoptedStyleSheets\" in node;\n}\n\nexport class Skeleton extends HTMLElement {\n public static readonly observedAttributes = [\"interactive\", \"inline\"];\n\n #unsubscribe?: Unsubscribe | undefined;\n\n readonly #observer = new MutationObserver(() => {\n this.#update();\n });\n\n public get loading() {\n return this.hasAttribute(\"loading\");\n }\n\n public set loading(value: boolean) {\n this.toggleAttribute(\"loading\", value);\n }\n\n /** Keeps the content focusable and clickable while loading */\n public get interactive() {\n return this.hasAttribute(\"interactive\");\n }\n\n public set interactive(value: boolean) {\n this.toggleAttribute(\"interactive\", value);\n }\n\n /** Forces text mode, even when the text is wrapped in elements */\n public get inline() {\n return this.hasAttribute(\"inline\");\n }\n\n public set inline(value: boolean) {\n this.toggleAttribute(\"inline\", value);\n }\n\n public attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) {\n return;\n }\n if (name === \"inline\") {\n this.#update();\n } else {\n this.#render(this.loading);\n }\n }\n\n public async connectedCallback() {\n const root = this.getRootNode();\n\n if (isDocumentOrShadowRoot(root)) {\n await adoptStyles(root);\n }\n\n this.#update();\n this.#render(false);\n\n this.dispatchEvent(\n new ContextRequestEvent(\n SKELETON_CONTEXT,\n (loading, unsubscribe) => {\n if (unsubscribe) {\n this.#unsubscribe = unsubscribe;\n }\n this.#render(loading);\n },\n true,\n ),\n );\n\n this.#observer.observe(this, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n }\n\n public disconnectedCallback() {\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n this.#observer.disconnect();\n }\n\n #update() {\n let isInline = this.inline;\n for (const node of this.childNodes) {\n if (isInline) {\n break;\n }\n if (node instanceof Text && node.data.trim() !== \"\") {\n isInline = true;\n }\n }\n this.toggleAttribute(\"inline\", isInline);\n }\n\n #render(loading: boolean) {\n this.toggleAttribute(\"loading\", loading);\n this.toggleAttribute(\"inert\", loading && !this.interactive);\n }\n}\n"],"mappings":"A,6JAMA,SAASA,EACP,EAC+B,CAC/B,MAAO,uBAAwBC,CACjC,CAEA,IAAaC,EAAb,cAA8B,WAAY,CACxC,OAAuB,mBAAqB,CAAC,cAAe,QAAQ,EAEpE,GAEA,GAAqB,IAAI,qBAAuB,CAC9C,KAAKC,GAAQ,CACf,CAAC,EAED,IAAW,SAAU,CACnB,OAAO,KAAK,aAAa,SAAS,CACpC,CAEA,IAAW,QAAQ,EAAgB,CACjC,KAAK,gBAAgB,UAAWC,CAAK,CACvC,CAGA,IAAW,aAAc,CACvB,OAAO,KAAK,aAAa,aAAa,CACxC,CAEA,IAAW,YAAY,EAAgB,CACrC,KAAK,gBAAgB,cAAeA,CAAK,CAC3C,CAGA,IAAW,QAAS,CAClB,OAAO,KAAK,aAAa,QAAQ,CACnC,CAEA,IAAW,OAAO,EAAgB,CAChC,KAAK,gBAAgB,SAAUA,CAAK,CACtC,CAEA,yBACE,EACA,EACA,EACA,CACIC,IAAaC,IAGbC,IAAS,SACX,KAAKJ,GAAQ,EAEb,KAAKK,GAAQ,KAAK,OAAO,EAE7B,CAEA,MAAa,mBAAoB,CAC/B,IAAMC,EAAO,KAAK,YAAY,EAE1BT,EAAuBS,CAAI,GAC7B,MAAMC,EAAYD,CAAI,EAGxB,KAAKN,GAAQ,EACb,KAAKK,GAAQ,EAAK,EAElB,KAAK,cACH,IAAIG,EACFC,GACC,EAAS,IAAgB,CACpBC,IACF,KAAKC,GAAeD,GAEtB,KAAKL,GAAQO,CAAO,CACtB,EACA,EACF,CACF,EAEA,KAAKC,GAAU,QAAQ,KAAM,CAC3B,UAAW,GACX,cAAe,GACf,QAAS,EACX,CAAC,CACH,CAEA,sBAA8B,CAC5B,KAAKF,KAAe,EACpB,KAAKA,GAAe,IAAA,GACpB,KAAKE,GAAU,WAAW,CAC5B,CAEA,IAAU,CACR,IAAIC,EAAW,KAAK,OACpB,IAAK,IAAMhB,KAAQ,KAAK,WAAY,CAClC,GAAIgB,EACF,MAEEhB,aAAgB,MAAQA,EAAK,KAAK,KAAK,IAAM,KAC/C,EAAW,GAEf,CACA,KAAK,gBAAgB,SAAUgB,CAAQ,CACzC,CAEA,GAAQ,EAAkB,CACxB,KAAK,gBAAgB,UAAWF,CAAO,EACvC,KAAK,gBAAgB,QAASA,GAAW,CAAC,KAAK,WAAW,CAC5D,CACF,E"}
|
package/dist/styles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import{COMPONENT_NAME as e,CSS_CONTENT as t}from"./constants.js";const n=`@scope (${e}) {
|
|
2
2
|
${t}
|
|
3
|
-
}
|
|
3
|
+
}`,r={};async function i(e){r.sheet||(r.sheet=new CSSStyleSheet,await r.sheet.replace(n));let{sheet:t}=r;e.adoptedStyleSheets.includes(t)||(e.adoptedStyleSheets=[...e.adoptedStyleSheets,t])}export{i as adoptStyles};
|
|
4
4
|
//# sourceMappingURL=styles.js.map
|
package/dist/styles.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","names":["css","COMPONENT_NAME","CSS_CONTENT","
|
|
1
|
+
{"version":3,"file":"styles.js","names":["css","COMPONENT_NAME","CSS_CONTENT","cache","adoptStyles","root","sheet"],"sources":["../src/styles.ts"],"sourcesContent":["import { CSS_CONTENT, COMPONENT_NAME } from \"#root/constants.js\";\n\n/** CSS_CONTENT get replaced with styles.css content during build. */\nconst css = `@scope (${COMPONENT_NAME}) {\n ${CSS_CONTENT}\n}`;\n\nconst cache: { sheet?: CSSStyleSheet } = {};\n\nexport async function adoptStyles(root: Document | ShadowRoot) {\n if (!cache.sheet) {\n cache.sheet = new CSSStyleSheet();\n await cache.sheet.replace(css);\n }\n const { sheet } = cache;\n if (!root.adoptedStyleSheets.includes(sheet)) {\n root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];\n }\n}\n"],"mappings":"A,iEAGA,MAAMA,EAAM,WAAWC,EAAe;IAClCC,EAAY;GAGVC,EAAmC,CAAC,EAE1C,eAAsBC,EAAY,EAA6B,CACxDD,EAAM,QACT,EAAM,MAAQ,IAAI,cAClB,MAAMA,EAAM,MAAM,QAAQH,CAAG,GAE/B,GAAM,CAAE,SAAUG,EACbE,EAAK,mBAAmB,SAASC,CAAK,IACzC,EAAK,mBAAqB,CAAC,GAAGD,EAAK,mBAAoBC,CAAK,EAEhE,C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nano-skeleton",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "The perfect skeleton component",
|
|
5
5
|
"homepage": "https://smashercosmo.github.io/nano-skeleton/",
|
|
6
6
|
"bugs": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@internal/tsconfig": "0.0.0",
|
|
25
|
-
"@tsdown/css": "0.23.
|
|
26
|
-
"@types/node": "26.6.
|
|
25
|
+
"@tsdown/css": "0.23.4",
|
|
26
|
+
"@types/node": "26.6.6",
|
|
27
27
|
"bumpp": "12.3.0",
|
|
28
|
-
"rolldown": "1.2.
|
|
29
|
-
"tsdown": "0.23.
|
|
28
|
+
"rolldown": "1.2.16",
|
|
29
|
+
"tsdown": "0.23.4"
|
|
30
30
|
},
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"exports": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ContextRequestEvent.js","names":["ContextRequestEvent","context","callback","subscribe"],"sources":["../src/ContextRequestEvent.ts"],"sourcesContent":["import type { LoadingCallback } from \"#root/types.js\";\n\n/**\n * Follows the community Context Protocol (@see\n * https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md)\n */\nexport class ContextRequestEvent extends Event {\n readonly context: unknown;\n readonly callback: LoadingCallback;\n readonly subscribe: boolean;\n\n constructor(context: unknown, callback: LoadingCallback, subscribe = false) {\n /** Composed, so skeletons inside shadow roots can reach a provider outside */\n super(\"context-request\", { bubbles: true, composed: true });\n this.context = context;\n this.callback = callback;\n this.subscribe = subscribe;\n }\n}\n"],"mappings":"AAMA,IAAaA,EAAb,cAAyC,KAAM,CAC7C,QACA,SACA,UAEA,YAAY,EAAkB,EAA2B,EAAY,GAAO,CAE1E,MAAM,kBAAmB,CAAE,QAAS,GAAM,SAAU,EAAK,CAAC,EAC1D,KAAK,QAAUC,EACf,KAAK,SAAWC,EAChB,KAAK,UAAYC,CACnB,CACF,E"}
|
package/dist/Skeleton.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{SKELETON_CONTEXT as e}from"./constants.js";import{ContextRequestEvent as t}from"./ContextRequestEvent.js";import{adoptStyles as n}from"./styles.js";function r(e){return`adoptedStyleSheets`in e}var i=class extends HTMLElement{static observedAttributes=[`interactive`,`text`];#e=!1;#t;#n=new MutationObserver(()=>this.#r());get interactive(){return this.hasAttribute(`interactive`)}set interactive(e){this.toggleAttribute(`interactive`,!!e)}get text(){return this.hasAttribute(`text`)}set text(e){this.toggleAttribute(`text`,!!e)}attributeChangedCallback(e,t,n){t!==n&&(e===`text`?this.#r():this.#i(this.#e))}connectedCallback(){let i=this.getRootNode();r(i)&&n(i),this.#r(),this.#i(!1),this.dispatchEvent(new t(e,(e,t)=>{t&&(this.#t=t),this.#i(e)},!0)),this.#n.observe(this,{childList:!0,characterData:!0,subtree:!0})}disconnectedCallback(){this.#t?.(),this.#t=void 0,this.#n.disconnect()}#r(){let e=this.text;for(let t of this.childNodes){if(e)break;t instanceof Text&&t.data.trim()!==``&&(e=!0)}this.toggleAttribute(`data-text`,e)}#i(e){this.#e=e,this.toggleAttribute(`inert`,e&&!this.interactive),this.toggleAttribute(`data-loading`,e)}};export{i as Skeleton};
|
|
2
|
-
//# sourceMappingURL=Skeleton.js.map
|
package/dist/Skeleton.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.js","names":["isDocumentOrShadowRoot","node","Skeleton","#update","value","oldValue","newValue","name","#render","#loading","root","adoptStyles","ContextRequestEvent","SKELETON_CONTEXT","unsubscribe","#unsubscribe","loading","#observer","isText"],"sources":["../src/Skeleton.ts"],"sourcesContent":["import { SKELETON_CONTEXT } from \"#root/constants.js\";\nimport { ContextRequestEvent } from \"#root/ContextRequestEvent.js\";\nimport { adoptStyles } from \"#root/styles.js\";\nimport { type Unsubscribe } from \"#root/types.js\";\n\nfunction isDocumentOrShadowRoot(\n node: Node | Document | ShadowRoot,\n): node is Document | ShadowRoot {\n return \"adoptedStyleSheets\" in node;\n}\n\nexport class Skeleton extends HTMLElement {\n static observedAttributes = [\"interactive\", \"text\"];\n\n #loading = false;\n #unsubscribe?: Unsubscribe | undefined;\n #observer = new MutationObserver(() => this.#update());\n\n /** Keeps the content focusable and clickable while loading */\n get interactive() {\n return this.hasAttribute(\"interactive\");\n }\n\n set interactive(value: boolean) {\n this.toggleAttribute(\"interactive\", Boolean(value));\n }\n\n /** Forces text mode, even when the text is wrapped in elements */\n get text() {\n return this.hasAttribute(\"text\");\n }\n\n set text(value: boolean) {\n this.toggleAttribute(\"text\", Boolean(value));\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) return;\n if (name === \"text\") this.#update();\n else this.#render(this.#loading);\n }\n\n connectedCallback() {\n const root = this.getRootNode();\n\n if (isDocumentOrShadowRoot(root)) {\n adoptStyles(root);\n }\n\n this.#update();\n this.#render(false);\n\n this.dispatchEvent(\n new ContextRequestEvent(\n SKELETON_CONTEXT,\n (loading, unsubscribe) => {\n if (unsubscribe) this.#unsubscribe = unsubscribe;\n this.#render(loading);\n },\n true,\n ),\n );\n\n this.#observer.observe(this, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n }\n\n disconnectedCallback() {\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n this.#observer.disconnect();\n }\n\n #update() {\n let isText = this.text;\n for (const node of this.childNodes) {\n if (isText) break;\n if (node instanceof Text && node.data.trim() !== \"\") {\n isText = true;\n }\n }\n this.toggleAttribute(\"data-text\", isText);\n }\n\n #render(loading: boolean) {\n this.#loading = loading;\n this.toggleAttribute(\"inert\", loading && !this.interactive);\n this.toggleAttribute(\"data-loading\", loading);\n }\n}\n"],"mappings":"A,2JAKA,SAASA,EACP,EAC+B,CAC/B,MAAO,uBAAwBC,CACjC,CAEA,IAAaC,EAAb,cAA8B,WAAY,CACxC,OAAO,mBAAqB,CAAC,cAAe,MAAM,EAElD,GAAW,GACX,GACA,GAAY,IAAI,qBAAuB,KAAKC,GAAQ,CAAC,EAGrD,IAAI,aAAc,CAChB,OAAO,KAAK,aAAa,aAAa,CACxC,CAEA,IAAI,YAAY,EAAgB,CAC9B,KAAK,gBAAgB,cAAe,EAAQC,CAAM,CACpD,CAGA,IAAI,MAAO,CACT,OAAO,KAAK,aAAa,MAAM,CACjC,CAEA,IAAI,KAAK,EAAgB,CACvB,KAAK,gBAAgB,OAAQ,EAAQA,CAAM,CAC7C,CAEA,yBACE,EACA,EACA,EACA,CACIC,IAAaC,IACbC,IAAS,OAAQ,KAAKJ,GAAQ,EAC7B,KAAKK,GAAQ,KAAKC,EAAQ,EACjC,CAEA,mBAAoB,CAClB,IAAMC,EAAO,KAAK,YAAY,EAE1BV,EAAuBU,CAAI,GAC7BC,EAAYD,CAAI,EAGlB,KAAKP,GAAQ,EACb,KAAKK,GAAQ,EAAK,EAElB,KAAK,cACH,IAAII,EACFC,GACC,EAAS,IAAgB,CACpBC,IAAa,KAAKC,GAAeD,GACrC,KAAKN,GAAQQ,CAAO,CACtB,EACA,EACF,CACF,EAEA,KAAKC,GAAU,QAAQ,KAAM,CAC3B,UAAW,GACX,cAAe,GACf,QAAS,EACX,CAAC,CACH,CAEA,sBAAuB,CACrB,KAAKF,KAAe,EACpB,KAAKA,GAAe,IAAA,GACpB,KAAKE,GAAU,WAAW,CAC5B,CAEA,IAAU,CACR,IAAIC,EAAS,KAAK,KAClB,IAAK,IAAMjB,KAAQ,KAAK,WAAY,CAClC,GAAIiB,EAAQ,MACRjB,aAAgB,MAAQA,EAAK,KAAK,KAAK,IAAM,KAC/C,EAAS,GAEb,CACA,KAAK,gBAAgB,YAAaiB,CAAM,CAC1C,CAEA,GAAQ,EAAkB,CACxB,KAAKT,GAAWO,EAChB,KAAK,gBAAgB,QAASA,GAAW,CAAC,KAAK,WAAW,EAC1D,KAAK,gBAAgB,eAAgBA,CAAO,CAC9C,CACF,E"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SkeletonProvider.js","names":["SkeletonProvider","event","SKELETON_CONTEXT","#subscribers","callback","value","oldValue","newValue","#internals"],"sources":["../src/SkeletonProvider.ts"],"sourcesContent":["import type { ContextRequestEvent } from \"#root/ContextRequestEvent.js\";\nimport type { LoadingCallback } from \"#root/types.js\";\n\nimport { SKELETON_CONTEXT } from \"#root/constants.js\";\n\nexport class SkeletonProvider extends HTMLElement {\n static observedAttributes = [\"loading\"];\n\n #subscribers = new Set<LoadingCallback>();\n #internals = this.attachInternals();\n\n constructor() {\n super();\n /**\n * Adding listener from the constructor so the provider is ready before any\n * descendant connects and asks for context.\n */\n this.addEventListener(\"context-request\", (event: ContextRequestEvent) => {\n if (\n event.context !== SKELETON_CONTEXT ||\n typeof event.callback !== \"function\"\n )\n return;\n\n /** The nearest provider wins */\n event.stopPropagation();\n\n const { callback } = event;\n\n if (event.subscribe) {\n this.#subscribers.add(callback);\n callback(this.loading, () => this.#subscribers.delete(callback));\n } else {\n callback(this.loading);\n }\n });\n }\n\n get loading() {\n return this.hasAttribute(\"loading\");\n }\n\n set loading(value: boolean) {\n this.toggleAttribute(\"loading\", Boolean(value));\n }\n\n attributeChangedCallback(\n _name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) return;\n /**\n * Via internals, so an aria-busy attribute set by the user still takes\n * precedence.\n */\n this.#internals.ariaBusy = this.loading ? \"true\" : null;\n for (const callback of this.#subscribers) callback(this.loading);\n }\n}\n"],"mappings":"A,kDAKA,IAAaA,EAAb,cAAsC,WAAY,CAChD,OAAO,mBAAqB,CAAC,SAAS,EAEtC,mBAAe,IAAI,IACnB,GAAa,KAAK,gBAAgB,EAElC,aAAc,CACZ,MAAM,EAKN,KAAK,iBAAiB,kBAAoB,GAA+B,CACvE,GACEC,EAAM,UAAYC,GAClB,OAAOD,EAAM,UAAa,WAE1B,OAGFA,EAAM,gBAAgB,EAEtB,GAAM,CAAE,YAAaA,EAEjBA,EAAM,WACR,KAAKE,GAAa,IAAIC,CAAQ,EAC9BA,EAAS,KAAK,YAAe,KAAKD,GAAa,OAAOC,CAAQ,CAAC,GAE/DA,EAAS,KAAK,OAAO,CAEzB,CAAC,CACH,CAEA,IAAI,SAAU,CACZ,OAAO,KAAK,aAAa,SAAS,CACpC,CAEA,IAAI,QAAQ,EAAgB,CAC1B,KAAK,gBAAgB,UAAW,EAAQC,CAAM,CAChD,CAEA,yBACE,EACA,EACA,EACA,CACA,GAAIC,IAAaC,EAKjB,MAAKC,GAAW,SAAW,KAAK,QAAU,OAAS,KACnD,IAAK,IAAMJ,KAAY,KAAKD,GAAcC,EAAS,KAAK,OAAO,CADZ,CAErD,CACF,E"}
|