nano-skeleton 0.5.1 → 0.6.0

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - refactor: replace `init()` with static `define()` methods
8
+
9
+ BREAKING CHANGES:
10
+
11
+ - `init()` is no more, use `SkeletonProvider.define()` and `Skeleton.define()` instead
12
+
3
13
  ## 0.5.1
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -54,16 +54,18 @@ yarn add nano-skeleton
54
54
 
55
55
  ## Quick start
56
56
 
57
- Call `init()` once to register the custom elements:
57
+ Register the custom elements:
58
58
 
59
59
  ```js
60
- import { init } from "nano-skeleton";
60
+ import { Skeleton, SkeletonProvider } from "nano-skeleton";
61
61
 
62
- init();
62
+ SkeletonProvider.define();
63
+ Skeleton.define();
63
64
  ```
64
65
 
65
- `init()` is idempotent, so calling it more than once (for example from several
66
- entry points or during hot reload) is safe.
66
+ Each `define()` does nothing if the element is already registered, so calling
67
+ it more than once (for example from several entry points or during hot reload)
68
+ is safe.
67
69
 
68
70
  Then use the elements in your markup:
69
71
 
@@ -305,9 +307,10 @@ directly:
305
307
 
306
308
  ```tsx
307
309
  import { useQuery } from "@tanstack/react-query";
308
- import { init } from "nano-skeleton";
310
+ import { Skeleton, SkeletonProvider } from "nano-skeleton";
309
311
 
310
- init();
312
+ SkeletonProvider.define();
313
+ Skeleton.define();
311
314
 
312
315
  export function UserCard({ id }: { id: string }) {
313
316
  const { data, isPending } = useQuery({
@@ -374,10 +377,15 @@ declare module "react" {
374
377
 
375
378
  ## API
376
379
 
377
- ### `init()`
380
+ ### `SkeletonProvider.define()`
378
381
 
379
- Registers `<nano-skeleton-provider>` and `<nano-skeleton>` with
380
- `customElements`. Safe to call multiple times.
382
+ Registers `<nano-skeleton-provider>` with `customElements`. Does nothing if the
383
+ element is already registered, so it's safe to call multiple times.
384
+
385
+ ### `Skeleton.define()`
386
+
387
+ Registers `<nano-skeleton>` with `customElements`. Does nothing if the element
388
+ is already registered, so it's safe to call multiple times.
381
389
 
382
390
  ### `<nano-skeleton-provider>`
383
391
 
@@ -390,10 +398,10 @@ While loading, the provider is exposed to assistive technology as
390
398
 
391
399
  ### `<nano-skeleton>`
392
400
 
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). |
401
+ | Attribute / property | Type | Description |
402
+ | -------------------- | --------- | ------------------------------------------------------------------------------------------------------------- |
403
+ | `interactive` | `boolean` | Keeps the content focusable and clickable while loading. See [Interactive skeletons](#interactive-skeletons). |
404
+ | `inline` | `boolean` | Forces inline mode, e.g. for text inside a link. See [Text wrapped in elements](#text-wrapped-in-elements). |
397
405
 
398
406
  The component also manages these attributes itself, which you can use as
399
407
  styling hooks:
@@ -408,7 +416,6 @@ styling hooks:
408
416
 
409
417
  ```ts
410
418
  import {
411
- init,
412
419
  Skeleton, // <nano-skeleton> class
413
420
  SkeletonProvider, // <nano-skeleton-provider> class
414
421
  COMPONENT_NAME, // "nano-skeleton"
@@ -420,11 +427,11 @@ import {
420
427
 
421
428
  The look is controlled by CSS custom properties:
422
429
 
423
- | Property | Default |
424
- | ------------------------------------ | -------------------------------------- |
425
- | `--nano-skeleton-background-image` | A light/dark-aware horizontal gradient |
426
- | `--nano-skeleton-background-size` | `300%` |
427
- | `--nano-skeleton-animation` | `shimmer 2s infinite ease-in-out` |
430
+ | Property | Default |
431
+ | ---------------------------------- | -------------------------------------- |
432
+ | `--nano-skeleton-background-image` | A light/dark-aware horizontal gradient |
433
+ | `--nano-skeleton-background-size` | `300%` |
434
+ | `--nano-skeleton-animation` | `shimmer 2s infinite ease-in-out` |
428
435
 
429
436
  Each `<nano-skeleton>` inherits these from its ancestors, so set them on a
430
437
  parent element (or on `:root` to change all skeletons at once), not on the
package/dist/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { COMPONENT_NAME, PROVIDER_NAME } from "./constants.js";
2
2
  import { Skeleton } from "./skeleton.js";
3
3
  import { SkeletonProvider } from "./skeleton-provider.js";
4
- declare function init(): void;
5
- export { COMPONENT_NAME, PROVIDER_NAME, Skeleton, SkeletonProvider, init };
6
- //# sourceMappingURL=index.d.ts.map
4
+ export { COMPONENT_NAME, PROVIDER_NAME, Skeleton, SkeletonProvider };
package/dist/index.js CHANGED
@@ -1,2 +1 @@
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
- //# sourceMappingURL=index.js.map
1
+ import{COMPONENT_NAME as e,PROVIDER_NAME as t}from"./constants.js";import{Skeleton as n}from"./skeleton.js";import{SkeletonProvider as r}from"./skeleton-provider.js";export{e as COMPONENT_NAME,t as PROVIDER_NAME,n as Skeleton,r as SkeletonProvider};
@@ -1,6 +1,7 @@
1
1
  export declare class SkeletonProvider extends HTMLElement {
2
2
  #private;
3
- static observedAttributes: readonly ["loading"];
3
+ static readonly observedAttributes: string[];
4
+ static define(): void;
4
5
  constructor();
5
6
  get loading(): boolean;
6
7
  set loading(value: boolean);
@@ -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`,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};
1
+ import{PROVIDER_NAME as e,SKELETON_CONTEXT as t}from"./constants.js";var n=class extends HTMLElement{static observedAttributes=[`loading`];static define(){customElements.get(`nano-skeleton-provider`)||customElements.define(e,this)}#e=/* @__PURE__ */ new Set;#t=this.attachInternals();constructor(){super(),this.addEventListener(`context-request`,e=>{if(e.context!==t||typeof e.callback!=`function`)return;e.stopPropagation();let{callback:n}=e;e.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{n as SkeletonProvider};
2
2
  //# sourceMappingURL=skeleton-provider.js.map
@@ -1 +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
+ {"version":3,"file":"skeleton-provider.js","names":["SkeletonProvider","PROVIDER_NAME","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 { PROVIDER_NAME, SKELETON_CONTEXT } from \"#root/constants.js\";\n\nexport class SkeletonProvider extends HTMLElement {\n public static readonly observedAttributes = [\"loading\"];\n public static define() {\n if (!customElements.get(PROVIDER_NAME)) {\n customElements.define(PROVIDER_NAME, this);\n }\n }\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\n * before any 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 /** Setting `aria-busy` Via internals so it can be overridden by user */\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,qEAKA,IAAaA,EAAb,cAAsC,WAAY,CAChD,OAAuB,mBAAqB,CAAC,SAAS,EACtD,OAAc,QAAS,CAChB,eAAe,IAAA,wBAAiB,GACnC,eAAe,OAAOC,EAAe,IAAI,CAE7C,CAEA,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,EAKjB,MAAKC,GAAW,SAAW,KAAK,QAAU,OAAS,KACnD,IAAK,IAAML,KAAc,KAAKC,GAC5BD,EAAW,KAAK,OAAO,CAF0B,CAIrD,CACF,E"}
@@ -1,12 +1,11 @@
1
1
  export declare class Skeleton extends HTMLElement {
2
2
  #private;
3
3
  static readonly observedAttributes: string[];
4
+ static define(): void;
4
5
  get loading(): boolean;
5
6
  set loading(value: boolean);
6
- /** Keeps the content focusable and clickable while loading */
7
7
  get interactive(): boolean;
8
8
  set interactive(value: boolean);
9
- /** Forces text mode, even when the text is wrapped in elements */
10
9
  get inline(): boolean;
11
10
  set inline(value: boolean);
12
11
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
package/dist/skeleton.js CHANGED
@@ -1,2 +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};
1
+ import{COMPONENT_NAME as e,SKELETON_CONTEXT as t}from"./constants.js";import{ContextRequestEvent as n}from"./context-request-event.js";import{adoptStyles as r}from"./styles.js";function i(e){return`adoptedStyleSheets`in e}var a=class extends HTMLElement{static observedAttributes=[`interactive`,`inline`];static define(){customElements.get(`nano-skeleton`)||customElements.define(e,this)}#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 e=this.getRootNode();i(e)&&await r(e),this.#n(),this.#r(!1),this.dispatchEvent(new n(t,(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{a as Skeleton};
2
2
  //# sourceMappingURL=skeleton.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"skeleton.js","names":["isDocumentOrShadowRoot","node","Skeleton","COMPONENT_NAME","#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 { COMPONENT_NAME, 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 public static define() {\n if (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, this);\n }\n }\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 public get interactive() {\n return this.hasAttribute(\"interactive\");\n }\n\n public set interactive(value: boolean) {\n this.toggleAttribute(\"interactive\", value);\n }\n\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 /**\n * Here we auto-determine `inline` mode.\n * Skeleton is considered `inline` if it contains\n * text nodes only.\n */\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,iLAMA,SAASA,EACP,EAC+B,CAC/B,MAAO,uBAAwBC,CACjC,CAEA,IAAaC,EAAb,cAA8B,WAAY,CACxC,OAAuB,mBAAqB,CAAC,cAAe,QAAQ,EACpE,OAAc,QAAS,CAChB,eAAe,IAAA,eAAkB,GACpC,eAAe,OAAOC,EAAgB,IAAI,CAE9C,CAEA,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,CAEA,IAAW,aAAc,CACvB,OAAO,KAAK,aAAa,aAAa,CACxC,CAEA,IAAW,YAAY,EAAgB,CACrC,KAAK,gBAAgB,cAAeA,CAAK,CAC3C,CAEA,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,EAE1BV,EAAuBU,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,OAMpB,IAAK,IAAMjB,KAAQ,KAAK,WAAY,CAClC,GAAIiB,EACF,MAEEjB,aAAgB,MAAQA,EAAK,KAAK,KAAK,IAAM,KAC/C,EAAW,GAEf,CACA,KAAK,gBAAgB,SAAUiB,CAAQ,CACzC,CAEA,GAAQ,EAAkB,CACxB,KAAK,gBAAgB,UAAWF,CAAO,EACvC,KAAK,gBAAgB,QAASA,GAAW,CAAC,KAAK,WAAW,CAC5D,CACF,E"}
@@ -1 +1 @@
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"}
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 constant gets 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.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "The perfect skeleton component",
5
5
  "homepage": "https://smashercosmo.github.io/nano-skeleton/",
6
6
  "bugs": {
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
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"}