@weasel-js/forge 1.13.0-rc.0 → 1.14.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 (57) hide show
  1. package/README.md +1 -1
  2. package/dist/{FrameController-Tv3jrHyT.d.ts → FrameController-BM6xuEfY.d.ts} +18 -10
  3. package/dist/{chunk-KEQBGDUZ.js → chunk-B26DWEYT.js} +3 -3
  4. package/dist/{chunk-KEQBGDUZ.js.map → chunk-B26DWEYT.js.map} +1 -1
  5. package/dist/{chunk-3BAM6M26.js → chunk-FPF2JSWB.js} +3 -3
  6. package/dist/chunk-FPF2JSWB.js.map +1 -0
  7. package/dist/{chunk-ORMMZ5PV.js → chunk-MRYXJTDO.js} +7 -3
  8. package/dist/chunk-MRYXJTDO.js.map +1 -0
  9. package/dist/{chunk-35DOPXHZ.js → chunk-PGLOYXDP.js} +193 -190
  10. package/dist/chunk-PGLOYXDP.js.map +1 -0
  11. package/dist/{chunk-BUXUWAI4.js → chunk-PPB7VP6R.js} +62 -8
  12. package/dist/chunk-PPB7VP6R.js.map +1 -0
  13. package/dist/{chunk-STF6NQ5N.js → chunk-RVJHUY7W.js} +2 -2
  14. package/dist/chunk-RVJHUY7W.js.map +1 -0
  15. package/dist/{chunk-LTTEILXY.js → chunk-SFKDHXVE.js} +26 -4
  16. package/dist/chunk-SFKDHXVE.js.map +1 -0
  17. package/dist/chunk-STK2AZH2.js +11 -0
  18. package/dist/chunk-STK2AZH2.js.map +1 -0
  19. package/dist/chunk-TT4ZOYB6.js +143 -0
  20. package/dist/chunk-TT4ZOYB6.js.map +1 -0
  21. package/dist/{chunk-RKQ277P5.js → chunk-UDEUT7AG.js} +125 -80
  22. package/dist/chunk-UDEUT7AG.js.map +1 -0
  23. package/dist/{config-Bd6FVlh7.d.ts → config-DOjWyIui.d.ts} +3 -21
  24. package/dist/csf/preview-api.js +2 -2
  25. package/dist/frame/index.css +0 -2
  26. package/dist/frame/index.css.map +1 -1
  27. package/dist/frame/index.d.ts +14 -8
  28. package/dist/frame/index.js +114 -79
  29. package/dist/frame/index.js.map +1 -1
  30. package/dist/index.d.ts +12 -5
  31. package/dist/index.js +4 -4
  32. package/dist/library-CdTj-hFp.d.ts +21 -0
  33. package/dist/{mountFrame-FdJDb3TB.d.ts → mountFrame-CRPAjg1M.d.ts} +1 -1
  34. package/dist/shell/index.css +166 -150
  35. package/dist/shell/index.css.map +1 -1
  36. package/dist/shell/index.d.ts +24 -5
  37. package/dist/shell/index.js +594 -402
  38. package/dist/shell/index.js.map +1 -1
  39. package/dist/test/index.css +2 -3
  40. package/dist/test/index.css.map +1 -1
  41. package/dist/test/index.d.ts +1 -1
  42. package/dist/test/index.js +8 -10
  43. package/dist/test/index.js.map +1 -1
  44. package/dist/vite/index.js +127 -32
  45. package/dist/vite/index.js.map +1 -1
  46. package/package.json +11 -9
  47. package/dist/chunk-35DOPXHZ.js.map +0 -1
  48. package/dist/chunk-3BAM6M26.js.map +0 -1
  49. package/dist/chunk-BUXUWAI4.js.map +0 -1
  50. package/dist/chunk-GH5RE5X5.js +0 -10
  51. package/dist/chunk-GH5RE5X5.js.map +0 -1
  52. package/dist/chunk-L5XM5VKB.js +0 -47
  53. package/dist/chunk-L5XM5VKB.js.map +0 -1
  54. package/dist/chunk-LTTEILXY.js.map +0 -1
  55. package/dist/chunk-ORMMZ5PV.js.map +0 -1
  56. package/dist/chunk-RKQ277P5.js.map +0 -1
  57. package/dist/chunk-STF6NQ5N.js.map +0 -1
@@ -1,28 +1,145 @@
1
- import { isNative, ClockContext } from './chunk-L5XM5VKB.js';
1
+ import { clockSpecOf, isNative, ClockContext } from './chunk-TT4ZOYB6.js';
2
2
  import { isSafeOverride, storyNameFromExport, storyId, PROP_ARG_TYPES_EXPORT } from './chunk-5WS67HMU.js';
3
- import { portSafePart, appliedLocal, withUnsent, coverOf, ArgsContext } from './chunk-KEQBGDUZ.js';
4
- import { useVisibleRaf, useLatest, isPlainObject } from '@weasel-js/core';
5
- import { createContext, useSyncExternalStore, useRef, useEffect, useState, Component, useLayoutEffect, useContext, createElement } from 'react';
6
- import { createEmitter } from '@weasel-js/registry';
3
+ import { portSafePart, appliedLocal, withUnsent, coverOf, ArgsContext } from './chunk-B26DWEYT.js';
4
+ import { DEFAULT_LAYOUT } from './chunk-STK2AZH2.js';
5
+ import { createContext, useState, useRef, useEffect, Component, useLayoutEffect, useContext, createElement } from 'react';
6
+ import { createClock, createClockRegistry } from '@weasel-js/clock';
7
+ import { useClockLoop } from '@weasel-js/clock/react';
8
+ import { useLatest, isPlainObject } from '@weasel-js/core';
7
9
  import { jsx } from 'react/jsx-runtime';
8
10
  import { f, UnionNode, ObjectNode } from '@weasel-js/labkit/config';
9
11
  import { iconFromData } from '@weasel-js/ui/components/Icon';
10
12
 
13
+ // src/frame/a11yContrast.ts
14
+ var RECHECKED = /* @__PURE__ */ new Set(["bgOverlap", "pseudoContent", "elmPartiallyObscured", "elmPartiallyObscuring"]);
15
+ var undecided = (key) => ({ decided: false, key });
16
+ function pseudoArea(el, which) {
17
+ const style = getComputedStyle(el, which);
18
+ if (style.content === "none" || style.display === "none" || style.visibility === "hidden" || style.position !== "absolute") return 0;
19
+ if (style.backgroundColor === "rgba(0, 0, 0, 0)" && style.backgroundImage === "none") return 0;
20
+ const width = parseFloat(style.width);
21
+ const height = parseFloat(style.height);
22
+ if (!style.width.endsWith("px") || !style.height.endsWith("px")) return width === 0 || height === 0 ? 0 : Infinity;
23
+ return width * height;
24
+ }
25
+ function hasPseudoBackdrop(node, root) {
26
+ const rect = node.getBoundingClientRect();
27
+ const minimum = rect.width * rect.height * 0.25;
28
+ for (let el = node; el; el = el.parentElement) {
29
+ if (pseudoArea(el, "::before") + pseudoArea(el, "::after") > minimum) return true;
30
+ if (el === root) return false;
31
+ }
32
+ return false;
33
+ }
34
+ function hasCompositing(node) {
35
+ for (let el = node; el; el = el.parentElement) {
36
+ const style = getComputedStyle(el);
37
+ if (Number(style.opacity) < 1 || style.filter !== "none" || style.mixBlendMode !== "normal") return true;
38
+ if (style.backdropFilter && style.backdropFilter !== "none") return true;
39
+ }
40
+ return false;
41
+ }
42
+ var isChrome = (el, root) => !root.contains(el) && !el.contains(root);
43
+ var paints = (style) => style.backgroundImage !== "none" || style.backgroundColor !== "rgba(0, 0, 0, 0)";
44
+ function stacksWithoutChrome(axe, node, root) {
45
+ const stacks = [];
46
+ for (const stack of axe.commons.dom.getTextElementStack(node)) {
47
+ const at = stack.indexOf(node);
48
+ const above = at < 0 ? stack : stack.slice(0, at);
49
+ if (above.some((el) => isChrome(el, root) && paints(getComputedStyle(el)))) return null;
50
+ const kept = [...above.filter((el) => !isChrome(el, root)), ...at < 0 ? [] : stack.slice(at)];
51
+ stacks.push(axe.commons.dom.reduceToElementsBelowFloating(kept, node));
52
+ }
53
+ return stacks;
54
+ }
55
+ function covers(el, style, rects) {
56
+ const box = el.getBoundingClientRect();
57
+ const scrolls = ["scroll", "auto"].includes(style.overflow);
58
+ const right = scrolls ? box.left + el.scrollWidth : box.right;
59
+ const bottom = scrolls ? box.top + el.scrollHeight : box.bottom;
60
+ return rects.every((r) => r.top >= box.top && r.bottom <= bottom && r.left >= box.left && r.right <= right);
61
+ }
62
+ function ownText(node) {
63
+ let text = "";
64
+ for (const child of node.childNodes) if (child.nodeType === Node.TEXT_NODE) text += child.textContent ?? "";
65
+ return text.trim();
66
+ }
67
+ function textRects(node) {
68
+ const range = document.createRange();
69
+ const rects = [];
70
+ for (const child of node.childNodes) {
71
+ if (child.nodeType !== Node.TEXT_NODE || !child.textContent?.trim()) continue;
72
+ range.selectNodeContents(child);
73
+ rects.push(...range.getClientRects());
74
+ }
75
+ return rects;
76
+ }
77
+ function judgeContrast(axe, node, root) {
78
+ const { Color, flattenColors, hasValidContrastRatio } = axe.commons.color;
79
+ const style = getComputedStyle(node);
80
+ if (hasPseudoBackdrop(node, root)) return undecided("pseudoContent");
81
+ if (style.textShadow !== "none") return undecided("complexTextShadows");
82
+ if (hasCompositing(node) || parseFloat(style.getPropertyValue("-webkit-text-stroke-width")) > 0) return undecided("blended");
83
+ const stacks = stacksWithoutChrome(axe, node, root);
84
+ if (!stacks?.length) return undecided("bgOverlap");
85
+ for (const stack of stacks) {
86
+ if (stack[0] !== node) return undecided("bgOverlap");
87
+ if (stack.length !== stacks[0].length || stack.some((el, i) => el !== stacks[0][i])) return undecided("elmPartiallyObscuring");
88
+ }
89
+ const rects = textRects(node);
90
+ const layers = [];
91
+ for (const el of stacks[0]) {
92
+ const elStyle = getComputedStyle(el);
93
+ if (elStyle.backgroundImage !== "none") return undecided(elStyle.backgroundImage.includes("gradient") ? "bgGradient" : "bgImage");
94
+ if (el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof SVGElement) return undecided("imgNode");
95
+ const color = new Color().parseString(elStyle.backgroundColor);
96
+ if (color.alpha === 0) continue;
97
+ if (elStyle.display !== "inline" && !covers(el, elStyle, rects)) return undecided("elmPartiallyObscured");
98
+ layers.push(color);
99
+ if (color.alpha === 1) break;
100
+ }
101
+ const white = new Color().parseString("#fff");
102
+ const bg = layers.reduceRight((backdrop, layer) => flattenColors(layer, backdrop), white);
103
+ const fg = new Color().parseString(style.getPropertyValue("-webkit-text-fill-color") || style.color);
104
+ const bold = parseFloat(style.fontWeight) >= 700 || style.fontWeight === "bold";
105
+ const verdict = hasValidContrastRatio(bg, fg, parseFloat(style.fontSize), bold);
106
+ if (Math.floor(verdict.contrastRatio * 100) === 100) return undecided("equalRatio");
107
+ if (!verdict.isValid && ownText(node).length === 1) return undecided("shortTextContent");
108
+ return {
109
+ decided: true,
110
+ isValid: verdict.isValid,
111
+ ratio: verdict.contrastRatio,
112
+ expected: verdict.expectedContrastRatio,
113
+ fg: fg.toHexString(),
114
+ bg: bg.toHexString()
115
+ };
116
+ }
117
+
11
118
  // src/frame/a11y.ts
12
119
  var IMPACTS = /* @__PURE__ */ new Set(["minor", "moderate", "serious", "critical"]);
13
- function finding(from) {
120
+ function reasonOf(node) {
121
+ if (node.reason) return node.reason;
122
+ const checks = [...node.any ?? [], ...node.all ?? [], ...node.none ?? []];
123
+ const said = checks.find((c) => c.data?.messageKey) ?? checks.find((c) => c.message);
124
+ if (!said) return void 0;
125
+ const key = said.data?.messageKey;
126
+ return { ...key ? { key } : {}, ...said.message ? { message: said.message } : {} };
127
+ }
128
+ function finding(from, withReasons = false) {
14
129
  const impact = from.impact ?? "";
130
+ const reasons = withReasons ? from.nodes.map(reasonOf) : [];
15
131
  return {
16
132
  id: from.id,
17
133
  impact: IMPACTS.has(impact) ? impact : null,
18
134
  help: from.help,
19
135
  helpUrl: from.helpUrl,
20
136
  description: from.description,
21
- nodes: from.nodes.map((node) => ({
137
+ nodes: from.nodes.map((node, i) => ({
22
138
  // An axe target entry is a selector, or an array of them for an element inside a shadow root.
23
139
  target: node.target.map((part) => Array.isArray(part) ? part.join(" ") : String(part)),
24
140
  html: node.html,
25
- ...node.failureSummary === void 0 ? {} : { failureSummary: node.failureSummary }
141
+ ...node.failureSummary === void 0 ? {} : { failureSummary: node.failureSummary },
142
+ ...reasons[i] ? { reason: reasons[i] } : {}
26
143
  }))
27
144
  };
28
145
  }
@@ -45,17 +162,49 @@ var last = Promise.resolve();
45
162
  async function runAxe(element, { page = true } = {}) {
46
163
  const axe = await loadAxe();
47
164
  const rules = page ? void 0 : Object.fromEntries(PAGE_RULES.map((id) => [id, { enabled: false }]));
48
- const run = () => axe.run(element, rules ? { rules } : void 0);
165
+ const run = async () => recheckContrast(axe, element, await axe.run(element, { elementRef: true, ...rules ? { rules } : {} }));
49
166
  const running = last.then(run, run);
50
167
  last = running;
51
168
  const result = await running;
52
169
  return {
53
- violations: result.violations.map(finding),
54
- incomplete: result.incomplete.map(finding),
170
+ violations: result.violations.map((f3) => finding(f3)),
171
+ incomplete: result.incomplete.map((f3) => finding(f3, true)),
55
172
  passes: result.passes.length,
56
173
  inapplicable: result.inapplicable.length
57
174
  };
58
175
  }
176
+ function recheckContrast(axe, root, result) {
177
+ const review = result.incomplete.find((f3) => f3.id === "color-contrast");
178
+ const rechecked = (n) => n.element !== void 0 && RECHECKED.has(reasonOf(n)?.key ?? "");
179
+ if (!review?.nodes.some(rechecked)) return result;
180
+ const kept = [];
181
+ const failed = [];
182
+ let passed = false;
183
+ axe.setup(document);
184
+ try {
185
+ for (const node of review.nodes) {
186
+ const verdict = rechecked(node) ? judgeContrast(axe, node.element, root) : null;
187
+ if (!verdict) kept.push(node);
188
+ else if (!verdict.decided) kept.push({ ...node, failureSummary: void 0, reason: { key: verdict.key } });
189
+ else if (verdict.isValid) passed = true;
190
+ else {
191
+ const summary = `Contrast is ${verdict.ratio.toFixed(2)}:1 (foreground ${verdict.fg}, background ${verdict.bg}); ${verdict.expected}:1 is needed.`;
192
+ failed.push({ ...node, failureSummary: summary });
193
+ }
194
+ }
195
+ } finally {
196
+ axe.teardown();
197
+ }
198
+ const others = (list) => list.filter((f3) => f3.id !== "color-contrast");
199
+ const failing = result.violations.find((f3) => f3.id === "color-contrast");
200
+ const violations = failed.length === 0 ? result.violations : [
201
+ ...others(result.violations),
202
+ { ...review, impact: "serious", ...failing, nodes: [...failing?.nodes ?? [], ...failed] }
203
+ ];
204
+ const incomplete = kept.length === 0 ? others(result.incomplete) : result.incomplete.map((f3) => f3 === review ? { ...f3, nodes: kept } : f3);
205
+ const passes = passed && !result.passes.some((p) => p.id === "color-contrast") ? [...result.passes, { id: "color-contrast" }] : result.passes;
206
+ return { ...result, violations, incomplete, passes };
207
+ }
59
208
  async function auditElement(element, options) {
60
209
  try {
61
210
  return { ok: true, report: await runAxe(element, options) };
@@ -332,138 +481,32 @@ function createGlobalsTarget(root, scope) {
332
481
  }
333
482
  };
334
483
  }
335
- var NEVER = () => () => {
336
- };
337
- var notPlaying = () => false;
338
- function useClockLoop(clock, target) {
339
- const playing = useSyncExternalStore(clock?.subscribe ?? NEVER, clock ? () => clock.get().playing : notPlaying);
340
- const last2 = useRef(null);
341
- const raf = useVisibleRaf(
342
- (now, raf2) => {
343
- if (!clock) return;
344
- const prev = last2.current;
345
- last2.current = now;
346
- if (prev !== null) clock.tick(now - prev);
347
- if (clock.get().playing) raf2.request();
348
- },
349
- {
350
- ...target ? { target } : {},
351
- onResume: () => {
352
- last2.current = null;
353
- }
354
- }
355
- );
356
- useEffect(() => {
357
- if (!playing) return;
358
- last2.current = null;
359
- raf.request();
360
- return () => raf.cancel();
361
- }, [playing, raf]);
362
- }
363
- function spanOf(spec) {
364
- const start = Number.isFinite(spec.start) ? spec.start : 0;
365
- const duration = Number.isFinite(spec.duration) && spec.duration > 0 ? spec.duration : 0;
366
- return { start, end: start + duration };
367
- }
368
- function clampTime(span, t) {
369
- if (Number.isNaN(t)) return span.start;
370
- return Math.min(span.end, Math.max(span.start, t));
371
- }
372
- function advance(state, elapsed) {
373
- if (!state.playing) return state;
374
- const { start, end } = state.span;
375
- const length = end - start;
376
- if (length <= 0) return { ...state, time: start, playing: false };
377
- const next = state.time + elapsed * state.rate;
378
- if (next <= end) return { ...state, time: next };
379
- if (!state.loop) return { ...state, time: end, playing: false };
380
- return { ...state, time: start + (next - start) % length };
381
- }
382
- var validRate = (rate) => typeof rate === "number" && Number.isFinite(rate) && rate > 0;
383
- var placed = (span, requested) => requested === null ? span.start : clampTime(span, requested);
384
- function createClock(spec, initial) {
385
- let declared = spanOf(spec);
386
- let override = null;
387
- let requested = { time: initial ?? null };
388
- let state = {
389
- time: placed(declared, requested.time),
390
- playing: false,
391
- loop: spec.loop ?? true,
392
- rate: validRate(spec.rate) ? spec.rate : 1,
393
- span: declared
394
- };
395
- const changed = createEmitter();
396
- const pendingIn = (at) => requested && requested.time !== null && requested.time !== at.time ? requested.time : null;
397
- let pending = pendingIn(state);
398
- const set = (next) => {
399
- const nextPending = pendingIn(next);
400
- if (nextPending === pending && next.time === state.time && next.playing === state.playing && next.loop === state.loop && next.rate === state.rate && next.span.start === state.span.start && next.span.end === state.span.end)
401
- return;
402
- state = next;
403
- pending = nextPending;
404
- changed.emit();
405
- };
406
- const moved = (next) => {
407
- requested = null;
408
- set(next);
409
- };
410
- const respan = () => {
411
- const span = override ?? declared;
412
- set({ ...state, span, time: requested ? placed(span, requested.time) : clampTime(span, state.time) });
413
- };
414
- return {
415
- get: () => state,
416
- subscribe: changed.subscribe,
417
- play() {
418
- if (state.playing) return;
419
- moved({ ...state, playing: true, time: state.time >= state.span.end ? state.span.start : state.time });
420
- },
421
- pause: () => set({ ...state, playing: false }),
422
- seek: (time) => moved({ ...state, time: clampTime(state.span, time) }),
423
- request(time) {
424
- requested = { time: Number.isNaN(time) ? null : time };
425
- set({ ...state, playing: false, time: placed(state.span, requested.time) });
426
- },
427
- pending: () => pending,
428
- setLoop: (loop) => set({ ...state, loop }),
429
- setRate(rate) {
430
- if (validRate(rate)) set({ ...state, rate });
431
- },
432
- setSpan(span) {
433
- override = span ? spanOf(span) : null;
434
- respan();
435
- },
436
- declare(next) {
437
- declared = spanOf(next);
438
- respan();
439
- },
440
- tick(elapsed) {
441
- if (state.playing) moved(advance(state, elapsed));
442
- }
443
- };
444
- }
445
- function useStoryClock(spec, initial) {
446
- const [clock, setClock] = useState(() => spec ? createClock(spec, initial()) : null);
447
- let current = clock;
448
- if (spec && !clock) {
449
- current = createClock(spec, initial());
450
- setClock(current);
451
- } else if (!spec && clock) {
484
+ var keyOf = (spec) => spec ? `${spec.start ?? 0}|${spec.duration}|${spec.loop ?? true}|${spec.rate ?? 1}` : null;
485
+ function useOwnClock(spec) {
486
+ const [handle, setHandle] = useState(() => spec ? createClock(clockSpecOf(spec)) : null);
487
+ let current = handle;
488
+ if (spec && !handle) {
489
+ current = createClock(clockSpecOf(spec));
490
+ setHandle(current);
491
+ } else if (!spec && handle) {
452
492
  current = null;
453
- setClock(null);
493
+ setHandle(null);
454
494
  }
455
495
  const latest = useLatest(spec);
456
- const spanKey = spec ? `${spec.start ?? 0}:${spec.duration}` : null;
496
+ const key = keyOf(spec);
497
+ const declared = useRef(key);
457
498
  useEffect(() => {
458
- if (current && latest.current) current.declare(latest.current);
459
- }, [current, spanKey, latest]);
499
+ if (!current || !latest.current || key === declared.current) return;
500
+ declared.current = key;
501
+ current.declare(clockSpecOf(latest.current));
502
+ }, [current, key, latest]);
460
503
  return current;
461
504
  }
462
- var NO_INITIAL = () => null;
463
505
  function OwnClock({ spec, children }) {
464
- const clock = useStoryClock(spec, NO_INITIAL);
465
- useClockLoop(clock);
466
- return /* @__PURE__ */ jsx(ClockContext.Provider, { value: clock, children });
506
+ const handle = useOwnClock(spec);
507
+ const [registry] = useState(() => createClockRegistry(handle));
508
+ useClockLoop(registry);
509
+ return /* @__PURE__ */ jsx(ClockContext.Provider, { value: handle, children });
467
510
  }
468
511
  function WithStoryClock({ story, config, children }) {
469
512
  const outer = useContext(ClockContext);
@@ -811,7 +854,7 @@ function loadCsfModule(mod, title, projectParameters = {}) {
811
854
  return scoped(ctx, (context) => renderFn(context.args, context));
812
855
  },
813
856
  decorators: [...asArray(spec.decorators), ...asArray(meta.decorators)].map(adapt),
814
- layout: layout && LAYOUTS.includes(layout) ? layout : "padded",
857
+ layout: layout && LAYOUTS.includes(layout) ? layout : DEFAULT_LAYOUT,
815
858
  viewport: resolveViewport(parameters, storyGlobals),
816
859
  isolate: typeof isolate === "string" ? isolate : null,
817
860
  play: play ? ({ canvasElement, config: config2, globals }) => play({ ...csfContext(config2, globals), canvasElement, step: async (_label, fn) => fn() }) : null,
@@ -835,7 +878,7 @@ function loadNativeModule(mod, title) {
835
878
  initialState: spec.state ?? null,
836
879
  render: spec.render,
837
880
  decorators: [...spec.decorators ?? [], ...metaSpec.decorators ?? []],
838
- layout: spec.layout ?? metaSpec.layout ?? "centered",
881
+ layout: spec.layout ?? metaSpec.layout ?? DEFAULT_LAYOUT,
839
882
  viewport: spec.viewport ?? null,
840
883
  isolate: spec.isolate ?? metaSpec.isolate ?? null,
841
884
  play: spec.play ?? null,
@@ -854,50 +897,12 @@ function indexRenderOf(mod) {
854
897
  const render = isNative(meta, "meta") ? meta?.index : meta?.parameters?.forge?.index;
855
898
  return typeof render === "function" ? render : null;
856
899
  }
857
- var IN_VIEW_MARGIN = "50%";
858
- function useNearViewport(ref, margin, initial = true) {
859
- const [near, setNear] = useState(() => initial || typeof IntersectionObserver === "undefined");
860
- useEffect(() => {
861
- const el = ref.current;
862
- if (!el || typeof IntersectionObserver === "undefined") return;
863
- const recheck = () => {
864
- observer.unobserve(el);
865
- observer.observe(el);
866
- };
867
- let watching = false;
868
- const watch = (on) => {
869
- if (on === watching) return;
870
- watching = on;
871
- if (on) {
872
- document.addEventListener("scroll", recheck, { capture: true, passive: true });
873
- window.addEventListener("resize", recheck);
874
- } else {
875
- document.removeEventListener("scroll", recheck, { capture: true });
876
- window.removeEventListener("resize", recheck);
877
- }
878
- };
879
- const observer = new IntersectionObserver(
880
- (entries) => {
881
- const last2 = entries.at(-1);
882
- if (!last2) return;
883
- const root = last2.rootBounds;
884
- const box = last2.boundingClientRect;
885
- setNear(
886
- root ? box.bottom >= root.top && box.top <= root.bottom && box.right >= root.left && box.left <= root.right : last2.isIntersecting
887
- );
888
- watch(Boolean(root) && !last2.isIntersecting);
889
- },
890
- { root: document, rootMargin: margin }
891
- );
892
- observer.observe(el);
893
- return () => {
894
- observer.disconnect();
895
- watch(false);
896
- };
897
- }, [ref, margin]);
898
- return near;
899
- }
900
900
  var scopeOf = (id) => `[data-fg-host="${id}"]`;
901
+ function applyGlobalsTo(host, hostId, globals, setup) {
902
+ const target = createGlobalsTarget(host, scopeOf(hostId));
903
+ setup.applyGlobals?.(globals, target);
904
+ return () => target.dispose();
905
+ }
901
906
  function useHostConditions(host, opts) {
902
907
  const { hostId, globals, overrides: persisted, setup } = opts;
903
908
  const latest = useLatest({ setup });
@@ -923,15 +928,13 @@ function useHostConditions(host, opts) {
923
928
  for (const [name, value] of Object.entries(persisted)) overrides.set(name, value);
924
929
  applied.current = { ...persisted };
925
930
  }, [host, hostId, persisted]);
926
- useLayoutEffect(() => {
927
- if (!host) return;
928
- const target = createGlobalsTarget(host, scopeOf(hostId));
929
- latest.current.setup.applyGlobals?.(globals, target);
930
- return () => target.dispose();
931
- }, [host, hostId, globals, latest]);
931
+ useLayoutEffect(
932
+ () => host ? applyGlobalsTo(host, hostId, globals, latest.current.setup) : void 0,
933
+ [host, hostId, globals, latest]
934
+ );
932
935
  return overridesRef;
933
936
  }
934
937
 
935
- export { IN_VIEW_MARGIN, StoryHost, auditElement, createA11yHighlighter, createGlobalsTarget, createOverrides, cssRulesOf, indexRenderOf, loadStories, resolveCssVar, runAxe, scanCssVars, useClockLoop, useHostConditions, useNearViewport, useStoryClock, varRefs };
936
- //# sourceMappingURL=chunk-35DOPXHZ.js.map
937
- //# sourceMappingURL=chunk-35DOPXHZ.js.map
938
+ export { StoryHost, applyGlobalsTo, auditElement, createA11yHighlighter, createGlobalsTarget, createOverrides, cssRulesOf, indexRenderOf, loadStories, resolveCssVar, runAxe, scanCssVars, useHostConditions, useOwnClock, varRefs };
939
+ //# sourceMappingURL=chunk-PGLOYXDP.js.map
940
+ //# sourceMappingURL=chunk-PGLOYXDP.js.map