@brytdesigns/web-component-core 1.1.7 → 3.1.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.
Files changed (84) hide show
  1. package/dist/main/animation/awaitAllAnimations.js +28 -32
  2. package/dist/main/animation/awaitAllAnimations.js.map +1 -1
  3. package/dist/main/animation/content.js +156 -0
  4. package/dist/main/animation/content.js.map +1 -0
  5. package/dist/main/animation/controlsToPromise.js +6 -5
  6. package/dist/main/animation/controlsToPromise.js.map +1 -1
  7. package/dist/main/animation/getTransitionConfig.js +52 -51
  8. package/dist/main/animation/getTransitionConfig.js.map +1 -1
  9. package/dist/main/animation/index.d.ts +61 -1
  10. package/dist/main/animation/index.js +5 -5
  11. package/dist/main/focusTrap/index.d.ts +1 -1
  12. package/dist/main/focusTrap/index.js +10 -14
  13. package/dist/main/focusTrap/index.js.map +1 -1
  14. package/dist/main/hooks/index.d.ts +14 -9
  15. package/dist/main/hooks/index.js +5 -6
  16. package/dist/main/hooks/internal/getValueFromPath.js +9 -10
  17. package/dist/main/hooks/internal/getValueFromPath.js.map +1 -1
  18. package/dist/main/hooks/makePathContextHooks.js +58 -68
  19. package/dist/main/hooks/makePathContextHooks.js.map +1 -1
  20. package/dist/main/hooks/makePathDataArrayComponent.js +61 -67
  21. package/dist/main/hooks/makePathDataArrayComponent.js.map +1 -1
  22. package/dist/main/hooks/makePathDataArrayItemComponent.js +17 -21
  23. package/dist/main/hooks/makePathDataArrayItemComponent.js.map +1 -1
  24. package/dist/main/hooks/makePathDataComponent.js +31 -33
  25. package/dist/main/hooks/makePathDataComponent.js.map +1 -1
  26. package/dist/main/index.d.ts +304 -27
  27. package/dist/main/index.js +28 -7
  28. package/dist/main/promise/abortablePromise.js +10 -15
  29. package/dist/main/promise/abortablePromise.js.map +1 -1
  30. package/dist/main/promise/index.js +2 -3
  31. package/dist/main/utils/a11y.js +133 -0
  32. package/dist/main/utils/a11y.js.map +1 -0
  33. package/dist/main/utils/body-scroll-lock.js +40 -18
  34. package/dist/main/utils/body-scroll-lock.js.map +1 -1
  35. package/dist/main/utils/checkLoaded.js +4 -4
  36. package/dist/main/utils/checkLoaded.js.map +1 -1
  37. package/dist/main/utils/createRemoteContext.js +85 -0
  38. package/dist/main/utils/createRemoteContext.js.map +1 -0
  39. package/dist/main/utils/disclosure.js +52 -0
  40. package/dist/main/utils/disclosure.js.map +1 -0
  41. package/dist/main/utils/formatValue.js +14 -18
  42. package/dist/main/utils/formatValue.js.map +1 -1
  43. package/dist/main/utils/getContextFromProvider.js +15 -10
  44. package/dist/main/utils/getContextFromProvider.js.map +1 -1
  45. package/dist/main/utils/getTemplateContent.js +6 -7
  46. package/dist/main/utils/getTemplateContent.js.map +1 -1
  47. package/dist/main/utils/guarded-effect.js +72 -0
  48. package/dist/main/utils/guarded-effect.js.map +1 -0
  49. package/dist/main/utils/index.d.ts +237 -20
  50. package/dist/main/utils/index.js +15 -12
  51. package/dist/main/utils/invokeOnLoaded.js +13 -13
  52. package/dist/main/utils/invokeOnLoaded.js.map +1 -1
  53. package/dist/main/utils/isCustomElement.js +4 -3
  54. package/dist/main/utils/isCustomElement.js.map +1 -1
  55. package/dist/main/utils/nested-overlays.js +36 -0
  56. package/dist/main/utils/nested-overlays.js.map +1 -0
  57. package/dist/main/utils/solid-element.js +48 -88
  58. package/dist/main/utils/solid-element.js.map +1 -1
  59. package/dist/main/utils/toHyphenated.js +4 -3
  60. package/dist/main/utils/toHyphenated.js.map +1 -1
  61. package/package.json +16 -15
  62. package/dist/esm/animation/index.js +0 -97
  63. package/dist/esm/animation/index.js.map +0 -1
  64. package/dist/esm/focusTrap/index.js +0 -18
  65. package/dist/esm/focusTrap/index.js.map +0 -1
  66. package/dist/esm/hooks/index.js +0 -235
  67. package/dist/esm/hooks/index.js.map +0 -1
  68. package/dist/esm/index.js +0 -547
  69. package/dist/esm/index.js.map +0 -1
  70. package/dist/esm/promise/index.js +0 -20
  71. package/dist/esm/promise/index.js.map +0 -1
  72. package/dist/esm/utils/index.js +0 -236
  73. package/dist/esm/utils/index.js.map +0 -1
  74. package/dist/main/animation/index.js.map +0 -1
  75. package/dist/main/hooks/index.js.map +0 -1
  76. package/dist/main/hooks/internal/index.js +0 -3
  77. package/dist/main/hooks/internal/index.js.map +0 -1
  78. package/dist/main/hooks/internal/types.js +0 -3
  79. package/dist/main/hooks/internal/types.js.map +0 -1
  80. package/dist/main/index.js.map +0 -1
  81. package/dist/main/promise/index.js.map +0 -1
  82. package/dist/main/utils/createWithElementContext.js +0 -64
  83. package/dist/main/utils/createWithElementContext.js.map +0 -1
  84. package/dist/main/utils/index.js.map +0 -1
@@ -1,24 +1,46 @@
1
- import { ReactiveSet } from '@solid-primitives/set';
2
- import { createRoot, createEffect } from 'solid-js';
3
- import { clearAllBodyScrollLocks, disableBodyScroll as disableBodyScroll$1, enableBodyScroll as enableBodyScroll$1 } from 'body-scroll-lock-upgrade';
4
-
5
- const activeElements = new ReactiveSet();
1
+ import { createGuardedEffect } from "./guarded-effect.js";
2
+ import { createRoot } from "solid-js";
3
+ import { ReactiveSet } from "@solid-primitives/set";
4
+ import { clearAllBodyScrollLocks, disableBodyScroll, enableBodyScroll } from "body-scroll-lock-upgrade";
5
+ //#region src/utils/body-scroll-lock.ts
6
+ var activeElements = new ReactiveSet();
6
7
  createRoot(() => {
7
- createEffect(() => {
8
- if (activeElements.size === 0) {
9
- clearAllBodyScrollLocks();
10
- }
11
- });
8
+ createGuardedEffect(() => activeElements.size, (size) => {
9
+ if (size === 0) clearAllBodyScrollLocks();
10
+ });
12
11
  });
13
- const disableBodyScroll = (element, options) => {
14
- disableBodyScroll$1(element, options);
15
- activeElements.add(element);
12
+ var disableBodyScroll$1 = (element, options) => {
13
+ disableBodyScroll(element, options);
14
+ activeElements.add(element);
16
15
  };
17
- const enableBodyScroll = (element) => {
18
- enableBodyScroll$1(element);
19
- activeElements.delete(element);
16
+ var enableBodyScroll$1 = (element) => {
17
+ enableBodyScroll(element);
18
+ activeElements.delete(element);
19
+ releaseOrphans(element);
20
20
  };
21
+ /**
22
+ * Drop locks that cannot be valid any more.
23
+ *
24
+ * Two ways one is orphaned, and nothing else notices either:
25
+ *
26
+ * - **It sits inside the element we just released.** A drawer nested in a
27
+ * closing drawer is hidden along with it, but its own `isOpen` never
28
+ * changes, so its effect never re-runs and its cleanup never fires.
29
+ * - **It left the document** without its component being disposed.
30
+ *
31
+ * This matters because the underlying library keeps the body locked while any
32
+ * element is still registered. One orphan is enough to freeze the page, and
33
+ * the `size === 0` sweep above can never fire while it is held.
34
+ */
35
+ function releaseOrphans(released) {
36
+ for (const active of [...activeElements]) {
37
+ if (active === released) continue;
38
+ if (!released.contains(active) && active.isConnected) continue;
39
+ enableBodyScroll(active);
40
+ activeElements.delete(active);
41
+ }
42
+ }
43
+ //#endregion
44
+ export { disableBodyScroll$1 as disableBodyScroll, enableBodyScroll$1 as enableBodyScroll };
21
45
 
22
- export { disableBodyScroll, enableBodyScroll };
23
- //# sourceMappingURL=body-scroll-lock.js.map
24
46
  //# sourceMappingURL=body-scroll-lock.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/body-scroll-lock.ts"],"names":["_clearAllBodyScrollLocks","_disableBodyScroll","_enableBodyScroll"],"mappings":";;;;AAUA,MAAM,cAAA,GAAiB,IAAI,WAA0C,EAAA;AAErE,UAAA,CAAW,MAAM;AACf,EAAA,YAAA,CAAa,MAAM;AACjB,IAAI,IAAA,cAAA,CAAe,SAAS,CAAG,EAAA;AAC7B,MAAyBA,uBAAA,EAAA;AAAA;AAC3B,GACD,CAAA;AACH,CAAC,CAAA;AAEY,MAAA,iBAAA,GAAoB,CAC/B,OAAA,EACA,OACG,KAAA;AACH,EAAAC,mBAAA,CAAmB,SAAS,OAAO,CAAA;AACnC,EAAA,cAAA,CAAe,IAAI,OAAO,CAAA;AAC5B;AAEa,MAAA,gBAAA,GAAmB,CAAC,OAA0C,KAAA;AACzE,EAAAC,kBAAA,CAAkB,OAAO,CAAA;AACzB,EAAA,cAAA,CAAe,OAAO,OAAO,CAAA;AAC/B","file":"body-scroll-lock.js","sourcesContent":["import { ReactiveSet } from \"@solid-primitives/set\";\nimport type { ICustomElement } from \"component-register\";\nimport { createEffect, createRoot } from \"solid-js\";\nimport {\n enableBodyScroll as _enableBodyScroll,\n disableBodyScroll as _disableBodyScroll,\n clearAllBodyScrollLocks as _clearAllBodyScrollLocks,\n type BodyScrollOptions,\n} from \"body-scroll-lock-upgrade\";\n\nconst activeElements = new ReactiveSet<ICustomElement & HTMLElement>();\n\ncreateRoot(() => {\n createEffect(() => {\n if (activeElements.size === 0) {\n _clearAllBodyScrollLocks();\n }\n });\n});\n\nexport const disableBodyScroll = (\n element: ICustomElement & HTMLElement,\n options?: BodyScrollOptions,\n) => {\n _disableBodyScroll(element, options);\n activeElements.add(element);\n};\n\nexport const enableBodyScroll = (element: ICustomElement & HTMLElement) => {\n _enableBodyScroll(element);\n activeElements.delete(element);\n};\n"]}
1
+ {"version":3,"file":"body-scroll-lock.js","names":[],"sources":["../../../src/utils/body-scroll-lock.ts"],"sourcesContent":["import { ReactiveSet } from \"@solid-primitives/set\";\nimport {\n\tclearAllBodyScrollLocks as _clearAllBodyScrollLocks,\n\tdisableBodyScroll as _disableBodyScroll,\n\tenableBodyScroll as _enableBodyScroll,\n\ttype BodyScrollOptions,\n} from \"body-scroll-lock-upgrade\";\nimport type { ICustomElement } from \"component-register\";\nimport { createRoot } from \"solid-js\";\nimport { createGuardedEffect } from \"./guarded-effect.js\";\n\nconst activeElements = new ReactiveSet<ICustomElement & HTMLElement>();\n\ncreateRoot(() => {\n\t// v2 `createEffect` is (compute, apply) — the single-argument form is an\n\t// error. The compute phase tracks; the apply phase does the work.\n\tcreateGuardedEffect(\n\t\t() => activeElements.size,\n\t\t(size) => {\n\t\t\tif (size === 0) _clearAllBodyScrollLocks();\n\t\t},\n\t);\n});\n\nexport const disableBodyScroll = (\n\telement: ICustomElement & HTMLElement,\n\toptions?: BodyScrollOptions,\n) => {\n\t_disableBodyScroll(element, options);\n\tactiveElements.add(element);\n};\n\nexport const enableBodyScroll = (element: ICustomElement & HTMLElement) => {\n\t_enableBodyScroll(element);\n\tactiveElements.delete(element);\n\treleaseOrphans(element);\n};\n\n/**\n * Drop locks that cannot be valid any more.\n *\n * Two ways one is orphaned, and nothing else notices either:\n *\n * - **It sits inside the element we just released.** A drawer nested in a\n * closing drawer is hidden along with it, but its own `isOpen` never\n * changes, so its effect never re-runs and its cleanup never fires.\n * - **It left the document** without its component being disposed.\n *\n * This matters because the underlying library keeps the body locked while any\n * element is still registered. One orphan is enough to freeze the page, and\n * the `size === 0` sweep above can never fire while it is held.\n */\nfunction releaseOrphans(released: ICustomElement & HTMLElement) {\n\tfor (const active of [...activeElements]) {\n\t\tif (active === released) continue;\n\t\tif (!released.contains(active) && active.isConnected) continue;\n\t\t_enableBodyScroll(active);\n\t\tactiveElements.delete(active);\n\t}\n}\n"],"mappings":";;;;;AAWA,IAAM,iBAAiB,IAAI,YAA0C;AAErE,iBAAiB;CAGhB,0BACO,eAAe,OACpB,SAAS;EACT,IAAI,SAAS,GAAG,wBAAyB;CAC1C,CACD;AACD,CAAC;AAED,IAAa,uBACZ,SACA,YACI;CACJ,kBAAmB,SAAS,OAAO;CACnC,eAAe,IAAI,OAAO;AAC3B;AAEA,IAAa,sBAAoB,YAA0C;CAC1E,iBAAkB,OAAO;CACzB,eAAe,OAAO,OAAO;CAC7B,eAAe,OAAO;AACvB;;;;;;;;;;;;;;;AAgBA,SAAS,eAAe,UAAwC;CAC/D,KAAK,MAAM,UAAU,CAAC,GAAG,cAAc,GAAG;EACzC,IAAI,WAAW,UAAU;EACzB,IAAI,CAAC,SAAS,SAAS,MAAM,KAAK,OAAO,aAAa;EACtD,iBAAkB,MAAM;EACxB,eAAe,OAAO,MAAM;CAC7B;AACD"}
@@ -1,8 +1,8 @@
1
+ //#region src/utils/checkLoaded.ts
1
2
  function checkLoaded() {
2
- return document.readyState === "complete" || //@ts-ignore
3
- document.readyState === "loaded" || document.readyState === "interactive";
3
+ return document.readyState === "complete" || document.readyState === "loaded" || document.readyState === "interactive";
4
4
  }
5
-
5
+ //#endregion
6
6
  export { checkLoaded };
7
- //# sourceMappingURL=checkLoaded.js.map
7
+
8
8
  //# sourceMappingURL=checkLoaded.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/checkLoaded.ts"],"names":[],"mappings":"AAAO,SAAS,WAAc,GAAA;AAC5B,EAAA,OACE,SAAS,UAAe,KAAA,UAAA;AAAA,EAExB,QAAS,CAAA,UAAA,KAAe,QACxB,IAAA,QAAA,CAAS,UAAe,KAAA,aAAA;AAE5B","file":"checkLoaded.js","sourcesContent":["export function checkLoaded() {\n return (\n document.readyState === \"complete\" ||\n //@ts-ignore\n document.readyState === \"loaded\" ||\n document.readyState === \"interactive\"\n );\n}\n"]}
1
+ {"version":3,"file":"checkLoaded.js","names":[],"sources":["../../../src/utils/checkLoaded.ts"],"sourcesContent":["export function checkLoaded() {\n\treturn (\n\t\tdocument.readyState === \"complete\" ||\n\t\t//@ts-expect-error\n\t\tdocument.readyState === \"loaded\" ||\n\t\tdocument.readyState === \"interactive\"\n\t);\n}\n"],"mappings":";AAAA,SAAgB,cAAc;CAC7B,OACC,SAAS,eAAe,cAExB,SAAS,eAAe,YACxB,SAAS,eAAe;AAE1B"}
@@ -0,0 +1,85 @@
1
+ import { createMemo, createSignal, onCleanup } from "solid-js";
2
+ //#region src/utils/createRemoteContext.ts
3
+ /** Milliseconds to wait before warning that a target has not appeared. */
4
+ var MISSING_TARGET_WARNING_MS = 5e3;
5
+ var RemoteContextError = class extends Error {
6
+ target;
7
+ constructor(message, target) {
8
+ super(message);
9
+ this.target = target;
10
+ this.name = "RemoteContextError";
11
+ }
12
+ };
13
+ var defaultResolve = (target, host) => target ? document.querySelector(target) : host;
14
+ /** Read a `component-register` context off an element, if it is there. */
15
+ var readContext = (element, context) => {
16
+ const symbol = Object.getOwnPropertySymbols(element).find((candidate) => candidate.description === "element-context");
17
+ if (!symbol) return void 0;
18
+ return element[symbol]?.[context.id];
19
+ };
20
+ /**
21
+ * Wait for `resolve` to produce an element.
22
+ *
23
+ * "Not found" and "not yet" are the same observation — Shopify sections mount
24
+ * asynchronously, so a trigger is routinely parsed before its provider exists.
25
+ * This never gives up, but warns once after a grace period so a typo is still
26
+ * discoverable rather than hanging in silence.
27
+ */
28
+ var waitForElement = (resolve, signal, describe) => new Promise((resolvePromise, reject) => {
29
+ const found = resolve();
30
+ if (found) return resolvePromise(found);
31
+ const warning = setTimeout(() => {
32
+ console.warn(`${describe()}: target has not appeared after ${MISSING_TARGET_WARNING_MS / 1e3}s. Still waiting — check the selector if this is unexpected.`);
33
+ }, MISSING_TARGET_WARNING_MS);
34
+ const settle = (element) => {
35
+ clearTimeout(warning);
36
+ observer.disconnect();
37
+ resolvePromise(element);
38
+ };
39
+ const observer = new MutationObserver(() => {
40
+ const element = resolve();
41
+ if (element) settle(element);
42
+ });
43
+ observer.observe(document.documentElement, {
44
+ childList: true,
45
+ subtree: true
46
+ });
47
+ signal.addEventListener("abort", () => {
48
+ clearTimeout(warning);
49
+ observer.disconnect();
50
+ reject(new RemoteContextError(`${describe()}: lookup aborted`, null));
51
+ }, { once: true });
52
+ });
53
+ /**
54
+ * Build a hook that resolves one context from a *lateral* element.
55
+ *
56
+ * ```ts
57
+ * export const useDrawerAt = createRemoteContext<DrawerContext>(DrawerContextState);
58
+ * ```
59
+ */
60
+ function createRemoteContext(context, { resolve = defaultResolve, name } = {}) {
61
+ return (target, host) => {
62
+ const [released, setReleased] = createSignal(0);
63
+ return createMemo(async () => {
64
+ const spec = target();
65
+ released();
66
+ const controller = new AbortController();
67
+ onCleanup(() => controller.abort());
68
+ const describe = () => `<${host.localName}> -> ${name ?? String(context.id.description)}` + (spec ? ` (target="${spec}")` : "");
69
+ const element = await waitForElement(() => resolve(spec, host), controller.signal, describe);
70
+ const tag = element.localName;
71
+ if (tag.includes("-")) {
72
+ await customElements.whenDefined(tag);
73
+ customElements.upgrade(element);
74
+ }
75
+ const value = readContext(element, context);
76
+ if (value === void 0) throw new RemoteContextError(`${describe()}: <${tag}> provides no such context`, spec);
77
+ element.addReleaseCallback?.(() => setReleased((n) => n + 1));
78
+ return value;
79
+ });
80
+ };
81
+ }
82
+ //#endregion
83
+ export { RemoteContextError, createRemoteContext };
84
+
85
+ //# sourceMappingURL=createRemoteContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRemoteContext.js","names":[],"sources":["../../../src/utils/createRemoteContext.ts"],"sourcesContent":["/**\n * Successor to `createWithElementContext`.\n *\n * The problem is unchanged and is *not* solved by Solid's owner tree:\n * `<drawer-trigger target=\"#drawer-a\">` needs the context of an element that\n * is not an ancestor. Owner adoption only walks up, in v1 and v2 alike, so\n * lateral lookup stays a DOM lookup. What changed is everything around it.\n *\n * v1 was `withDrawerElementContext(target, dependencies, callback)` — an\n * effect taking a callback and returning nothing. The callback form existed\n * for exactly one reason: the context was not available at call time, so\n * there was nothing to return. Every other part was scaffolding for that\n * fact — `createEffect(on(...))` to re-run, a hand-rolled `createRoot` to give\n * the callback a disposal scope, an `AbortController` to cancel a pending\n * lookup, `invokeOnLoaded` to wait for `DOMContentLoaded`, and a custom\n * `bryt-designs:element-destroyed` event so teardown could be observed.\n *\n * Solid 2 makes \"not available yet\" a value, so this returns the context:\n *\n * ```ts\n * const drawer = useDrawerAt(() => props.target);\n *\n * createEffect(\n * () => ({ ctx: drawer(), action: props.action }),\n * ({ ctx, action }) => { ...; return () => cleanup(); },\n * );\n * ```\n *\n * An effect whose compute reads a *pending* async memo simply waits — apply is\n * deferred until it resolves, with no `<Loading>` boundary required. That\n * matters because most consumers here render no markup and have nowhere to\n * suspend. The `dependencies` argument is gone because `createEffect`'s\n * compute phase *is* the explicit-dependency form that `on()` used to provide.\n *\n * Two readiness facts from `component-register` make the wait exact rather\n * than a timing guess:\n *\n * - `provide()` stamps `element[Symbol('element-context')]` synchronously\n * inside `connectedCallback`, so the context exists the moment the element\n * is upgraded and connected — never later. `DOMContentLoaded` was never the\n * right signal.\n * - `addReleaseCallback` is the real teardown hook, so a provider going away\n * is observable without cooperation from whoever wrote it.\n */\nimport type { ICustomElement } from \"component-register\";\nimport { createMemo, createSignal, onCleanup } from \"solid-js\";\n\n/** Milliseconds to wait before warning that a target has not appeared. */\nconst MISSING_TARGET_WARNING_MS = 5_000;\n\ntype ContextRef = { id: symbol };\n\nexport class RemoteContextError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\treadonly target: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"RemoteContextError\";\n\t}\n}\n\nexport type RemoteContextOptions = {\n\t/**\n\t * Map a `target` to the element that actually provides the context.\n\t *\n\t * Synchronous by design — it answers \"which element, given the DOM as it is\n\t * now\". Waiting for the element to appear is the helper's job, and it\n\t * re-runs `resolve` on each mutation, so a strategy never has to implement\n\t * retrying itself.\n\t *\n\t * Defaults to `document.querySelector(target)`, falling back to the host\n\t * element when `target` is empty.\n\t */\n\tresolve?: (target: string | undefined, host: HTMLElement) => Element | null;\n\t/** Label used in warnings and errors. Defaults to the context's symbol. */\n\tname?: string;\n};\n\nconst defaultResolve = (target: string | undefined, host: HTMLElement) =>\n\ttarget ? document.querySelector(target) : host;\n\n/** Read a `component-register` context off an element, if it is there. */\nconst readContext = <T>(element: Element, context: ContextRef) => {\n\tconst symbol = Object.getOwnPropertySymbols(element).find(\n\t\t(candidate) => candidate.description === \"element-context\",\n\t);\n\tif (!symbol) return undefined;\n\tconst contexts = (element as never as Record<symbol, Record<symbol, T>>)[\n\t\tsymbol\n\t];\n\treturn contexts?.[context.id];\n};\n\n/**\n * Wait for `resolve` to produce an element.\n *\n * \"Not found\" and \"not yet\" are the same observation — Shopify sections mount\n * asynchronously, so a trigger is routinely parsed before its provider exists.\n * This never gives up, but warns once after a grace period so a typo is still\n * discoverable rather than hanging in silence.\n */\nconst waitForElement = (\n\tresolve: () => Element | null,\n\tsignal: AbortSignal,\n\tdescribe: () => string,\n) =>\n\tnew Promise<Element>((resolvePromise, reject) => {\n\t\tconst found = resolve();\n\t\tif (found) return resolvePromise(found);\n\n\t\tconst warning = setTimeout(() => {\n\t\t\tconsole.warn(\n\t\t\t\t`${describe()}: target has not appeared after ${\n\t\t\t\t\tMISSING_TARGET_WARNING_MS / 1000\n\t\t\t\t}s. Still waiting — check the selector if this is unexpected.`,\n\t\t\t);\n\t\t}, MISSING_TARGET_WARNING_MS);\n\n\t\tconst settle = (element: Element) => {\n\t\t\tclearTimeout(warning);\n\t\t\tobserver.disconnect();\n\t\t\tresolvePromise(element);\n\t\t};\n\n\t\tconst observer = new MutationObserver(() => {\n\t\t\tconst element = resolve();\n\t\t\tif (element) settle(element);\n\t\t});\n\t\tobserver.observe(document.documentElement, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t});\n\n\t\tsignal.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(warning);\n\t\t\t\tobserver.disconnect();\n\t\t\t\treject(new RemoteContextError(`${describe()}: lookup aborted`, null));\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n\n/**\n * Build a hook that resolves one context from a *lateral* element.\n *\n * ```ts\n * export const useDrawerAt = createRemoteContext<DrawerContext>(DrawerContextState);\n * ```\n */\nexport function createRemoteContext<T>(\n\tcontext: ContextRef,\n\t{ resolve = defaultResolve, name }: RemoteContextOptions = {},\n) {\n\treturn (target: () => string | undefined, host: HTMLElement) => {\n\t\t// Lives outside the memo on purpose. Created inside, every re-run would\n\t\t// make a fresh signal and the release callback registered by the previous\n\t\t// run would bump a signal nothing reads.\n\t\tconst [released, setReleased] = createSignal(0);\n\n\t\treturn createMemo(async () => {\n\t\t\t// Every reactive read must happen before the first `await`, or the\n\t\t\t// dependency is silently lost and this memo never re-runs again.\n\t\t\tconst spec = target();\n\t\t\treleased();\n\n\t\t\tconst controller = new AbortController();\n\t\t\tonCleanup(() => controller.abort());\n\n\t\t\tconst describe = () =>\n\t\t\t\t`<${host.localName}> -> ${name ?? String(context.id.description)}` +\n\t\t\t\t(spec ? ` (target=\"${spec}\")` : \"\");\n\n\t\t\tconst element = await waitForElement(\n\t\t\t\t() => resolve(spec, host),\n\t\t\t\tcontroller.signal,\n\t\t\t\tdescribe,\n\t\t\t);\n\n\t\t\t// The element may still be un-upgraded if this consumer was parsed\n\t\t\t// before the provider's definition landed. `whenDefined` plus an\n\t\t\t// explicit `upgrade` covers both orderings, and neither needs\n\t\t\t// `DOMContentLoaded`.\n\t\t\tconst tag = element.localName;\n\t\t\tif (tag.includes(\"-\")) {\n\t\t\t\tawait customElements.whenDefined(tag);\n\t\t\t\tcustomElements.upgrade(element);\n\t\t\t}\n\n\t\t\tconst value = readContext<T>(element, context);\n\t\t\tif (value === undefined) {\n\t\t\t\t// A definite mismatch, not a race: the element exists and is\n\t\t\t\t// upgraded, and still provides nothing. More waiting cannot fix it.\n\t\t\t\tthrow new RemoteContextError(\n\t\t\t\t\t`${describe()}: <${tag}> provides no such context`,\n\t\t\t\t\tspec,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Re-attach on replacement. v1 aborted here and never recovered, so a\n\t\t\t// Shopify section re-render left every outside trigger permanently\n\t\t\t// inert. Bumping the signal invalidates this memo, which waits for the\n\t\t\t// replacement and hands the consumer's effect a fresh context.\n\t\t\tconst provider = element as Element & Partial<ICustomElement>;\n\t\t\tprovider.addReleaseCallback?.(() => setReleased((n) => n + 1));\n\n\t\t\treturn value;\n\t\t});\n\t};\n}\n"],"mappings":";;;AAgDA,IAAM,4BAA4B;AAIlC,IAAa,qBAAb,cAAwC,MAAM;CAGnC;CAFV,YACC,SACA,QACC;EACD,MAAM,OAAO;EAFJ,KAAA,SAAA;EAGT,KAAK,OAAO;CACb;AACD;AAmBA,IAAM,kBAAkB,QAA4B,SACnD,SAAS,SAAS,cAAc,MAAM,IAAI;;AAG3C,IAAM,eAAkB,SAAkB,YAAwB;CACjE,MAAM,SAAS,OAAO,sBAAsB,OAAO,CAAC,CAAC,MACnD,cAAc,UAAU,gBAAgB,iBAC1C;CACA,IAAI,CAAC,QAAQ,OAAO,KAAA;CAIpB,OAHkB,QACjB,OAEM,GAAW,QAAQ;AAC3B;;;;;;;;;AAUA,IAAM,kBACL,SACA,QACA,aAEA,IAAI,SAAkB,gBAAgB,WAAW;CAChD,MAAM,QAAQ,QAAQ;CACtB,IAAI,OAAO,OAAO,eAAe,KAAK;CAEtC,MAAM,UAAU,iBAAiB;EAChC,QAAQ,KACP,GAAG,SAAS,EAAE,kCACb,4BAA4B,IAC5B,6DACF;CACD,GAAG,yBAAyB;CAE5B,MAAM,UAAU,YAAqB;EACpC,aAAa,OAAO;EACpB,SAAS,WAAW;EACpB,eAAe,OAAO;CACvB;CAEA,MAAM,WAAW,IAAI,uBAAuB;EAC3C,MAAM,UAAU,QAAQ;EACxB,IAAI,SAAS,OAAO,OAAO;CAC5B,CAAC;CACD,SAAS,QAAQ,SAAS,iBAAiB;EAC1C,WAAW;EACX,SAAS;CACV,CAAC;CAED,OAAO,iBACN,eACM;EACL,aAAa,OAAO;EACpB,SAAS,WAAW;EACpB,OAAO,IAAI,mBAAmB,GAAG,SAAS,EAAE,mBAAmB,IAAI,CAAC;CACrE,GACA,EAAE,MAAM,KAAK,CACd;AACD,CAAC;;;;;;;;AASF,SAAgB,oBACf,SACA,EAAE,UAAU,gBAAgB,SAA+B,CAAC,GAC3D;CACD,QAAQ,QAAkC,SAAsB;EAI/D,MAAM,CAAC,UAAU,eAAe,aAAa,CAAC;EAE9C,OAAO,WAAW,YAAY;GAG7B,MAAM,OAAO,OAAO;GACpB,SAAS;GAET,MAAM,aAAa,IAAI,gBAAgB;GACvC,gBAAgB,WAAW,MAAM,CAAC;GAElC,MAAM,iBACL,IAAI,KAAK,UAAU,OAAO,QAAQ,OAAO,QAAQ,GAAG,WAAW,OAC9D,OAAO,aAAa,KAAK,MAAM;GAEjC,MAAM,UAAU,MAAM,qBACf,QAAQ,MAAM,IAAI,GACxB,WAAW,QACX,QACD;GAMA,MAAM,MAAM,QAAQ;GACpB,IAAI,IAAI,SAAS,GAAG,GAAG;IACtB,MAAM,eAAe,YAAY,GAAG;IACpC,eAAe,QAAQ,OAAO;GAC/B;GAEA,MAAM,QAAQ,YAAe,SAAS,OAAO;GAC7C,IAAI,UAAU,KAAA,GAGb,MAAM,IAAI,mBACT,GAAG,SAAS,EAAE,KAAK,IAAI,6BACvB,IACD;GAQD,QAAS,2BAA2B,aAAa,MAAM,IAAI,CAAC,CAAC;GAE7D,OAAO;EACR,CAAC;CACF;AACD"}
@@ -0,0 +1,52 @@
1
+ //#region src/utils/disclosure.ts
2
+ /**
3
+ * ARIA wiring for disclosure widgets (accordion and collapsible): the
4
+ * trigger's button reports whether its content is open and which content it
5
+ * controls.
6
+ */
7
+ var CONTROL_SELECTOR = "button, [role='button']";
8
+ /**
9
+ * The element a trigger exposes to assistive tech: the trigger itself when it
10
+ * has `role="button"`, otherwise the first button inside it.
11
+ */
12
+ function findDisclosureControl(trigger) {
13
+ if (trigger.getAttribute("role") === "button") return trigger;
14
+ return trigger.querySelector(CONTROL_SELECTOR);
15
+ }
16
+ /**
17
+ * The content belonging to the same item as `trigger`, skipping the content of items
18
+ * nested inside it.
19
+ */
20
+ function findDisclosureContent(trigger, itemTag, contentTag) {
21
+ const item = trigger.closest(itemTag);
22
+ if (!item) return null;
23
+ for (const content of Array.from(item.querySelectorAll(contentTag))) if (content.closest(itemTag) === item) return content;
24
+ return null;
25
+ }
26
+ var generatedIdCount = 0;
27
+ /** Returns the element's id, assigning a unique `${prefix}-N` if it has none. */
28
+ function ensureId(element, prefix) {
29
+ if (!element.id) {
30
+ let id;
31
+ do {
32
+ generatedIdCount += 1;
33
+ id = `${prefix}-${generatedIdCount}`;
34
+ } while (document.getElementById(id));
35
+ element.id = id;
36
+ }
37
+ return element.id;
38
+ }
39
+ /**
40
+ * Points the control's `aria-controls` at the content, unless the markup already
41
+ * set one. Returns whether the control is linked.
42
+ */
43
+ function linkDisclosureControl(control, content, idPrefix) {
44
+ if (control.hasAttribute("aria-controls")) return true;
45
+ if (!content) return false;
46
+ control.setAttribute("aria-controls", ensureId(content, idPrefix));
47
+ return true;
48
+ }
49
+ //#endregion
50
+ export { ensureId, findDisclosureContent, findDisclosureControl, linkDisclosureControl };
51
+
52
+ //# sourceMappingURL=disclosure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disclosure.js","names":[],"sources":["../../../src/utils/disclosure.ts"],"sourcesContent":["/**\n * ARIA wiring for disclosure widgets (accordion and collapsible): the\n * trigger's button reports whether its content is open and which content it\n * controls.\n */\n\nconst CONTROL_SELECTOR = \"button, [role='button']\";\n\n/**\n * The element a trigger exposes to assistive tech: the trigger itself when it\n * has `role=\"button\"`, otherwise the first button inside it.\n */\nexport function findDisclosureControl(trigger: HTMLElement) {\n\tif (trigger.getAttribute(\"role\") === \"button\") return trigger;\n\treturn trigger.querySelector<HTMLElement>(CONTROL_SELECTOR);\n}\n\n/**\n * The content belonging to the same item as `trigger`, skipping the content of items\n * nested inside it.\n */\nexport function findDisclosureContent(\n\ttrigger: HTMLElement,\n\titemTag: string,\n\tcontentTag: string,\n) {\n\tconst item = trigger.closest(itemTag);\n\tif (!item) return null;\n\tfor (const content of Array.from(\n\t\titem.querySelectorAll<HTMLElement>(contentTag),\n\t)) {\n\t\tif (content.closest(itemTag) === item) return content;\n\t}\n\treturn null;\n}\n\nlet generatedIdCount = 0;\n\n/** Returns the element's id, assigning a unique `${prefix}-N` if it has none. */\nexport function ensureId(element: HTMLElement, prefix: string) {\n\tif (!element.id) {\n\t\tlet id: string;\n\t\tdo {\n\t\t\tgeneratedIdCount += 1;\n\t\t\tid = `${prefix}-${generatedIdCount}`;\n\t\t} while (document.getElementById(id));\n\t\telement.id = id;\n\t}\n\treturn element.id;\n}\n\n/**\n * Points the control's `aria-controls` at the content, unless the markup already\n * set one. Returns whether the control is linked.\n */\nexport function linkDisclosureControl(\n\tcontrol: HTMLElement,\n\tcontent: HTMLElement | null,\n\tidPrefix: string,\n) {\n\tif (control.hasAttribute(\"aria-controls\")) return true;\n\tif (!content) return false;\n\tcontrol.setAttribute(\"aria-controls\", ensureId(content, idPrefix));\n\treturn true;\n}\n"],"mappings":";;;;;;AAMA,IAAM,mBAAmB;;;;;AAMzB,SAAgB,sBAAsB,SAAsB;CAC3D,IAAI,QAAQ,aAAa,MAAM,MAAM,UAAU,OAAO;CACtD,OAAO,QAAQ,cAA2B,gBAAgB;AAC3D;;;;;AAMA,SAAgB,sBACf,SACA,SACA,YACC;CACD,MAAM,OAAO,QAAQ,QAAQ,OAAO;CACpC,IAAI,CAAC,MAAM,OAAO;CAClB,KAAK,MAAM,WAAW,MAAM,KAC3B,KAAK,iBAA8B,UAAU,CAC9C,GACC,IAAI,QAAQ,QAAQ,OAAO,MAAM,MAAM,OAAO;CAE/C,OAAO;AACR;AAEA,IAAI,mBAAmB;;AAGvB,SAAgB,SAAS,SAAsB,QAAgB;CAC9D,IAAI,CAAC,QAAQ,IAAI;EAChB,IAAI;EACJ,GAAG;GACF,oBAAoB;GACpB,KAAK,GAAG,OAAO,GAAG;EACnB,SAAS,SAAS,eAAe,EAAE;EACnC,QAAQ,KAAK;CACd;CACA,OAAO,QAAQ;AAChB;;;;;AAMA,SAAgB,sBACf,SACA,SACA,UACC;CACD,IAAI,QAAQ,aAAa,eAAe,GAAG,OAAO;CAClD,IAAI,CAAC,SAAS,OAAO;CACrB,QAAQ,aAAa,iBAAiB,SAAS,SAAS,QAAQ,CAAC;CACjE,OAAO;AACR"}
@@ -1,22 +1,18 @@
1
+ //#region src/utils/formatValue.ts
1
2
  function formatValue(format, value) {
2
- switch (format) {
3
- case "currency":
4
- if (typeof value === "number") return window.Shopify.formatMoney(value);
5
- if (typeof value === "string")
6
- return window.Shopify.formatMoney(parseFloat(value));
7
- console.warn("formatValue: Unsupported value type for currency format.");
8
- return window.Shopify.formatMoney(0);
9
- case "number":
10
- return Number(value);
11
- case "boolean":
12
- return Boolean(value);
13
- case "invert_boolean":
14
- return !Boolean(value);
15
- default:
16
- return value;
17
- }
3
+ switch (format) {
4
+ case "currency":
5
+ if (typeof value === "number") return window.Shopify.formatMoney(value);
6
+ if (typeof value === "string") return window.Shopify.formatMoney(parseFloat(value));
7
+ console.warn("formatValue: Unsupported value type for currency format.");
8
+ return window.Shopify.formatMoney(0);
9
+ case "number": return Number(value);
10
+ case "boolean": return Boolean(value);
11
+ case "invert_boolean": return !value;
12
+ default: return value;
13
+ }
18
14
  }
19
-
15
+ //#endregion
20
16
  export { formatValue };
21
- //# sourceMappingURL=formatValue.js.map
17
+
22
18
  //# sourceMappingURL=formatValue.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/formatValue.ts"],"names":[],"mappings":"AAOO,SAAS,WAAA,CAAY,QAAgB,KAAgB,EAAA;AAC1D,EAAA,QAAQ,MAAQ;AAAA,IACd,KAAK,UAAA;AACH,MAAA,IAAI,OAAO,KAAU,KAAA,QAAA,SAAiB,MAAO,CAAA,OAAA,CAAQ,YAAY,KAAK,CAAA;AACtE,MAAA,IAAI,OAAO,KAAU,KAAA,QAAA;AACnB,QAAA,OAAO,MAAO,CAAA,OAAA,CAAQ,WAAY,CAAA,UAAA,CAAW,KAAK,CAAC,CAAA;AACrD,MAAA,OAAA,CAAQ,KAAK,0DAA0D,CAAA;AACvE,MAAO,OAAA,MAAA,CAAO,OAAQ,CAAA,WAAA,CAAY,CAAC,CAAA;AAAA,IACrC,KAAK,QAAA;AACH,MAAA,OAAO,OAAO,KAAK,CAAA;AAAA,IACrB,KAAK,SAAA;AACH,MAAA,OAAO,QAAQ,KAAK,CAAA;AAAA,IACtB,KAAK,gBAAA;AACH,MAAO,OAAA,CAAC,QAAQ,KAAK,CAAA;AAAA,IACvB;AACE,MAAO,OAAA,KAAA;AAAA;AAEb","file":"formatValue.js","sourcesContent":["export type Format =\n | \"currency\"\n | \"number\"\n | \"boolean\"\n | \"invert_boolean\"\n | string;\n\nexport function formatValue(format: Format, value: unknown) {\n switch (format) {\n case \"currency\":\n if (typeof value === \"number\") return window.Shopify.formatMoney(value);\n if (typeof value === \"string\")\n return window.Shopify.formatMoney(parseFloat(value));\n console.warn(\"formatValue: Unsupported value type for currency format.\");\n return window.Shopify.formatMoney(0);\n case \"number\":\n return Number(value);\n case \"boolean\":\n return Boolean(value);\n case \"invert_boolean\":\n return !Boolean(value);\n default:\n return value as any;\n }\n}\n\ndeclare global {\n interface Window {\n Shopify: {\n formatMoney(value: number, currency?: string): string;\n };\n }\n}\n"]}
1
+ {"version":3,"file":"formatValue.js","names":[],"sources":["../../../src/utils/formatValue.ts"],"sourcesContent":["export type Format =\n\t| \"currency\"\n\t| \"number\"\n\t| \"boolean\"\n\t| \"invert_boolean\"\n\t| string;\n\nexport function formatValue(format: Format, value: unknown) {\n\tswitch (format) {\n\t\tcase \"currency\":\n\t\t\tif (typeof value === \"number\") return window.Shopify.formatMoney(value);\n\t\t\tif (typeof value === \"string\")\n\t\t\t\treturn window.Shopify.formatMoney(parseFloat(value));\n\t\t\tconsole.warn(\"formatValue: Unsupported value type for currency format.\");\n\t\t\treturn window.Shopify.formatMoney(0);\n\t\tcase \"number\":\n\t\t\treturn Number(value);\n\t\tcase \"boolean\":\n\t\t\treturn Boolean(value);\n\t\tcase \"invert_boolean\":\n\t\t\treturn !value;\n\t\tdefault:\n\t\t\treturn value as any;\n\t}\n}\n\ndeclare global {\n\t// Contributed to the shared `Shopify` interface rather than re-declaring\n\t// `Window[\"Shopify\"]` as an object literal: several packages describe\n\t// different slices of the same runtime object, and two literals collide\n\t// as soon as they meet in one program (`docs:gen`). The shape of\n\t// `Window[\"Shopify\"]` here must stay identical to the one in\n\t// @brytdesigns/shopify-utils' vendored `standard-actions.d.ts`.\n\tinterface Shopify {\n\t\tformatMoney(value: number, currency?: string): string;\n\t}\n\n\tinterface Window {\n\t\tShopify: Shopify & {\n\t\t\t[key: string]: unknown;\n\t\t};\n\t}\n}\n"],"mappings":";AAOA,SAAgB,YAAY,QAAgB,OAAgB;CAC3D,QAAQ,QAAR;EACC,KAAK;GACJ,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,QAAQ,YAAY,KAAK;GACtE,IAAI,OAAO,UAAU,UACpB,OAAO,OAAO,QAAQ,YAAY,WAAW,KAAK,CAAC;GACpD,QAAQ,KAAK,0DAA0D;GACvE,OAAO,OAAO,QAAQ,YAAY,CAAC;EACpC,KAAK,UACJ,OAAO,OAAO,KAAK;EACpB,KAAK,WACJ,OAAO,QAAQ,KAAK;EACrB,KAAK,kBACJ,OAAO,CAAC;EACT,SACC,OAAO;CACT;AACD"}
@@ -1,14 +1,19 @@
1
+ //#region src/utils/getContextFromProvider.ts
2
+ /**
3
+ * Read a `component-register` context off an element, synchronously.
4
+ *
5
+ * The imperative counterpart to `createRemoteContext`: no waiting, no effect,
6
+ * callable from a plain `<script>` in a Liquid template when you already know
7
+ * the element is there. Deliberately kept separate — forcing an async,
8
+ * effect-shaped API on this use case would break theme scripts for no gain.
9
+ */
1
10
  function getContextFromProvider(context, element) {
2
- const ctxSymbol = Object.getOwnPropertySymbols(element).find(
3
- (s) => s.description === "element-context"
4
- );
5
- if (!ctxSymbol) {
6
- throw new Error("No context provider found for the provided element");
7
- }
8
- const contexts = element[ctxSymbol];
9
- return contexts[context.id];
11
+ const ctxSymbol = Object.getOwnPropertySymbols(element).find((s) => s.description === "element-context");
12
+ const value = (ctxSymbol ? element[ctxSymbol] : void 0)?.[context.id];
13
+ if (value === void 0) throw new Error(`<${element.localName}> provides no ${String(context.id.description ?? "element")} context. Check that it is the provider element and that it has been upgraded.`);
14
+ return value;
10
15
  }
11
-
16
+ //#endregion
12
17
  export { getContextFromProvider };
13
- //# sourceMappingURL=getContextFromProvider.js.map
18
+
14
19
  //# sourceMappingURL=getContextFromProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/getContextFromProvider.ts"],"names":[],"mappings":"AAEO,SAAS,sBAAA,CACd,SACA,OACA,EAAA;AACA,EAAA,MAAM,SAAY,GAAA,MAAA,CAAO,qBAAsB,CAAA,OAAO,CAAE,CAAA,IAAA;AAAA,IACtD,CAAC,CAAM,KAAA,CAAA,CAAE,WAAgB,KAAA;AAAA,GAC3B;AACA,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAM,MAAA,IAAI,MAAM,oDAAoD,CAAA;AAAA;AAGtE,EAAM,MAAA,QAAA,GAAW,QAAQ,SAAS,CAAA;AAClC,EAAO,OAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AAC5B","file":"getContextFromProvider.js","sourcesContent":["import type { createContext } from \"component-register\";\n\nexport function getContextFromProvider<T>(\n context: ReturnType<typeof createContext>,\n element: Element,\n) {\n const ctxSymbol = Object.getOwnPropertySymbols(element).find(\n (s) => s.description === \"element-context\",\n );\n if (!ctxSymbol) {\n throw new Error(\"No context provider found for the provided element\");\n }\n // @ts-ignore\n const contexts = element[ctxSymbol];\n return contexts[context.id] as T;\n}\n"]}
1
+ {"version":3,"file":"getContextFromProvider.js","names":[],"sources":["../../../src/utils/getContextFromProvider.ts"],"sourcesContent":["import type { createContext } from \"component-register\";\n\n/**\n * Read a `component-register` context off an element, synchronously.\n *\n * The imperative counterpart to `createRemoteContext`: no waiting, no effect,\n * callable from a plain `<script>` in a Liquid template when you already know\n * the element is there. Deliberately kept separate — forcing an async,\n * effect-shaped API on this use case would break theme scripts for no gain.\n */\nexport function getContextFromProvider<T>(\n\tcontext: ReturnType<typeof createContext>,\n\telement: Element,\n) {\n\tconst ctxSymbol = Object.getOwnPropertySymbols(element).find(\n\t\t(s) => s.description === \"element-context\",\n\t);\n\tconst contexts = ctxSymbol\n\t\t? (element as never as Record<symbol, Record<symbol, T>>)[ctxSymbol]\n\t\t: undefined;\n\tconst value = contexts?.[context.id];\n\n\tif (value === undefined) {\n\t\t// Naming both sides matters: the usual cause is a `target` pointing at\n\t\t// the wrong element, and \"no context provider found\" does not say which\n\t\t// element was inspected or what was expected of it.\n\t\tthrow new Error(\n\t\t\t`<${element.localName}> provides no ${String(\n\t\t\t\tcontext.id.description ?? \"element\",\n\t\t\t)} context. Check that it is the provider element and that it has been upgraded.`,\n\t\t);\n\t}\n\n\treturn value;\n}\n"],"mappings":";;;;;;;;;AAUA,SAAgB,uBACf,SACA,SACC;CACD,MAAM,YAAY,OAAO,sBAAsB,OAAO,CAAC,CAAC,MACtD,MAAM,EAAE,gBAAgB,iBAC1B;CAIA,MAAM,SAHW,YACb,QAAuD,aACxD,KAAA,EAAA,GACsB,QAAQ;CAEjC,IAAI,UAAU,KAAA,GAIb,MAAM,IAAI,MACT,IAAI,QAAQ,UAAU,gBAAgB,OACrC,QAAQ,GAAG,eAAe,SAC3B,EAAE,+EACH;CAGD,OAAO;AACR"}
@@ -1,11 +1,10 @@
1
+ //#region src/utils/getTemplateContent.ts
1
2
  function getTemplateContent(element, state) {
2
- const tmpl = element.querySelector(
3
- `template[${state}]`
4
- );
5
- if (!tmpl) return null;
6
- return tmpl.content;
3
+ const tmpl = element.querySelector(`template[${state}]`);
4
+ if (!tmpl) return null;
5
+ return tmpl.content;
7
6
  }
8
-
7
+ //#endregion
9
8
  export { getTemplateContent };
10
- //# sourceMappingURL=getTemplateContent.js.map
9
+
11
10
  //# sourceMappingURL=getTemplateContent.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/getTemplateContent.ts"],"names":[],"mappings":"AAAO,SAAS,kBAAA,CAAmB,SAAsB,KAAe,EAAA;AACtE,EAAA,MAAM,OAAO,OAAQ,CAAA,aAAA;AAAA,IACnB,YAAY,KAAK,CAAA,CAAA;AAAA,GACnB;AACA,EAAI,IAAA,CAAC,MAAa,OAAA,IAAA;AAClB,EAAA,OAAO,IAAK,CAAA,OAAA;AACd","file":"getTemplateContent.js","sourcesContent":["export function getTemplateContent(element: HTMLElement, state: string) {\n const tmpl = element.querySelector(\n `template[${state}]`,\n ) as HTMLTemplateElement;\n if (!tmpl) return null;\n return tmpl.content;\n}\n"]}
1
+ {"version":3,"file":"getTemplateContent.js","names":[],"sources":["../../../src/utils/getTemplateContent.ts"],"sourcesContent":["export function getTemplateContent(element: HTMLElement, state: string) {\n\tconst tmpl = element.querySelector(\n\t\t`template[${state}]`,\n\t) as HTMLTemplateElement;\n\tif (!tmpl) return null;\n\treturn tmpl.content;\n}\n"],"mappings":";AAAA,SAAgB,mBAAmB,SAAsB,OAAe;CACvE,MAAM,OAAO,QAAQ,cACpB,YAAY,MAAM,EACnB;CACA,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO,KAAK;AACb"}
@@ -0,0 +1,72 @@
1
+ import { createEffect } from "solid-js";
2
+ //#region src/utils/guarded-effect.ts
3
+ /**
4
+ * `createEffect` whose **apply phase cannot take the page down**.
5
+ *
6
+ * ## Why this exists
7
+ *
8
+ * Solid 2 treats the two phases of an effect very differently, and only one
9
+ * of them is dangerous. Measured against the installed `2.0.0-rc.14`, in
10
+ * `tests/component/reactivity-halt.test.ts`:
11
+ *
12
+ * | throw site | rest of the page |
13
+ * | ----------------- | --------------------------- |
14
+ * | component setup | unaffected |
15
+ * | compute phase | unaffected — the run is skipped and logged |
16
+ * | **apply phase** | **every computation on the page stops, permanently** |
17
+ *
18
+ * An uncaught apply-phase throw calls the runtime's `haltReactivity`, which
19
+ * is module-level global state. On a Shopify page every Bryt element shares
20
+ * one `solid-js` singleton, so one bad attribute on one element freezes every
21
+ * drawer, modal, slider and the cart until the shopper reloads.
22
+ *
23
+ * ## Why not an error boundary
24
+ *
25
+ * Because it does not work. `createErrorBoundary` is documented as catching
26
+ * this, and it does not in rc.14: the characterization test drives a
27
+ * subscribed boundary around the throwing effect and the fallback never runs
28
+ * while the rest of the page still freezes. Upstream's own guidance in
29
+ * `@solidjs/signals`' `createEffect` docblock is the half that holds —
30
+ *
31
+ * > The **effect phase is different**: it is your own imperative code, so
32
+ * > handle failures with `try/catch` where they occur.
33
+ *
34
+ * — which is all this function is. If a later release makes boundaries catch
35
+ * it, the characterization test starts failing and says so.
36
+ *
37
+ * The compute phase is deliberately NOT guarded. It is already non-fatal, and
38
+ * swallowing a compute error would hide a genuine reactivity bug behind an
39
+ * effect that silently stops updating.
40
+ *
41
+ * @param compute the tracking phase — reads signals, returns the value
42
+ * @param apply the imperative phase, may return a cleanup function
43
+ * @param options forwarded to `createEffect`; `name` is used in the report
44
+ */
45
+ function createGuardedEffect(compute, apply, options) {
46
+ createEffect(compute, ((value, prev) => {
47
+ try {
48
+ return apply(value, prev);
49
+ } catch (error) {
50
+ reportEffectFailure(error, options?.name);
51
+ return;
52
+ }
53
+ }), options);
54
+ }
55
+ /**
56
+ * Surfaces a contained failure without rethrowing it.
57
+ *
58
+ * `reportError` is the channel the platform already routes to
59
+ * `window.onerror`, so an error monitor on the page hears this exactly as it
60
+ * would have heard the uncaught throw — the difference is only that the
61
+ * reactive system is still running afterwards. It is not in older Safari, so
62
+ * the console is the fallback rather than the primary.
63
+ */
64
+ function reportEffectFailure(error, name) {
65
+ const where = name ? ` in effect "${name}"` : "";
66
+ console.error(`[brytdesigns] contained a failure${where}. The page keeps working; this would otherwise have halted every component on it.`, error);
67
+ if (typeof reportError === "function") reportError(error);
68
+ }
69
+ //#endregion
70
+ export { createGuardedEffect };
71
+
72
+ //# sourceMappingURL=guarded-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guarded-effect.js","names":[],"sources":["../../../src/utils/guarded-effect.ts"],"sourcesContent":["import type { EffectFunction, EffectOptions } from \"solid-js\";\nimport { createEffect } from \"solid-js\";\n\n/**\n * `createEffect` whose **apply phase cannot take the page down**.\n *\n * ## Why this exists\n *\n * Solid 2 treats the two phases of an effect very differently, and only one\n * of them is dangerous. Measured against the installed `2.0.0-rc.14`, in\n * `tests/component/reactivity-halt.test.ts`:\n *\n * | throw site | rest of the page |\n * | ----------------- | --------------------------- |\n * | component setup | unaffected |\n * | compute phase | unaffected — the run is skipped and logged |\n * | **apply phase** | **every computation on the page stops, permanently** |\n *\n * An uncaught apply-phase throw calls the runtime's `haltReactivity`, which\n * is module-level global state. On a Shopify page every Bryt element shares\n * one `solid-js` singleton, so one bad attribute on one element freezes every\n * drawer, modal, slider and the cart until the shopper reloads.\n *\n * ## Why not an error boundary\n *\n * Because it does not work. `createErrorBoundary` is documented as catching\n * this, and it does not in rc.14: the characterization test drives a\n * subscribed boundary around the throwing effect and the fallback never runs\n * while the rest of the page still freezes. Upstream's own guidance in\n * `@solidjs/signals`' `createEffect` docblock is the half that holds —\n *\n * > The **effect phase is different**: it is your own imperative code, so\n * > handle failures with `try/catch` where they occur.\n *\n * — which is all this function is. If a later release makes boundaries catch\n * it, the characterization test starts failing and says so.\n *\n * The compute phase is deliberately NOT guarded. It is already non-fatal, and\n * swallowing a compute error would hide a genuine reactivity bug behind an\n * effect that silently stops updating.\n *\n * @param compute the tracking phase — reads signals, returns the value\n * @param apply the imperative phase, may return a cleanup function\n * @param options forwarded to `createEffect`; `name` is used in the report\n */\nexport function createGuardedEffect<const T>(\n\tcompute: (prev: T | undefined) => T,\n\tapply: EffectFunction<NoInfer<T>, T>,\n\toptions?: EffectOptions,\n): void {\n\tcreateEffect(\n\t\tcompute,\n\t\t((value, prev) => {\n\t\t\ttry {\n\t\t\t\treturn apply(value, prev);\n\t\t\t} catch (error) {\n\t\t\t\treportEffectFailure(error, options?.name);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}) as EffectFunction<NoInfer<T>, T>,\n\t\toptions,\n\t);\n}\n\n/**\n * Surfaces a contained failure without rethrowing it.\n *\n * `reportError` is the channel the platform already routes to\n * `window.onerror`, so an error monitor on the page hears this exactly as it\n * would have heard the uncaught throw — the difference is only that the\n * reactive system is still running afterwards. It is not in older Safari, so\n * the console is the fallback rather than the primary.\n */\nfunction reportEffectFailure(error: unknown, name: string | undefined): void {\n\tconst where = name ? ` in effect \"${name}\"` : \"\";\n\tconsole.error(\n\t\t`[brytdesigns] contained a failure${where}. The page keeps working; this would otherwise have halted every component on it.`,\n\t\terror,\n\t);\n\tif (typeof reportError === \"function\") reportError(error);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,oBACf,SACA,OACA,SACO;CACP,aACC,WACE,OAAO,SAAS;EACjB,IAAI;GACH,OAAO,MAAM,OAAO,IAAI;EACzB,SAAS,OAAO;GACf,oBAAoB,OAAO,SAAS,IAAI;GACxC;EACD;CACD,IACA,OACD;AACD;;;;;;;;;;AAWA,SAAS,oBAAoB,OAAgB,MAAgC;CAC5E,MAAM,QAAQ,OAAO,eAAe,KAAK,KAAK;CAC9C,QAAQ,MACP,oCAAoC,MAAM,oFAC1C,KACD;CACA,IAAI,OAAO,gBAAgB,YAAY,YAAY,KAAK;AACzD"}