@kerfjs/ui 5.0.0-beta.92 → 5.0.0-beta.93
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/README.md +5 -1
- package/ai/public-api-signatures-v1.md +41 -8
- package/ai/skill.md +5 -1
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/catalog-resources.d.ts +1 -1
- package/dist/catalog.d.ts +30 -4
- package/dist/catalog.js +46 -3
- package/dist/catalog.js.map +1 -1
- package/dist/chunk-6C6DLP3W.js +31 -0
- package/dist/chunk-6C6DLP3W.js.map +1 -0
- package/dist/{chunk-H45DAZBL.js → chunk-KQDU7Z7P.js} +76 -23
- package/dist/chunk-KQDU7Z7P.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{types-oLe_N1w0.d.ts → types-BNP4gA41.d.ts} +18 -1
- package/dist/wire-catalog.d.ts +9 -2
- package/dist/wire-catalog.js +39 -5
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-tab-bars.js +1 -1
- package/docs/catalog.md +51 -0
- package/docs/component-contract.md +6 -0
- package/package.json +7 -1
- package/vite-ux-review.d.mts +44 -0
- package/vite-ux-review.mjs +65 -0
- package/dist/chunk-H45DAZBL.js.map +0 -1
|
@@ -65,17 +65,41 @@ function revealInStrip(tab) {
|
|
|
65
65
|
else if (box.right > bounds.right)
|
|
66
66
|
strip.scrollLeft += Math.min(box.right - bounds.right, box.left - left);
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
var TAB_BAR = '[data-component="tab-bar"]';
|
|
69
|
+
var TAB_LIST = "[data-kui-tab-list]";
|
|
70
|
+
function selectedTabs(scopes) {
|
|
69
71
|
const selected = /* @__PURE__ */ new Map();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
for (const scope of scopes)
|
|
73
|
+
scope.querySelectorAll(
|
|
74
|
+
`${TAB_LIST} [role="tab"][aria-selected="true"]`
|
|
75
|
+
).forEach((button) => {
|
|
76
|
+
const tab = tabRoot(button);
|
|
77
|
+
const bar = tab && tabBar(tab);
|
|
78
|
+
if (bar && !selected.has(bar)) selected.set(bar, button);
|
|
79
|
+
});
|
|
77
80
|
return selected;
|
|
78
81
|
}
|
|
82
|
+
function selfAndBelow(node, selector) {
|
|
83
|
+
if (!(node instanceof Element)) return [];
|
|
84
|
+
const found = [...node.querySelectorAll(selector)];
|
|
85
|
+
if (node.matches(selector)) found.unshift(node);
|
|
86
|
+
return found;
|
|
87
|
+
}
|
|
88
|
+
function touchedBars(records) {
|
|
89
|
+
const bars = /* @__PURE__ */ new Set();
|
|
90
|
+
for (const record of records) {
|
|
91
|
+
const target = record.target instanceof Element ? record.target : record.target.parentElement;
|
|
92
|
+
const owner = target?.closest(TAB_BAR);
|
|
93
|
+
if (owner) bars.add(owner);
|
|
94
|
+
else if (record.attributeName === "dir" && target)
|
|
95
|
+
for (const bar of selfAndBelow(target, TAB_BAR))
|
|
96
|
+
bars.add(bar);
|
|
97
|
+
record.addedNodes.forEach((node) => {
|
|
98
|
+
for (const bar of selfAndBelow(node, TAB_BAR)) bars.add(bar);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return bars;
|
|
102
|
+
}
|
|
79
103
|
function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
80
104
|
const ownerDocument = root.nodeType === 9 ? root : root.ownerDocument;
|
|
81
105
|
const view = ownerDocument.defaultView;
|
|
@@ -92,11 +116,25 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
92
116
|
if (!disposed) syncSnapInRoot();
|
|
93
117
|
});
|
|
94
118
|
};
|
|
95
|
-
const
|
|
119
|
+
const pinnedObserved = /* @__PURE__ */ new Set();
|
|
120
|
+
const resizeObserver = view.ResizeObserver ? new view.ResizeObserver((entries) => {
|
|
121
|
+
if (disposed) return;
|
|
122
|
+
let snap = false;
|
|
123
|
+
for (const { target } of entries) {
|
|
124
|
+
if (pinnedObserved.has(target))
|
|
125
|
+
syncPinned(target);
|
|
126
|
+
if (snapObserved.has(target)) snap = true;
|
|
127
|
+
}
|
|
128
|
+
if (snap) scheduleSnapSync();
|
|
129
|
+
}) : void 0;
|
|
130
|
+
const observe = (element) => {
|
|
131
|
+
if (!snapObserved.has(element) && !pinnedObserved.has(element))
|
|
132
|
+
resizeObserver?.observe(element);
|
|
133
|
+
};
|
|
96
134
|
const observeSnap = (element) => {
|
|
97
135
|
if (snapObserved.has(element)) return;
|
|
136
|
+
observe(element);
|
|
98
137
|
snapObserved.add(element);
|
|
99
|
-
snapResizeObserver?.observe(element);
|
|
100
138
|
};
|
|
101
139
|
const syncSnap = (strip) => {
|
|
102
140
|
const previousExtra = snapExtras.get(strip) ?? 0;
|
|
@@ -140,10 +178,11 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
140
178
|
snapExtras.set(strip, extra);
|
|
141
179
|
}
|
|
142
180
|
};
|
|
143
|
-
const
|
|
144
|
-
for (const element of snapObserved)
|
|
181
|
+
const releaseDetached = () => {
|
|
182
|
+
for (const element of /* @__PURE__ */ new Set([...pinnedObserved, ...snapObserved]))
|
|
145
183
|
if (!root.contains(element)) {
|
|
146
|
-
|
|
184
|
+
resizeObserver?.unobserve(element);
|
|
185
|
+
pinnedObserved.delete(element);
|
|
147
186
|
snapObserved.delete(element);
|
|
148
187
|
}
|
|
149
188
|
for (const strip of snapStrips)
|
|
@@ -152,13 +191,20 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
152
191
|
strip.style.removeProperty("--kui-tab-bar-snap-end-extra");
|
|
153
192
|
snapStrips.delete(strip);
|
|
154
193
|
}
|
|
155
|
-
|
|
194
|
+
};
|
|
195
|
+
const syncSnapInRoot = () => {
|
|
196
|
+
releaseDetached();
|
|
197
|
+
root.querySelectorAll(TAB_LIST).forEach(syncSnap);
|
|
156
198
|
};
|
|
157
199
|
const syncPinned = (strip) => {
|
|
158
200
|
const pinned = strip.querySelector(
|
|
159
201
|
':scope > .kui-app-tab[data-pinned="true"]'
|
|
160
202
|
);
|
|
161
203
|
if (!pinned) return;
|
|
204
|
+
if (!pinnedObserved.has(strip)) {
|
|
205
|
+
observe(strip);
|
|
206
|
+
pinnedObserved.add(strip);
|
|
207
|
+
}
|
|
162
208
|
const previous = pinned.style.translate ? pinnedOffsets.get(pinned) ?? 0 : 0;
|
|
163
209
|
const style = view.getComputedStyle(strip);
|
|
164
210
|
if (style.direction !== "rtl" || strip.scrollWidth <= strip.clientWidth) {
|
|
@@ -433,9 +479,9 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
433
479
|
'[data-kui-tab-list] [role="tab"][aria-selected="true"]'
|
|
434
480
|
).forEach(reveal);
|
|
435
481
|
const selectedIds = /* @__PURE__ */ new Map();
|
|
436
|
-
const recordSelection = () => {
|
|
482
|
+
const recordSelection = (scopes) => {
|
|
437
483
|
const changed = [];
|
|
438
|
-
for (const [bar, button] of selectedTabs(
|
|
484
|
+
for (const [bar, button] of selectedTabs(scopes)) {
|
|
439
485
|
const id = barId(bar);
|
|
440
486
|
const tab = tabRoot(button);
|
|
441
487
|
const selectedId = tab && tabId(tab);
|
|
@@ -446,14 +492,20 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
446
492
|
}
|
|
447
493
|
return changed;
|
|
448
494
|
};
|
|
449
|
-
recordSelection();
|
|
495
|
+
recordSelection([root]);
|
|
450
496
|
syncPinnedInRoot();
|
|
451
|
-
const selectionObserver = new view.MutationObserver(() => {
|
|
497
|
+
const selectionObserver = new view.MutationObserver((records) => {
|
|
452
498
|
if (disposed) return;
|
|
453
|
-
|
|
454
|
-
|
|
499
|
+
if (records.some((record) => record.removedNodes.length)) releaseDetached();
|
|
500
|
+
const bars = touchedBars(records);
|
|
501
|
+
if (!bars.size) return;
|
|
502
|
+
const strips = [...bars].flatMap(
|
|
503
|
+
(bar) => selfAndBelow(bar, TAB_LIST)
|
|
504
|
+
);
|
|
505
|
+
strips.forEach(syncSnap);
|
|
506
|
+
for (const button of recordSelection(bars))
|
|
455
507
|
if (ownerDocument.activeElement !== button) revealInStrip(button);
|
|
456
|
-
|
|
508
|
+
strips.forEach(syncPinned);
|
|
457
509
|
});
|
|
458
510
|
selectionObserver.observe(root, {
|
|
459
511
|
subtree: true,
|
|
@@ -465,7 +517,8 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
465
517
|
disposed = true;
|
|
466
518
|
if (snapFrame !== void 0) view.cancelAnimationFrame(snapFrame);
|
|
467
519
|
selectionObserver.disconnect();
|
|
468
|
-
|
|
520
|
+
resizeObserver?.disconnect();
|
|
521
|
+
pinnedObserved.clear();
|
|
469
522
|
for (const strip of snapStrips) {
|
|
470
523
|
strip.style.removeProperty("--kui-tab-bar-snap-inset");
|
|
471
524
|
strip.style.removeProperty("--kui-tab-bar-snap-end-extra");
|
|
@@ -488,4 +541,4 @@ function wireTabBars(root, { onReorder, activation = "automatic" }) {
|
|
|
488
541
|
|
|
489
542
|
export { reorderTabs, wireTabBars };
|
|
490
543
|
|
|
491
|
-
//# sourceMappingURL=chunk-
|
|
544
|
+
//# sourceMappingURL=chunk-KQDU7Z7P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/navigation/tab-bar/wiring/wire-tab-bars.ts"],"names":["target"],"mappings":"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","file":"chunk-KQDU7Z7P.js","sourcesContent":["import type { TabActivation } from '../tab-bar.js';\n\nexport type TabReorderSource = 'pointer' | 'keyboard';\nexport type TabDropPosition = 'before' | 'after';\n\nexport interface TabReorder {\n barId: string;\n sourceId: string;\n targetId: string;\n position: TabDropPosition;\n source: TabReorderSource;\n}\n\nexport type { TabActivation } from '../tab-bar.js';\n\nexport interface WireTabBarsOptions {\n onReorder: (change: TabReorder) => void;\n /**\n * How arrow / Home / End keys activate tabs (default `'automatic'`):\n * - `'automatic'` moves roving focus **and** selects the focused tab (clicks it).\n * - `'manual'` moves roving focus only; the user selects with Enter / Space / click\n * (the ARIA Tabs manual-activation pattern). Use this when activation is a heavy or\n * side-effecting action (e.g. a tab that loads a project) so arrowing through the\n * strip doesn't trigger it on every tab.\n *\n * A per-bar `data-tab-activation=\"manual\" | \"automatic\"` attribute (see the `TabBar`\n * `activation` prop) overrides this option for that strip.\n */\n activation?: TabActivation;\n}\n\ntype TabRoot = HTMLElement;\ntype TabBarRoot = HTMLElement;\n\nconst AUTO_SCROLL_EDGE_PX = 56;\nconst AUTO_SCROLL_MIN_PX_PER_SECOND = 180;\nconst AUTO_SCROLL_MAX_PX_PER_SECOND = 900;\n\nexport function reorderTabs<T>(\n items: readonly T[],\n getId: (item: T) => string,\n sourceId: string,\n targetId: string,\n position: TabDropPosition,\n): T[] {\n if (sourceId === targetId) return [...items];\n const source = items.find((item) => getId(item) === sourceId);\n if (!source || !items.some((item) => getId(item) === targetId))\n return [...items];\n const remaining = items.filter((item) => getId(item) !== sourceId);\n const targetIndex = remaining.findIndex((item) => getId(item) === targetId);\n remaining.splice(targetIndex + (position === 'after' ? 1 : 0), 0, source);\n return remaining;\n}\n\nfunction tabRoot(target: EventTarget | null): TabRoot | undefined {\n return target instanceof Element\n ? (target.closest<TabRoot>('[data-component=\"app-tab\"]') ?? undefined)\n : undefined;\n}\n\nfunction tabBar(tab: TabRoot): TabBarRoot | undefined {\n return tab.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ?? undefined;\n}\n\nfunction tabId(tab: TabRoot): string | undefined {\n return tab.dataset.tabId || undefined;\n}\n\nfunction barId(bar: TabBarRoot): string | undefined {\n return bar.dataset.tabBarId || undefined;\n}\n\nfunction positionFor(event: DragEvent, tab: TabRoot): TabDropPosition {\n const bounds = tab.getBoundingClientRect();\n return event.clientX < bounds.left + bounds.width / 2 ? 'before' : 'after';\n}\n\nfunction tabsIn(bar: TabBarRoot): HTMLButtonElement[] {\n return [\n ...bar.querySelectorAll<HTMLButtonElement>(\n '[data-kui-tab-list] [role=\"tab\"]',\n ),\n ];\n}\n\nfunction reveal(tab: HTMLElement | null | undefined): void {\n tab?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n if (\n tab\n ?.closest('[data-kui-tab-list]')\n ?.querySelector(':scope > .kui-app-tab[data-pinned=\"true\"]')\n )\n revealInStrip(tab);\n}\n\n/**\n * Scroll a tab's own strip, and nothing else, so the tab is fully visible.\n * Used for selection changes the application makes without focus (adding and\n * selecting a tab): unlike `scrollIntoView`, it never scrolls the page to a\n * bar that is out of view.\n */\nfunction revealInStrip(tab: HTMLElement): void {\n const strip = tab.closest<HTMLElement>('[data-kui-tab-list]');\n if (!strip || strip.scrollWidth <= strip.clientWidth) return;\n const bounds = strip.getBoundingClientRect();\n const snap = strip.dataset.snapTabs === 'true';\n const box = snap\n ? (tab.closest<HTMLElement>('.kui-app-tab')?.getBoundingClientRect() ??\n tab.getBoundingClientRect())\n : tab.getBoundingClientRect();\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n if (pinned?.contains(tab)) return;\n const pinnedBox = pinned?.getBoundingClientRect();\n if (\n strip.ownerDocument.defaultView?.getComputedStyle(strip).direction === 'rtl'\n ) {\n const right = pinnedBox\n ? Math.min(bounds.right, pinnedBox.left)\n : bounds.right;\n if (snap) strip.scrollLeft += box.right - right;\n else if (box.right > right) strip.scrollLeft += box.right - right;\n else if (box.left < bounds.left)\n strip.scrollLeft -= Math.min(bounds.left - box.left, right - box.right);\n return;\n }\n const left = pinnedBox ? Math.max(bounds.left, pinnedBox.right) : bounds.left;\n if (snap) strip.scrollLeft += box.left - left;\n else if (box.left < left) strip.scrollLeft -= left - box.left;\n else if (box.right > bounds.right)\n strip.scrollLeft += Math.min(box.right - bounds.right, box.left - left);\n}\n\nconst TAB_BAR = '[data-component=\"tab-bar\"]';\nconst TAB_LIST = '[data-kui-tab-list]';\n\n/** The selected tab button of every bar below each scope, keyed by bar. */\nfunction selectedTabs(scopes: Iterable<ParentNode>) {\n const selected = new Map<TabBarRoot, HTMLElement>();\n for (const scope of scopes)\n scope\n .querySelectorAll<HTMLElement>(\n `${TAB_LIST} [role=\"tab\"][aria-selected=\"true\"]`,\n )\n .forEach((button) => {\n const tab = tabRoot(button);\n const bar = tab && tabBar(tab);\n if (bar && !selected.has(bar)) selected.set(bar, button);\n });\n return selected;\n}\n\n/** `node` itself when it matches `selector`, plus its matching descendants. */\nfunction selfAndBelow<T extends Element>(node: Node, selector: string): T[] {\n if (!(node instanceof Element)) return [];\n const found = [...node.querySelectorAll<T>(selector)];\n if (node.matches(selector)) found.unshift(node as T);\n return found;\n}\n\n/**\n * The tab bars a batch of mutation records can have changed. A mutation\n * inside a bar touches that bar; an inserted subtree touches the bars it\n * brings; a `dir` change touches every bar below its element. Mutations\n * elsewhere in the root — terminal output, list morphs, streamed content —\n * touch nothing and cost one `closest` per record.\n */\nfunction touchedBars(records: readonly MutationRecord[]) {\n const bars = new Set<TabBarRoot>();\n for (const record of records) {\n const target =\n record.target instanceof Element\n ? record.target\n : record.target.parentElement;\n const owner = target?.closest<TabBarRoot>(TAB_BAR);\n if (owner) bars.add(owner);\n else if (record.attributeName === 'dir' && target)\n for (const bar of selfAndBelow<TabBarRoot>(target, TAB_BAR))\n bars.add(bar);\n record.addedNodes.forEach((node) => {\n for (const bar of selfAndBelow<TabBarRoot>(node, TAB_BAR)) bars.add(bar);\n });\n }\n return bars;\n}\n\n/** Wire reordering and keyboard navigation while leaving controlled state in the application. */\nexport function wireTabBars(\n root: HTMLElement | Document,\n { onReorder, activation = 'automatic' }: WireTabBarsOptions,\n): () => void {\n const ownerDocument = (\n root.nodeType === 9 ? (root as Document) : root.ownerDocument\n )!;\n const view = ownerDocument.defaultView!;\n // WebKit positions an RTL sticky flex child as though the negative scroll\n // offset were added to its inline-start inset. Measure the actual pinned\n // edge and correct only the displacement; Chromium and Firefox stay at 0.\n const pinnedOffsets = new WeakMap<HTMLElement, number>();\n const adjustedPinned = new Set<HTMLElement>();\n const snapExtras = new WeakMap<HTMLElement, number>();\n const snapStrips = new Set<HTMLElement>();\n const snapObserved = new Set<HTMLElement>();\n let snapFrame: number | undefined;\n const scheduleSnapSync = () => {\n if (disposed || snapFrame !== undefined) return;\n snapFrame = view.requestAnimationFrame(() => {\n snapFrame = undefined;\n if (!disposed) syncSnapInRoot();\n });\n };\n // A pinned strip's correction depends on its own box, which can change\n // without a mutation inside it (a sibling panel collapsing), so resizes\n // re-sync it directly; unrelated mutations elsewhere never have to.\n const pinnedObserved = new Set<HTMLElement>();\n const resizeObserver = view.ResizeObserver\n ? new view.ResizeObserver((entries: ResizeObserverEntry[]) => {\n if (disposed) return;\n let snap = false;\n for (const { target } of entries) {\n if (pinnedObserved.has(target as HTMLElement))\n syncPinned(target as HTMLElement);\n if (snapObserved.has(target as HTMLElement)) snap = true;\n }\n if (snap) scheduleSnapSync();\n })\n : undefined;\n const observe = (element: HTMLElement) => {\n if (!snapObserved.has(element) && !pinnedObserved.has(element))\n resizeObserver?.observe(element);\n };\n const observeSnap = (element: HTMLElement) => {\n if (snapObserved.has(element)) return;\n observe(element);\n snapObserved.add(element);\n };\n const syncSnap = (strip: HTMLElement) => {\n const previousExtra = snapExtras.get(strip) ?? 0;\n if (strip.dataset.snapTabs !== 'true') {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n snapExtras.delete(strip);\n snapStrips.delete(strip);\n return;\n }\n snapStrips.add(strip);\n observeSnap(strip);\n const tabs = [\n ...strip.querySelectorAll<HTMLElement>(':scope > .kui-app-tab'),\n ];\n tabs.forEach(observeSnap);\n const pinned = tabs.find(\n (candidate) => candidate.dataset.pinned === 'true',\n );\n const last = tabs.at(-1);\n const bounds = strip.getBoundingClientRect();\n const pinnedBox = pinned?.getBoundingClientRect();\n const rtl = view.getComputedStyle(strip).direction === 'rtl';\n const inset = pinnedBox\n ? rtl\n ? bounds.right - pinnedBox.left\n : pinnedBox.right - bounds.left\n : 0;\n strip.style.setProperty(\n '--kui-tab-bar-snap-inset',\n `${Math.max(0, inset)}px`,\n );\n const baseMaximum = Math.max(\n 0,\n strip.scrollWidth - strip.clientWidth - previousExtra,\n );\n let extra = 0;\n if (baseMaximum > 0 && last && last !== pinned) {\n const lastBox = last.getBoundingClientRect();\n const desired = rtl\n ? -(\n strip.scrollLeft +\n lastBox.right -\n (pinnedBox?.left ?? bounds.right)\n )\n : strip.scrollLeft + lastBox.left - (pinnedBox?.right ?? bounds.left);\n extra = Math.max(0, desired - baseMaximum);\n }\n if (Math.abs(extra - previousExtra) >= 0.5) {\n strip.style.setProperty('--kui-tab-bar-snap-end-extra', `${extra}px`);\n snapExtras.set(strip, extra);\n }\n };\n /** Release observers and inline geometry of strips that left the root. */\n const releaseDetached = () => {\n for (const element of new Set([...pinnedObserved, ...snapObserved]))\n if (!root.contains(element)) {\n resizeObserver?.unobserve(element);\n pinnedObserved.delete(element);\n snapObserved.delete(element);\n }\n for (const strip of snapStrips)\n if (!root.contains(strip)) {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n snapStrips.delete(strip);\n }\n };\n const syncSnapInRoot = () => {\n releaseDetached();\n root.querySelectorAll<HTMLElement>(TAB_LIST).forEach(syncSnap);\n };\n const syncPinned = (strip: HTMLElement) => {\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n if (!pinned) return;\n if (!pinnedObserved.has(strip)) {\n observe(strip);\n pinnedObserved.add(strip);\n }\n const previous = pinned.style.translate\n ? (pinnedOffsets.get(pinned) ?? 0)\n : 0;\n const style = view.getComputedStyle(strip);\n if (style.direction !== 'rtl' || strip.scrollWidth <= strip.clientWidth) {\n if (previous !== 0) pinned.style.translate = '';\n pinnedOffsets.set(pinned, 0);\n return;\n }\n const targetRight =\n strip.getBoundingClientRect().right -\n (Number.parseFloat(style.borderRightWidth) || 0) -\n (Number.parseFloat(style.paddingRight) || 0);\n const rawRight = pinned.getBoundingClientRect().right - previous;\n const delta = targetRight - rawRight;\n const next = Math.abs(delta) < 0.5 ? 0 : delta;\n if (next !== previous) pinned.style.translate = next ? `${next}px 0` : '';\n pinnedOffsets.set(pinned, next);\n adjustedPinned.add(pinned);\n };\n const syncPinnedInRoot = () =>\n root\n .querySelectorAll<HTMLElement>('[data-kui-tab-list]')\n .forEach(syncPinned);\n const onStripScroll = (event: Event) => {\n const target = event.target;\n if (target instanceof HTMLElement && target.matches('[data-kui-tab-list]'))\n syncPinned(target);\n };\n // CSS snap settles direct gestures in most engines. WebKit can leave a\n // programmatic smooth scroll between snap points, so normalize its final\n // position to the nearest whole peer at the pinned inset.\n const onStripScrollEnd = (event: Event) => {\n const strip = event.target;\n if (\n !(strip instanceof HTMLElement) ||\n !strip.matches('[data-kui-tab-list][data-snap-tabs=\"true\"]') ||\n Math.abs(strip.scrollLeft) < 0.5\n )\n return;\n const peers = [\n ...strip.querySelectorAll<HTMLElement>(\n ':scope > .kui-app-tab:not([data-pinned=\"true\"])',\n ),\n ];\n if (!peers.length) return;\n const bounds = strip.getBoundingClientRect();\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n const pinnedBox = pinned?.getBoundingClientRect();\n const rtl = view.getComputedStyle(strip).direction === 'rtl';\n const edge = rtl\n ? (pinnedBox?.left ?? bounds.right)\n : (pinnedBox?.right ?? bounds.left);\n const distances = peers.map((peer) => {\n const box = peer.getBoundingClientRect();\n return rtl ? box.right - edge : box.left - edge;\n });\n const closest = distances.reduce((best, distance) =>\n Math.abs(distance) < Math.abs(best) ? distance : best,\n );\n if (Math.abs(closest) > 0.5) strip.scrollLeft += closest;\n };\n let autoScroll:\n | {\n strip: HTMLElement;\n velocity: number;\n frame: number | undefined;\n previousTime: number | undefined;\n }\n | undefined;\n const stopAutoScroll = () => {\n if (!autoScroll) return;\n if (autoScroll.frame !== undefined)\n view.cancelAnimationFrame(autoScroll.frame);\n delete autoScroll.strip.dataset.tabAutoscroll;\n autoScroll = undefined;\n };\n const runAutoScroll = (time: number) => {\n const current = autoScroll;\n if (!current) return;\n current.frame = undefined;\n const elapsed =\n current.previousTime === undefined\n ? 1000 / 60\n : Math.min(40, Math.max(1, time - current.previousTime));\n current.previousTime = time;\n const maximum = Math.max(\n 0,\n current.strip.scrollWidth - current.strip.clientWidth,\n );\n const previous = current.strip.scrollLeft;\n current.strip.scrollLeft = Math.max(\n 0,\n Math.min(maximum, previous + (current.velocity * elapsed) / 1000),\n );\n if (current.strip.scrollLeft === previous) {\n stopAutoScroll();\n return;\n }\n current.frame = view.requestAnimationFrame(runAutoScroll);\n };\n const updateAutoScroll = (event: DragEvent, strip: HTMLElement) => {\n if (strip.scrollWidth <= strip.clientWidth) {\n stopAutoScroll();\n return;\n }\n const bounds = strip.getBoundingClientRect();\n const edge = Math.min(AUTO_SCROLL_EDGE_PX, bounds.width / 3);\n const startDistance = event.clientX - bounds.left;\n const endDistance = bounds.right - event.clientX;\n let direction = 0;\n let strength = 0;\n if (startDistance >= 0 && startDistance < edge) {\n direction = -1;\n strength = 1 - startDistance / edge;\n } else if (endDistance >= 0 && endDistance < edge) {\n direction = 1;\n strength = 1 - endDistance / edge;\n }\n if (direction === 0) {\n stopAutoScroll();\n return;\n }\n const maximum = Math.max(0, strip.scrollWidth - strip.clientWidth);\n if (\n (direction < 0 && strip.scrollLeft <= 0) ||\n (direction > 0 && strip.scrollLeft >= maximum)\n ) {\n stopAutoScroll();\n return;\n }\n const speed =\n AUTO_SCROLL_MIN_PX_PER_SECOND +\n (AUTO_SCROLL_MAX_PX_PER_SECOND - AUTO_SCROLL_MIN_PX_PER_SECOND) *\n strength *\n strength;\n if (autoScroll?.strip !== strip) stopAutoScroll();\n autoScroll ??= {\n strip,\n velocity: 0,\n frame: undefined,\n previousTime: undefined,\n };\n autoScroll.velocity = direction * speed;\n strip.dataset.tabAutoscroll = direction < 0 ? 'start' : 'end';\n autoScroll.frame ??= view.requestAnimationFrame(runAutoScroll);\n };\n let dragged: { barId: string; tabId: string } | undefined;\n const clearDropPositions = () =>\n root\n .querySelectorAll<HTMLElement>('[data-tab-drop-position]')\n .forEach((tab) => delete tab.dataset.tabDropPosition);\n const clear = () => {\n stopAutoScroll();\n dragged = undefined;\n root\n .querySelectorAll<HTMLElement>(\n '[data-tab-dragging], [data-tab-drop-position]',\n )\n .forEach((tab) => {\n delete tab.dataset.tabDragging;\n delete tab.dataset.tabDropPosition;\n });\n };\n let disposed = false;\n const afterControlledRender = (sourceBarId: string, sourceTabId: string) =>\n globalThis.queueMicrotask(() => {\n if (disposed) return;\n const tab = [\n ...root.querySelectorAll<TabRoot>('[data-component=\"app-tab\"]'),\n ].find((candidate) => {\n const candidateBar = tabBar(candidate);\n return (\n tabId(candidate) === sourceTabId &&\n candidateBar !== undefined &&\n barId(candidateBar) === sourceBarId\n );\n });\n const button = tab?.querySelector<HTMLButtonElement>('[role=\"tab\"]');\n button?.focus();\n reveal(button);\n });\n\n const onDragStart = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const sourceId = tab && tabId(tab);\n const sourceBarId = bar && barId(bar);\n if (\n !tab ||\n tab.getAttribute('draggable') !== 'true' ||\n !sourceId ||\n !sourceBarId\n )\n return;\n dragged = { barId: sourceBarId, tabId: sourceId };\n tab.dataset.tabDragging = 'true';\n if (dragEvent.dataTransfer) {\n dragEvent.dataTransfer.effectAllowed = 'move';\n dragEvent.dataTransfer.setData(\n 'application/x-kerf-tab',\n `${sourceBarId}:${sourceId}`,\n );\n }\n };\n const onDragOver = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar =\n event.target instanceof Element\n ? (event.target.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ??\n undefined)\n : undefined;\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (!dragged || !bar || targetBarId !== dragged.barId) {\n stopAutoScroll();\n clearDropPositions();\n return;\n }\n dragEvent.preventDefault();\n const strip = bar.querySelector<HTMLElement>('[data-kui-tab-list]');\n if (strip) updateAutoScroll(dragEvent, strip);\n else stopAutoScroll();\n clearDropPositions();\n if (!tab || !targetId || targetId === dragged.tabId) return;\n tab.dataset.tabDropPosition = positionFor(dragEvent, tab);\n if (dragEvent.dataTransfer) dragEvent.dataTransfer.dropEffect = 'move';\n };\n const onDrop = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (\n !dragged ||\n !tab ||\n !targetId ||\n targetId === dragged.tabId ||\n targetBarId !== dragged.barId\n ) {\n clear();\n return;\n }\n dragEvent.preventDefault();\n const change: TabReorder = {\n barId: dragged.barId,\n sourceId: dragged.tabId,\n targetId,\n position: positionFor(dragEvent, tab),\n source: 'pointer',\n };\n clear();\n onReorder(change);\n afterControlledRender(change.barId, change.sourceId);\n };\n const onKeyDown = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent;\n const button =\n event.target instanceof Element\n ? event.target.closest<HTMLButtonElement>('[role=\"tab\"]')\n : null;\n const tab = button && tabRoot(button);\n const bar = tab && tabBar(tab);\n if (!button || !tab || !bar) return;\n if (keyboardEvent.key === 'Delete' || keyboardEvent.key === 'Backspace') {\n const close = tab.querySelector<HTMLButtonElement>('.kui-app-tab__close');\n if (!close) return;\n keyboardEvent.preventDefault();\n close.click();\n return;\n }\n const tabs = tabsIn(bar);\n const current = tabs.indexOf(button);\n if (current < 0) return;\n if (\n keyboardEvent.altKey &&\n keyboardEvent.shiftKey &&\n (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowRight')\n ) {\n const target =\n tabs[current + (keyboardEvent.key === 'ArrowLeft' ? -1 : 1)];\n const sourceId = tabId(tab);\n const targetRoot = target && tabRoot(target);\n const targetId = targetRoot && tabId(targetRoot);\n const sourceBarId = barId(bar);\n if (\n !target ||\n !sourceId ||\n !targetId ||\n !sourceBarId ||\n tab.getAttribute('draggable') !== 'true'\n )\n return;\n keyboardEvent.preventDefault();\n onReorder({\n barId: sourceBarId,\n sourceId,\n targetId,\n position: keyboardEvent.key === 'ArrowLeft' ? 'before' : 'after',\n source: 'keyboard',\n });\n afterControlledRender(sourceBarId, sourceId);\n return;\n }\n let next: number | undefined;\n if (keyboardEvent.key === 'ArrowRight') next = (current + 1) % tabs.length;\n else if (keyboardEvent.key === 'ArrowLeft')\n next = (current - 1 + tabs.length) % tabs.length;\n else if (keyboardEvent.key === 'Home') next = 0;\n else if (keyboardEvent.key === 'End') next = tabs.length - 1;\n if (next === undefined) return;\n keyboardEvent.preventDefault();\n // A per-bar data-tab-activation attribute overrides the wireTabBars option.\n const perBar = bar.dataset.tabActivation;\n const mode: TabActivation =\n perBar === 'manual' || perBar === 'automatic' ? perBar : activation;\n const target = tabs[next]!;\n target.focus();\n // Manual activation moves roving focus only; the user selects with Enter / Space\n // (native on the tab <button>) or click. Automatic also selects the focused tab.\n if (mode === 'automatic') {\n target.click();\n // Activation can synchronously replace the controlled strip. Resolve the\n // logical tab again after rendering instead of leaving focus on a dead node.\n if (!target.isConnected)\n afterControlledRender(\n bar.dataset.tabBarId!,\n tabRoot(target)!.dataset.tabId!,\n );\n }\n reveal(target);\n };\n const onFocusIn = (event: Event) => {\n const tab =\n event.target instanceof HTMLElement &&\n event.target.matches('[role=\"tab\"]')\n ? event.target\n : undefined;\n reveal(tab);\n };\n\n root.addEventListener('dragstart', onDragStart);\n root.addEventListener('dragover', onDragOver);\n root.addEventListener('drop', onDrop);\n root.addEventListener('dragend', clear);\n root.addEventListener('keydown', onKeyDown);\n root.addEventListener('focusin', onFocusIn);\n root.addEventListener('scroll', onStripScroll, true);\n root.addEventListener('scrollend', onStripScrollEnd, true);\n syncSnapInRoot();\n root\n .querySelectorAll<HTMLElement>(\n '[data-kui-tab-list] [role=\"tab\"][aria-selected=\"true\"]',\n )\n .forEach(reveal);\n\n // Reveal a tab the application selects without focus (for example a tab it\n // adds and selects) once its controlled render lands. Only a change of a\n // bar's selected tab reveals, so a re-render that keeps the selection never\n // pulls a strip the user scrolled back to it.\n const selectedIds = new Map<string, string>();\n const recordSelection = (scopes: Iterable<ParentNode>) => {\n const changed: HTMLElement[] = [];\n for (const [bar, button] of selectedTabs(scopes)) {\n const id = barId(bar);\n const tab = tabRoot(button);\n const selectedId = tab && tabId(tab);\n if (!id || !selectedId) continue;\n if (selectedIds.has(id) && selectedIds.get(id) !== selectedId)\n changed.push(button);\n selectedIds.set(id, selectedId);\n }\n return changed;\n };\n recordSelection([root]);\n syncPinnedInRoot();\n // Only the bars the records touched are re-measured: this observer watches\n // the whole application root, so most of its batches are unrelated content.\n const selectionObserver = new view.MutationObserver((records) => {\n if (disposed) return;\n // A removal anywhere may have taken a strip with it; releasing walks only\n // the few strips and tabs this wiring tracks.\n if (records.some((record) => record.removedNodes.length)) releaseDetached();\n const bars = touchedBars(records);\n if (!bars.size) return;\n const strips = [...bars].flatMap((bar) =>\n selfAndBelow<HTMLElement>(bar, TAB_LIST),\n );\n strips.forEach(syncSnap);\n for (const button of recordSelection(bars))\n if (ownerDocument.activeElement !== button) revealInStrip(button);\n strips.forEach(syncPinned);\n });\n selectionObserver.observe(root, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-selected', 'dir', 'data-snap-tabs', 'data-pinned'],\n });\n\n return () => {\n disposed = true;\n if (snapFrame !== undefined) view.cancelAnimationFrame(snapFrame);\n selectionObserver.disconnect();\n resizeObserver?.disconnect();\n pinnedObserved.clear();\n for (const strip of snapStrips) {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n }\n snapStrips.clear();\n snapObserved.clear();\n clear();\n root.removeEventListener('dragstart', onDragStart);\n root.removeEventListener('dragover', onDragOver);\n root.removeEventListener('drop', onDrop);\n root.removeEventListener('dragend', clear);\n root.removeEventListener('keydown', onKeyDown);\n root.removeEventListener('focusin', onFocusIn);\n root.removeEventListener('scroll', onStripScroll, true);\n root.removeEventListener('scrollend', onStripScrollEnd, true);\n for (const pinned of adjustedPinned) pinned.style.translate = '';\n adjustedPinned.clear();\n };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { reorderTabs, wireTabBars } from './chunk-
|
|
1
|
+
export { reorderTabs, wireTabBars } from './chunk-KQDU7Z7P.js';
|
|
2
2
|
export { wireTokenSearchFields } from './chunk-TNG3XKHB.js';
|
|
3
3
|
export { wireToolbarVisibility } from './chunk-2GOCOLQL.js';
|
|
4
4
|
export { ValueTable, ValueTableRow } from './chunk-DL55BSQX.js';
|
|
@@ -42,6 +42,17 @@ interface CatalogBrand {
|
|
|
42
42
|
/** Logo image URL (rendered decorative). Omit for a text-only brand. */
|
|
43
43
|
logoUrl?: string;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The UX Review capture link a {@link Catalog} shows when it knows the local
|
|
47
|
+
* project directory (see `uxReviewPlugin` and `wireCatalog`'s
|
|
48
|
+
* `onUxReviewProject`).
|
|
49
|
+
*/
|
|
50
|
+
interface CatalogUxReview {
|
|
51
|
+
/** The absolute project directory UX Review files captures under. */
|
|
52
|
+
projectDirectory: string;
|
|
53
|
+
/** The catalog's name in the capture title and context (default `brand.title`). */
|
|
54
|
+
name?: string;
|
|
55
|
+
}
|
|
45
56
|
type CatalogStageRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
46
57
|
'data-catalog-stage'?: never;
|
|
47
58
|
'data-background-style'?: never;
|
|
@@ -81,6 +92,12 @@ interface CatalogProps {
|
|
|
81
92
|
/** Show the preview background chooser; `backgroundStyle` remains app-owned. */
|
|
82
93
|
backgroundControl?: boolean;
|
|
83
94
|
headerActions?: KerfUiContent;
|
|
95
|
+
/**
|
|
96
|
+
* Show a "Capture region" link to UX Review after `headerActions`, preset
|
|
97
|
+
* with the project and the active entry. Supply it once the project is known
|
|
98
|
+
* (`wireCatalog`'s `onUxReviewProject`); omit it on static hosting.
|
|
99
|
+
*/
|
|
100
|
+
uxReview?: CatalogUxReview;
|
|
84
101
|
secondarySections?: CatalogSecondaryGroup;
|
|
85
102
|
sidebarFooter?: KerfUiContent;
|
|
86
103
|
status?: KerfUiContent;
|
|
@@ -127,4 +144,4 @@ interface CatalogProps {
|
|
|
127
144
|
slot?: string;
|
|
128
145
|
}
|
|
129
146
|
|
|
130
|
-
export type { CatalogResource as C, CatalogBackgroundStyle as a, CatalogProps as b, CatalogBrand as c, CatalogEntry as d, CatalogRelated as e, CatalogSecondaryGroup as f, CatalogSection as g, CatalogStageRootAttributes as h };
|
|
147
|
+
export type { CatalogResource as C, CatalogBackgroundStyle as a, CatalogProps as b, CatalogBrand as c, CatalogEntry as d, CatalogRelated as e, CatalogSecondaryGroup as f, CatalogSection as g, CatalogStageRootAttributes as h, CatalogUxReview as i };
|
package/dist/wire-catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Signal } from 'kerfjs';
|
|
2
|
-
import { a as CatalogBackgroundStyle } from './types-
|
|
2
|
+
import { a as CatalogBackgroundStyle } from './types-BNP4gA41.js';
|
|
3
3
|
import './toolbar.js';
|
|
4
4
|
import './semantic-content-BbzjvSu9.js';
|
|
5
5
|
import './sides-BPSWde0A.js';
|
|
@@ -65,6 +65,13 @@ interface WireCatalogOptions {
|
|
|
65
65
|
sidebarStorageKey?: string;
|
|
66
66
|
/** The Catalog's `id`, when it is not the default `kui-catalog`. */
|
|
67
67
|
id?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Ask the page's server once for its UX Review project directory (served by
|
|
70
|
+
* `uxReviewPlugin` from `@kerfjs/ui/vite-ux-review`) and report it, so the
|
|
71
|
+
* app can render the Catalog with `uxReview`. Never called on static
|
|
72
|
+
* hosting or a server without the plugin.
|
|
73
|
+
*/
|
|
74
|
+
onUxReviewProject?: (projectDirectory: string) => void;
|
|
68
75
|
}
|
|
69
76
|
interface CatalogRevealOptions {
|
|
70
77
|
/** Scroll alignment within the sidebar. Default `'nearest'`. */
|
|
@@ -93,6 +100,6 @@ declare function revealCatalogEntry(root: HTMLElement, id: string, { block, inli
|
|
|
93
100
|
* catalog's panes and every example in it show their chrome dividers only
|
|
94
101
|
* while scrolled. Returns a disposer.
|
|
95
102
|
*/
|
|
96
|
-
declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleContrast, onToggleMotion, onSelectBackground, onToggleSecondary, urlParam, revealSelection, selectAction, toggleSidebarAction, toggleThemeAction, toggleContrastAction, toggleMotionAction, selectBackgroundAction, toggleSecondaryAction, collapsed, sidebarSize, sidebarStorageKey, id: catalogId, }: WireCatalogOptions): () => void;
|
|
103
|
+
declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleContrast, onToggleMotion, onSelectBackground, onToggleSecondary, urlParam, revealSelection, selectAction, toggleSidebarAction, toggleThemeAction, toggleContrastAction, toggleMotionAction, selectBackgroundAction, toggleSecondaryAction, collapsed, sidebarSize, sidebarStorageKey, id: catalogId, onUxReviewProject, }: WireCatalogOptions): () => void;
|
|
97
104
|
|
|
98
105
|
export { type CatalogRevealOptions, type WireCatalogOptions, revealCatalogEntry, wireCatalog };
|
package/dist/wire-catalog.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { wireWorkbench } from './chunk-QLA25V2K.js';
|
|
2
2
|
|
|
3
3
|
import { wireScrollDividers } from './chunk-6STQXRT4.js';
|
|
4
|
+
import { loadUxReviewProject } from './chunk-6C6DLP3W.js';
|
|
4
5
|
|
|
5
6
|
import { readTokenSearchField } from './chunk-ROZLJULF.js';
|
|
6
7
|
|
|
@@ -160,16 +161,26 @@ function wireCatalog(root, {
|
|
|
160
161
|
collapsed,
|
|
161
162
|
sidebarSize,
|
|
162
163
|
sidebarStorageKey,
|
|
163
|
-
id: catalogId = "kui-catalog"
|
|
164
|
+
id: catalogId = "kui-catalog",
|
|
165
|
+
onUxReviewProject
|
|
164
166
|
}) {
|
|
167
|
+
let disposed = false;
|
|
168
|
+
const view = root.ownerDocument.defaultView;
|
|
169
|
+
if (onUxReviewProject && view)
|
|
170
|
+
void loadUxReviewProject(
|
|
171
|
+
view.location.href,
|
|
172
|
+
(input, init) => view.fetch(input, init)
|
|
173
|
+
).then((directory) => {
|
|
174
|
+
if (directory && !disposed) onUxReviewProject(directory);
|
|
175
|
+
});
|
|
165
176
|
let cancelReveal;
|
|
166
177
|
const select = (id) => {
|
|
167
178
|
onSelect(id);
|
|
168
|
-
const
|
|
169
|
-
if (urlParam &&
|
|
170
|
-
const url = new URL(
|
|
179
|
+
const view2 = root.ownerDocument.defaultView;
|
|
180
|
+
if (urlParam && view2) {
|
|
181
|
+
const url = new URL(view2.location.href);
|
|
171
182
|
url.searchParams.set(urlParam, id);
|
|
172
|
-
|
|
183
|
+
view2.history.replaceState(null, "", url);
|
|
173
184
|
}
|
|
174
185
|
const rail = root.ownerDocument.getElementById(`${catalogId}-left-rail`);
|
|
175
186
|
if (collapsed && !collapsed.peek() && rail && root.ownerDocument.defaultView?.getComputedStyle(rail).position === "absolute")
|
|
@@ -275,7 +286,30 @@ function wireCatalog(root, {
|
|
|
275
286
|
}
|
|
276
287
|
})
|
|
277
288
|
);
|
|
289
|
+
if (onUxReviewProject)
|
|
290
|
+
disposers.push(
|
|
291
|
+
delegate(
|
|
292
|
+
root,
|
|
293
|
+
"click",
|
|
294
|
+
'a.kui-toolbar-action-link[href^="uxreview://capture"]',
|
|
295
|
+
(_event, element) => {
|
|
296
|
+
const link = element;
|
|
297
|
+
const url = new URL(link.href);
|
|
298
|
+
const context = (url.searchParams.get("context") ?? "").replace(
|
|
299
|
+
/\nPage: [^\n]*$/,
|
|
300
|
+
""
|
|
301
|
+
);
|
|
302
|
+
url.searchParams.set(
|
|
303
|
+
"context",
|
|
304
|
+
`${context}
|
|
305
|
+
Page: ${link.ownerDocument.location.href}`
|
|
306
|
+
);
|
|
307
|
+
link.href = url.href;
|
|
308
|
+
}
|
|
309
|
+
)
|
|
310
|
+
);
|
|
278
311
|
return () => {
|
|
312
|
+
disposed = true;
|
|
279
313
|
cancelReveal?.();
|
|
280
314
|
for (const dispose of disposers.splice(0)) dispose();
|
|
281
315
|
};
|
package/dist/wire-catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog/wiring/wire-catalog.ts"],"names":[],"mappings":";;;;;;;;;;AA0EA,IAAM,mBAAA,GAAsB,uBAAA;AAG5B,SAAS,kBAAkB,IAAA,EAA+B;AACxD,EAAA,MAAM,IAAA,GAAO,KAAK,aAAA,CAAc,WAAA;AAChC,EAAA,IAAI,WAAA,GAAc,EAAA;AAClB,EAAA,IAAI,KAAA,GAAQ,EAAA;AACZ,EAAA,IAAI,aAAA,GAAoC,IAAA;AACxC,EAAA,MAAM,UAAU,CAAC,IAAA,KAAiB,KAAK,iBAAA,EAAkB,CAAE,SAAS,KAAK,CAAA;AACzE,EAAA,MAAM,QAAQ,MAAM;AAClB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,aAAA,CAA2B,uBAAuB,CAAA;AACtE,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,aAAA,CAA2B,wBAAwB,CAAA;AACpE,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,GAAA,EAAK;AACrB,IAAA,IAAI,WAAW,aAAA,EAAe;AAC5B,MAAA,aAAA,GAAgB,MAAA;AAChB,MAAA,IAAI,oBAAA,CAAqB,MAAM,CAAA,CAAE,KAAA,KAAU,WAAA;AACzC,QAAA,MAAA,CAAO,WAAA,GAAc,WAAA;AAAA,IACzB;AACA,IAAA,MAAM,QAAQ,MAAA,CAAO,OAAA;AAAA,MACnB;AAAA,KACF;AACA,IAAA,KAAA,CAAM,OAAA,CAAQ,mBAAA,GAAsB,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AACzD,IAAA,MAAM,kBAAA,GAAqB,MAAA,CAAO,CAAC,WAAW,CAAA;AAC9C,IAAA,IAAI,KAAA,CAAM,QAAQ,kBAAA,KAAuB,kBAAA;AACvC,MAAA,KAAA,CAAM,QAAQ,kBAAA,GAAqB,kBAAA;AACrC,IAAA,MAAM,QAAQ,KAAA,CAAM,aAAA;AAAA,MAClB;AAAA,KACF;AACA,IAAA,KAAA,CAAM,KAAA,CAAM,OAAA,GAAU,KAAA,GAAQ,EAAA,GAAK,MAAA;AACnC,IAAA,GAAA,CAAI,OAAA,CAAQ,mBAAA,GAAsB,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AACvD,IAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,IAAA,KAAA,MAAW,WAAW,GAAA,CAAI,gBAAA;AAAA,MACxB;AAAA,KACF,EAAG;AACD,MAAA,MAAM,QAAQ,OAAA,CAAQ,OAAA;AAAA,QACpB;AAAA,OACF;AACA,MAAA,MAAM,cAAA,GACJ,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,cAAe,CAAA,IACtC,KAAA,KAAU,IAAA,IAAQ,OAAA,CAAQ,KAAA,CAAM,OAAA,CAAQ,qBAAsB,CAAA;AACjE,MAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,MAAA,KAAA,MAAW,SAAS,OAAA,CAAQ,gBAAA;AAAA,QAC1B;AAAA,OACF,EAAG;AACD,QAAA,MAAM,UACJ,CAAC,KAAA,IAAS,kBAAkB,OAAA,CAAQ,KAAA,CAAM,QAAQ,gBAAiB,CAAA;AACrE,QAAA,KAAA,CAAM,SAAS,CAAC,OAAA;AAChB,QAAA,IAAI,OAAA,EAAS,cAAA,EAAA;AAAA,MACf;AACA,MAAA,OAAA,CAAQ,MAAA,GAAS,OAAA,CAAQ,KAAK,CAAA,IAAK,cAAA,KAAmB,CAAA;AACtD,MAAA,cAAA,IAAkB,cAAA;AAAA,IACpB;AACA,IAAA,KAAA,MAAW,SAAS,GAAA,CAAI,gBAAA;AAAA,MACtB;AAAA,KACF,EAAG;AACD,MAAA,KAAA,CAAM,MAAA,GACJ,OAAA,CAAQ,KAAK,CAAA,IACb,CAAC,KAAA,CAAM,IAAA;AAAA,QACL,KAAA,CAAM,iBAA8B,wBAAwB;AAAA,QAC5D,IAAA,CAAK,CAAC,OAAA,KAAY,CAAC,QAAQ,MAAM,CAAA;AACrC,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,aAAA,CAA2B,iBAAiB,CAAA;AACjE,MAAA,MAAM,UAAU,KAAA,CAAM,aAAA;AAAA,QACpB;AAAA,OACF;AACA,MAAA,IAAI,UAAU,OAAA,EAAS;AACrB,QAAA,MAAM,WAAW,KAAA,GACb,CAAC,MAAM,MAAA,GACP,OAAA,CAAQ,QAAQ,yBAAA,KAA8B,MAAA;AAClD,QAAA,MAAA,CAAO,YAAA,CAAa,eAAA,EAAiB,MAAA,CAAO,QAAQ,CAAC,CAAA;AACrD,QAAA,MAAM,QAAQ,MAAA,CAAO,aAAA;AAAA,UACnB;AAAA,SACF;AACA,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,KAAA,CAAM,OAAA,CAAQ,IAAA,GAAO,MAAA,CAAO,QAAQ,CAAA;AACpC,UAAA,KAAA,CAAM,OAAA,CAAQ,SAAA,GAAY,QAAA,GAAW,MAAA,GAAS,OAAA;AAC9C,UAAA,KAAA,CAAM,KAAA,CAAM,WAAA;AAAA,YACV,kCAAA;AAAA,YACA,WAAW,OAAA,GAAU;AAAA,WACvB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,IAAA,MAAM,QAAQ,GAAA,CAAI,aAAA;AAAA,MAChB;AAAA,KACF;AACA,IAAA,KAAA,CAAM,QAAQ,OAAA,GAAU,MAAA,CAAO,QAAQ,KAAK,CAAA,IAAK,mBAAmB,CAAC,CAAA;AAAA,EACvE,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAiB;AAChC,IAAA,MAAM,SAAS,KAAA,CAAM,MAAA;AACrB,IAAA,MAAM,MAAA,GAAS,MAAA,EAAQ,OAAA,CAAqB,uBAAuB,CAAA;AACnE,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG;AACvC,IAAA,WAAA,GAAc,oBAAA,CAAqB,MAAM,CAAA,CAAE,KAAA;AAC3C,IAAA,KAAA,GAAQ,WAAA,CAAY,IAAA,EAAK,CAAE,iBAAA,EAAkB;AAC7C,IAAA,KAAA,EAAM;AAAA,EACR,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAiB;AAChC,IAAA,MAAM,SAAS,KAAA,CAAM,MAAA;AACrB,IAAA,IAAI,CAAC,MAAA,CAAO,OAAA,CAAQ,sCAAsC,CAAA,EAAG;AAC7D,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,aAAA,CAA2B,uBAAuB,CAAA;AACtE,IAAA,WAAA,GAAc,EAAA;AACd,IAAA,KAAA,GAAQ,EAAA;AACR,IAAA,MAAA,CAAO,WAAA,GAAc,EAAA;AACrB,IAAA,KAAA,EAAM;AACN,IAAA,MAAA,CAAO,KAAA,EAAM;AAAA,EACf,CAAA;AACA,EAAA,IAAA,CAAK,gBAAA,CAAiB,SAAS,OAAO,CAAA;AACtC,EAAA,IAAA,CAAK,gBAAA,CAAiB,SAAS,OAAO,CAAA;AACtC,EAAA,MAAM,QAAA,GAAW,IAAI,IAAA,CAAK,gBAAA,CAAiB,MAAM;AAC/C,IAAA,IAAI,aAAa,KAAA,EAAM;AAAA,EACzB,CAAC,CAAA;AACD,EAAA,QAAA,CAAS,QAAQ,IAAA,EAAM;AAAA,IACrB,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,0BAA0B,CAAA;AAAA,IAC5C,SAAA,EAAW,IAAA;AAAA,IACX,OAAA,EAAS;AAAA,GACV,CAAA;AACD,EAAA,KAAA,EAAM;AACN,EAAA,OAAO,MAAM;AACX,IAAA,IAAA,CAAK,mBAAA,CAAoB,SAAS,OAAO,CAAA;AACzC,IAAA,IAAA,CAAK,mBAAA,CAAoB,SAAS,OAAO,CAAA;AACzC,IAAA,QAAA,CAAS,UAAA,EAAW;AAAA,EACtB,CAAA;AACF;AAMO,SAAS,kBAAA,CACd,MACA,EAAA,EACA;AAAA,EACE,KAAA,GAAQ,SAAA;AAAA,EACR,MAAA,GAAS,SAAA;AAAA,EACT,QAAA,GAAW,MAAA;AAAA,EACX,KAAA,GAAQ;AACV,CAAA,GAA0B,EAAC,EACf;AACZ,EAAA,MAAM,IAAA,GAAO,KAAK,aAAA,CAAc,WAAA;AAChC,EAAA,IAAI,CAAC,IAAA,IAAS,KAAA,IAAS,CAAC,IAAA,CAAK,WAAW,KAAK,CAAA,CAAE,OAAA,EAAU,OAAO,MAAM;AAAA,EAAC,CAAA;AACvE,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,qBAAA,CAAsB,MAAM;AAC7C,IAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,gBAAA,CAA8B,gBAAgB,CAAA,EAAG;AACvE,MAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,MAAA,KAAW,EAAA,EAAI;AAChC,MAAA,IAAA,CAAK,cAAA,CAAe,EAAE,KAAA,EAAO,MAAA,EAAQ,UAAU,CAAA;AAC/C,MAAA;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,MAAM,IAAA,CAAK,oBAAA,CAAqB,KAAK,CAAA;AAC9C;AAWO,SAAS,YACd,IAAA,EACA;AAAA,EACE,QAAA;AAAA,EACA,eAAA;AAAA,EACA,aAAA;AAAA,EACA,gBAAA;AAAA,EACA,cAAA;AAAA,EACA,kBAAA;AAAA,EACA,iBAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,YAAA,GAAe,gBAAA;AAAA,EACf,mBAAA,GAAsB,wBAAA;AAAA,EACtB,iBAAA,GAAoB,sBAAA;AAAA,EACpB,oBAAA,GAAuB,yBAAA;AAAA,EACvB,kBAAA,GAAqB,uBAAA;AAAA,EACrB,sBAAA,GAAyB,2BAAA;AAAA,EACzB,qBAAA,GAAwB,0BAAA;AAAA,EACxB,SAAA;AAAA,EACA,WAAA;AAAA,EACA,iBAAA;AAAA,EACA,IAAI,SAAA,GAAY;AAClB,CAAA,EACY;AACZ,EAAA,IAAI,YAAA;AACJ,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,KAAe;AAC7B,IAAA,QAAA,CAAS,EAAE,CAAA;AACX,IAAA,MAAM,IAAA,GAAO,KAAK,aAAA,CAAc,WAAA;AAChC,IAAA,IAAI,YAAY,IAAA,EAAM;AACpB,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,IAAA,CAAK,SAAS,IAAI,CAAA;AACtC,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,EAAE,CAAA;AACjC,MAAA,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,IAAA,EAAM,EAAA,EAAI,GAAG,CAAA;AAAA,IACzC;AAEA,IAAA,MAAM,OAAO,IAAA,CAAK,aAAA,CAAc,cAAA,CAAe,CAAA,EAAG,SAAS,CAAA,UAAA,CAAY,CAAA;AACvE,IAAA,IACE,SAAA,IACA,CAAC,SAAA,CAAU,IAAA,EAAK,IAChB,IAAA,IACA,IAAA,CAAK,aAAA,CAAc,WAAA,EAAa,gBAAA,CAAiB,IAAI,CAAA,CAAE,QAAA,KACrD,UAAA;AAEF,MAAA,SAAA,CAAU,KAAA,GAAQ,IAAA;AACpB,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA,YAAA,IAAe;AACf,MAAA,YAAA,GAAe,kBAAA;AAAA,QACb,IAAA;AAAA,QACA,EAAA;AAAA,QACA,eAAA,KAAoB,OAAO,MAAA,GAAY;AAAA,OACzC;AAAA,IACF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,SAAA,GAA+B;AAAA,IACnC,mBAAmB,IAAI,CAAA;AAAA,IACvB,kBAAkB,IAAI,CAAA;AAAA;AAAA;AAAA,IAGtB,QAAA;AAAA,MACE,IAAA;AAAA,MACA,OAAA;AAAA,MACA,iBAAiB,YAAY,CAAA,EAAA,CAAA;AAAA,MAC7B,CAAC,QAAQ,OAAA,KAAY;AACnB,QAAA,MAAM,EAAA,GAAM,QAAwB,OAAA,CAAQ,MAAA;AAC5C,QAAA,IAAI,EAAA,SAAW,EAAE,CAAA;AAAA,MACnB;AAAA;AACF,GACF;AACA,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,QAAA;AAAA,QAAS,IAAA;AAAA,QAAM,OAAA;AAAA,QAAS,iBAAiB,mBAAmB,CAAA,EAAA,CAAA;AAAA,QAAM,MAChE,eAAA;AAAgB;AAClB,KACF;AAAA,EACF;AACA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,QAAA;AAAA,QAAS,IAAA;AAAA,QAAM,OAAA;AAAA,QAAS,iBAAiB,iBAAiB,CAAA,EAAA,CAAA;AAAA,QAAM,MAC9D,aAAA;AAAc;AAChB,KACF;AAAA,EACF;AACA,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,QAAA;AAAA,QAAS,IAAA;AAAA,QAAM,OAAA;AAAA,QAAS,iBAAiB,oBAAoB,CAAA,EAAA,CAAA;AAAA,QAAM,MACjE,gBAAA;AAAiB;AACnB,KACF;AAAA,EACF;AACA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,QAAA;AAAA,QAAS,IAAA;AAAA,QAAM,OAAA;AAAA,QAAS,iBAAiB,kBAAkB,CAAA,EAAA,CAAA;AAAA,QAAM,MAC/D,cAAA;AAAe;AACjB,KACF;AAAA,EACF;AACA,EAAA,IAAI,kBAAA,EAAoB;AACtB,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,QAAA;AAAA,QACE,IAAA;AAAA,QACA,OAAA;AAAA,QACA,iBAAiB,sBAAsB,CAAA,EAAA,CAAA;AAAA,QACvC,CAAC,QAAQ,OAAA,KAAY;AACnB,UAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,YAAA,CAAa,wBAAwB,CAAA;AAC3D,UAAA,IACE,KAAA,KAAU,kBACV,KAAA,KAAU,kBAAA,IACV,UAAU,cAAA,IACV,KAAA,KAAU,aACV,KAAA,KAAU,QAAA;AAEV,YAAA,kBAAA,CAAmB,KAAK,CAAA;AAAA,QAC5B;AAAA;AACF,KACF;AAAA,EACF;AACA,EAAA,IAAI,iBAAA,EAAmB;AACrB,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,QAAA;AAAA,QAAS,IAAA;AAAA,QAAM,OAAA;AAAA,QAAS,iBAAiB,qBAAqB,CAAA,EAAA,CAAA;AAAA,QAAM,MAClE,iBAAA;AAAkB;AACpB,KACF;AAAA,EACF;AACA,EAAA,IAAI,SAAA,IAAa,WAAA;AACf,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,cAAc,IAAA,EAAM;AAAA,QAClB,EAAA,EAAI,SAAA;AAAA,QACJ,MAAA,EAAQ;AAAA,UACN,QAAA,EAAU;AAAA,YACR,SAAA;AAAA,YACA,IAAA,EAAM,WAAA;AAAA,YACN,UAAA,EAAY;AAAA;AACd;AACF,OACD;AAAA,KACH;AACF,EAAA,OAAO,MAAM;AACX,IAAA,YAAA,IAAe;AACf,IAAA,KAAA,MAAW,OAAA,IAAW,SAAA,CAAU,MAAA,CAAO,CAAC,GAAG,OAAA,EAAQ;AAAA,EACrD,CAAA;AACF","file":"wire-catalog.js","sourcesContent":["import { delegate, type Signal } from 'kerfjs';\n\nimport { readTokenSearchField } from '../../components/forms/token-search-field/token-search-field.js';\nimport { wireWorkbench } from '../../components/layout/workbench/wiring/wire-workbench.js';\nimport { wireScrollDividers } from '../../wiring/wire-scroll-dividers.js';\nimport type { CatalogBackgroundStyle } from '../types.js';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Report a contrast-toggle press; the app updates its signal and applies the preference. */\n onToggleContrast?: () => void;\n /** Report a reduced-motion-toggle press; the app updates its signal and applies the preference. */\n onToggleMotion?: () => void;\n /** Report a preview background choice; the app updates `backgroundStyle`. */\n onSelectBackground?: (style: CatalogBackgroundStyle) => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n /**\n * Reveal the chosen sidebar row after selection. `true` uses desktop-safe\n * defaults; pass options to customize scroll alignment or the media guard.\n */\n revealSelection?: boolean | CatalogRevealOptions;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleContrastAction?: string;\n toggleMotionAction?: string;\n selectBackgroundAction?: string;\n toggleSecondaryAction?: string;\n /**\n * The sidebar's app-owned collapsed flag. With it, the catalog's Workbench\n * sidebar is wired like any Workbench rail: it becomes a transient overlay\n * on a small screen (collapsed as the breakpoint applies, closed on Escape\n * or a press outside, focus handed back), and choosing an entry from the\n * open overlay closes it.\n */\n collapsed?: Signal<boolean>;\n /**\n * The app-owned sidebar width signal for a Catalog rendered with\n * `sidebar={{ resizable: true, size: sidebarSize.value }}`. Each committed\n * drag or keyboard resize is written to it.\n */\n sidebarSize?: Signal<number>;\n /**\n * With `sidebarSize`, load and save the width under this storage key so the\n * catalog remembers the user's sidebar width.\n */\n sidebarStorageKey?: string;\n /** The Catalog's `id`, when it is not the default `kui-catalog`. */\n id?: string;\n}\n\nexport interface CatalogRevealOptions {\n /** Scroll alignment within the sidebar. Default `'nearest'`. */\n block?: ScrollLogicalPosition;\n /** Cross-axis alignment. Default `'nearest'`. */\n inline?: ScrollLogicalPosition;\n /** Scroll behavior. Default `'auto'`. */\n behavior?: ScrollBehavior;\n /**\n * Only reveal when this media query matches. Defaults to the Catalog's\n * desktop layout; pass `false` to reveal at every viewport size.\n */\n media?: string | false;\n}\n\n// The Workbench keeps the sidebar inline above its `narrow` breakpoint (704px).\nconst catalogDesktopMedia = '(min-width: 44.01rem)';\n\n/** Keep the sidebar filter local to the shared Catalog, including across controlled rerenders. */\nfunction wireCatalogFilter(root: HTMLElement): () => void {\n const view = root.ownerDocument.defaultView!;\n let filterValue = '';\n let query = '';\n let currentEditor: HTMLElement | null = null;\n const matches = (name: string) => name.toLocaleLowerCase().includes(query);\n const apply = () => {\n const editor = root.querySelector<HTMLElement>('[data-catalog-filter]');\n const nav = root.querySelector<HTMLElement>('[data-catalog-sidebar]');\n if (!editor || !nav) return;\n if (editor !== currentEditor) {\n currentEditor = editor;\n if (readTokenSearchField(editor).query !== filterValue)\n editor.textContent = filterValue;\n }\n const field = editor.closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n )!;\n field.dataset.catalogFilterActive = String(Boolean(query));\n const placeholderVisible = String(!filterValue);\n if (field.dataset.placeholderVisible !== placeholderVisible)\n field.dataset.placeholderVisible = placeholderVisible;\n const clear = field.querySelector<HTMLElement>(\n '[data-action=\"catalog-clear-filter\"]',\n )!;\n clear.style.display = query ? '' : 'none';\n nav.dataset.catalogFilterActive = String(Boolean(query));\n let visibleEntries = 0;\n for (const section of nav.querySelectorAll<HTMLElement>(\n '[data-catalog-section]',\n )) {\n const group = section.closest<HTMLElement>(\n '[data-catalog-secondary-group]',\n );\n const headingMatches =\n matches(section.dataset.catalogSection!) ||\n (group !== null && matches(group.dataset.catalogSecondaryGroup!));\n let sectionEntries = 0;\n for (const entry of section.querySelectorAll<HTMLElement>(\n '[data-catalog-entry-name]',\n )) {\n const visible =\n !query || headingMatches || matches(entry.dataset.catalogEntryName!);\n entry.hidden = !visible;\n if (visible) sectionEntries++;\n }\n section.hidden = Boolean(query) && sectionEntries === 0;\n visibleEntries += sectionEntries;\n }\n for (const group of nav.querySelectorAll<HTMLElement>(\n '[data-catalog-secondary-group]',\n )) {\n group.hidden =\n Boolean(query) &&\n !Array.from(\n group.querySelectorAll<HTMLElement>('[data-catalog-section]'),\n ).some((section) => !section.hidden);\n const toggle = group.querySelector<HTMLElement>('[aria-expanded]');\n const content = group.querySelector<HTMLElement>(\n '[data-catalog-secondary-collapsed]',\n );\n if (toggle && content) {\n const expanded = query\n ? !group.hidden\n : content.dataset.catalogSecondaryCollapsed !== 'true';\n toggle.setAttribute('aria-expanded', String(expanded));\n const arrow = toggle.querySelector<HTMLElement>(\n '[data-component=\"disclosure-arrow\"]',\n );\n if (arrow) {\n arrow.dataset.open = String(expanded);\n arrow.dataset.direction = expanded ? 'down' : 'right';\n arrow.style.setProperty(\n '--_kui-disclosure-arrow-rotation',\n expanded ? '90deg' : '0deg',\n );\n }\n }\n }\n const empty = nav.querySelector<HTMLElement>(\n '[data-catalog-filter-empty]',\n )!;\n empty.dataset.visible = String(Boolean(query) && visibleEntries === 0);\n };\n const onInput = (event: Event) => {\n const target = event.target as HTMLElement | null;\n const editor = target?.closest<HTMLElement>('[data-catalog-filter]');\n if (!editor || !root.contains(editor)) return;\n filterValue = readTokenSearchField(editor).query;\n query = filterValue.trim().toLocaleLowerCase();\n apply();\n };\n const onClick = (event: Event) => {\n const target = event.target as Element;\n if (!target.closest('[data-action=\"catalog-clear-filter\"]')) return;\n const editor = root.querySelector<HTMLElement>('[data-catalog-filter]')!;\n filterValue = '';\n query = '';\n editor.textContent = '';\n apply();\n editor.focus();\n };\n root.addEventListener('input', onInput);\n root.addEventListener('click', onClick);\n const observer = new view.MutationObserver(() => {\n if (filterValue) apply();\n });\n observer.observe(root, {\n attributes: true,\n attributeFilter: ['data-placeholder-visible'],\n childList: true,\n subtree: true,\n });\n apply();\n return () => {\n root.removeEventListener('input', onInput);\n root.removeEventListener('click', onClick);\n observer.disconnect();\n };\n}\n\n/**\n * Reveal one Catalog sidebar entry after the controlled render settles without\n * moving focus. Returns a cancellation function for rapid selection changes.\n */\nexport function revealCatalogEntry(\n root: HTMLElement,\n id: string,\n {\n block = 'nearest',\n inline = 'nearest',\n behavior = 'auto',\n media = catalogDesktopMedia,\n }: CatalogRevealOptions = {},\n): () => void {\n const view = root.ownerDocument.defaultView;\n if (!view || (media && !view.matchMedia(media).matches)) return () => {};\n const frame = view.requestAnimationFrame(() => {\n for (const item of root.querySelectorAll<HTMLElement>('[data-item-id]')) {\n if (item.dataset.itemId !== id) continue;\n item.scrollIntoView({ block, inline, behavior });\n break;\n }\n });\n return () => view.cancelAnimationFrame(frame);\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry popup menu), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * It also wires the scroll dividers below root (`wireScrollDividers`), so the\n * catalog's panes and every example in it show their chrome dividers only\n * while scrolled. Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleContrast,\n onToggleMotion,\n onSelectBackground,\n onToggleSecondary,\n urlParam,\n revealSelection,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleContrastAction = 'catalog-toggle-contrast',\n toggleMotionAction = 'catalog-toggle-motion',\n selectBackgroundAction = 'catalog-select-background',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n collapsed,\n sidebarSize,\n sidebarStorageKey,\n id: catalogId = 'kui-catalog',\n }: WireCatalogOptions,\n): () => void {\n let cancelReveal: (() => void) | undefined;\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n // An open overlay sidebar gives way to the entry just chosen from it.\n const rail = root.ownerDocument.getElementById(`${catalogId}-left-rail`);\n if (\n collapsed &&\n !collapsed.peek() &&\n rail &&\n root.ownerDocument.defaultView?.getComputedStyle(rail).position ===\n 'absolute'\n )\n collapsed.value = true;\n if (revealSelection) {\n cancelReveal?.();\n cancelReveal = revealCatalogEntry(\n root,\n id,\n revealSelection === true ? undefined : revealSelection,\n );\n }\n };\n\n const disposers: Array<() => void> = [\n wireScrollDividers(root),\n wireCatalogFilter(root),\n // Sidebar items AND the footer's related-entry popup-menu items both carry\n // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.\n delegate(\n root,\n 'click',\n `[data-action=\"${selectAction}\"]`,\n (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n },\n ),\n ];\n if (onToggleSidebar) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () =>\n onToggleSidebar(),\n ),\n );\n }\n if (onToggleTheme) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () =>\n onToggleTheme(),\n ),\n );\n }\n if (onToggleContrast) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleContrastAction}\"]`, () =>\n onToggleContrast(),\n ),\n );\n }\n if (onToggleMotion) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleMotionAction}\"]`, () =>\n onToggleMotion(),\n ),\n );\n }\n if (onSelectBackground) {\n disposers.push(\n delegate(\n root,\n 'click',\n `[data-action=\"${selectBackgroundAction}\"]`,\n (_event, element) => {\n const style = element.getAttribute('data-background-choice');\n if (\n style === 'checkerboard' ||\n style === 'vertical-stripes' ||\n style === 'layout-guide' ||\n style === 'surface' ||\n style === 'sunken'\n )\n onSelectBackground(style);\n },\n ),\n );\n }\n if (onToggleSecondary) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () =>\n onToggleSecondary(),\n ),\n );\n }\n if (collapsed || sidebarSize)\n disposers.push(\n wireWorkbench(root, {\n id: catalogId,\n panels: {\n leftRail: {\n collapsed,\n size: sidebarSize,\n storageKey: sidebarStorageKey,\n },\n },\n }),\n );\n return () => {\n cancelReveal?.();\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/catalog/wiring/wire-catalog.ts"],"names":["view"],"mappings":"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","file":"wire-catalog.js","sourcesContent":["import { delegate, type Signal } from 'kerfjs';\n\nimport { readTokenSearchField } from '../../components/forms/token-search-field/token-search-field.js';\nimport { wireWorkbench } from '../../components/layout/workbench/wiring/wire-workbench.js';\nimport { wireScrollDividers } from '../../wiring/wire-scroll-dividers.js';\nimport type { CatalogBackgroundStyle } from '../types.js';\nimport { loadUxReviewProject } from '../ux-review.js';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Report a contrast-toggle press; the app updates its signal and applies the preference. */\n onToggleContrast?: () => void;\n /** Report a reduced-motion-toggle press; the app updates its signal and applies the preference. */\n onToggleMotion?: () => void;\n /** Report a preview background choice; the app updates `backgroundStyle`. */\n onSelectBackground?: (style: CatalogBackgroundStyle) => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n /**\n * Reveal the chosen sidebar row after selection. `true` uses desktop-safe\n * defaults; pass options to customize scroll alignment or the media guard.\n */\n revealSelection?: boolean | CatalogRevealOptions;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleContrastAction?: string;\n toggleMotionAction?: string;\n selectBackgroundAction?: string;\n toggleSecondaryAction?: string;\n /**\n * The sidebar's app-owned collapsed flag. With it, the catalog's Workbench\n * sidebar is wired like any Workbench rail: it becomes a transient overlay\n * on a small screen (collapsed as the breakpoint applies, closed on Escape\n * or a press outside, focus handed back), and choosing an entry from the\n * open overlay closes it.\n */\n collapsed?: Signal<boolean>;\n /**\n * The app-owned sidebar width signal for a Catalog rendered with\n * `sidebar={{ resizable: true, size: sidebarSize.value }}`. Each committed\n * drag or keyboard resize is written to it.\n */\n sidebarSize?: Signal<number>;\n /**\n * With `sidebarSize`, load and save the width under this storage key so the\n * catalog remembers the user's sidebar width.\n */\n sidebarStorageKey?: string;\n /** The Catalog's `id`, when it is not the default `kui-catalog`. */\n id?: string;\n /**\n * Ask the page's server once for its UX Review project directory (served by\n * `uxReviewPlugin` from `@kerfjs/ui/vite-ux-review`) and report it, so the\n * app can render the Catalog with `uxReview`. Never called on static\n * hosting or a server without the plugin.\n */\n onUxReviewProject?: (projectDirectory: string) => void;\n}\n\nexport interface CatalogRevealOptions {\n /** Scroll alignment within the sidebar. Default `'nearest'`. */\n block?: ScrollLogicalPosition;\n /** Cross-axis alignment. Default `'nearest'`. */\n inline?: ScrollLogicalPosition;\n /** Scroll behavior. Default `'auto'`. */\n behavior?: ScrollBehavior;\n /**\n * Only reveal when this media query matches. Defaults to the Catalog's\n * desktop layout; pass `false` to reveal at every viewport size.\n */\n media?: string | false;\n}\n\n// The Workbench keeps the sidebar inline above its `narrow` breakpoint (704px).\nconst catalogDesktopMedia = '(min-width: 44.01rem)';\n\n/** Keep the sidebar filter local to the shared Catalog, including across controlled rerenders. */\nfunction wireCatalogFilter(root: HTMLElement): () => void {\n const view = root.ownerDocument.defaultView!;\n let filterValue = '';\n let query = '';\n let currentEditor: HTMLElement | null = null;\n const matches = (name: string) => name.toLocaleLowerCase().includes(query);\n const apply = () => {\n const editor = root.querySelector<HTMLElement>('[data-catalog-filter]');\n const nav = root.querySelector<HTMLElement>('[data-catalog-sidebar]');\n if (!editor || !nav) return;\n if (editor !== currentEditor) {\n currentEditor = editor;\n if (readTokenSearchField(editor).query !== filterValue)\n editor.textContent = filterValue;\n }\n const field = editor.closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n )!;\n field.dataset.catalogFilterActive = String(Boolean(query));\n const placeholderVisible = String(!filterValue);\n if (field.dataset.placeholderVisible !== placeholderVisible)\n field.dataset.placeholderVisible = placeholderVisible;\n const clear = field.querySelector<HTMLElement>(\n '[data-action=\"catalog-clear-filter\"]',\n )!;\n clear.style.display = query ? '' : 'none';\n nav.dataset.catalogFilterActive = String(Boolean(query));\n let visibleEntries = 0;\n for (const section of nav.querySelectorAll<HTMLElement>(\n '[data-catalog-section]',\n )) {\n const group = section.closest<HTMLElement>(\n '[data-catalog-secondary-group]',\n );\n const headingMatches =\n matches(section.dataset.catalogSection!) ||\n (group !== null && matches(group.dataset.catalogSecondaryGroup!));\n let sectionEntries = 0;\n for (const entry of section.querySelectorAll<HTMLElement>(\n '[data-catalog-entry-name]',\n )) {\n const visible =\n !query || headingMatches || matches(entry.dataset.catalogEntryName!);\n entry.hidden = !visible;\n if (visible) sectionEntries++;\n }\n section.hidden = Boolean(query) && sectionEntries === 0;\n visibleEntries += sectionEntries;\n }\n for (const group of nav.querySelectorAll<HTMLElement>(\n '[data-catalog-secondary-group]',\n )) {\n group.hidden =\n Boolean(query) &&\n !Array.from(\n group.querySelectorAll<HTMLElement>('[data-catalog-section]'),\n ).some((section) => !section.hidden);\n const toggle = group.querySelector<HTMLElement>('[aria-expanded]');\n const content = group.querySelector<HTMLElement>(\n '[data-catalog-secondary-collapsed]',\n );\n if (toggle && content) {\n const expanded = query\n ? !group.hidden\n : content.dataset.catalogSecondaryCollapsed !== 'true';\n toggle.setAttribute('aria-expanded', String(expanded));\n const arrow = toggle.querySelector<HTMLElement>(\n '[data-component=\"disclosure-arrow\"]',\n );\n if (arrow) {\n arrow.dataset.open = String(expanded);\n arrow.dataset.direction = expanded ? 'down' : 'right';\n arrow.style.setProperty(\n '--_kui-disclosure-arrow-rotation',\n expanded ? '90deg' : '0deg',\n );\n }\n }\n }\n const empty = nav.querySelector<HTMLElement>(\n '[data-catalog-filter-empty]',\n )!;\n empty.dataset.visible = String(Boolean(query) && visibleEntries === 0);\n };\n const onInput = (event: Event) => {\n const target = event.target as HTMLElement | null;\n const editor = target?.closest<HTMLElement>('[data-catalog-filter]');\n if (!editor || !root.contains(editor)) return;\n filterValue = readTokenSearchField(editor).query;\n query = filterValue.trim().toLocaleLowerCase();\n apply();\n };\n const onClick = (event: Event) => {\n const target = event.target as Element;\n if (!target.closest('[data-action=\"catalog-clear-filter\"]')) return;\n const editor = root.querySelector<HTMLElement>('[data-catalog-filter]')!;\n filterValue = '';\n query = '';\n editor.textContent = '';\n apply();\n editor.focus();\n };\n root.addEventListener('input', onInput);\n root.addEventListener('click', onClick);\n const observer = new view.MutationObserver(() => {\n if (filterValue) apply();\n });\n observer.observe(root, {\n attributes: true,\n attributeFilter: ['data-placeholder-visible'],\n childList: true,\n subtree: true,\n });\n apply();\n return () => {\n root.removeEventListener('input', onInput);\n root.removeEventListener('click', onClick);\n observer.disconnect();\n };\n}\n\n/**\n * Reveal one Catalog sidebar entry after the controlled render settles without\n * moving focus. Returns a cancellation function for rapid selection changes.\n */\nexport function revealCatalogEntry(\n root: HTMLElement,\n id: string,\n {\n block = 'nearest',\n inline = 'nearest',\n behavior = 'auto',\n media = catalogDesktopMedia,\n }: CatalogRevealOptions = {},\n): () => void {\n const view = root.ownerDocument.defaultView;\n if (!view || (media && !view.matchMedia(media).matches)) return () => {};\n const frame = view.requestAnimationFrame(() => {\n for (const item of root.querySelectorAll<HTMLElement>('[data-item-id]')) {\n if (item.dataset.itemId !== id) continue;\n item.scrollIntoView({ block, inline, behavior });\n break;\n }\n });\n return () => view.cancelAnimationFrame(frame);\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry popup menu), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * It also wires the scroll dividers below root (`wireScrollDividers`), so the\n * catalog's panes and every example in it show their chrome dividers only\n * while scrolled. Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleContrast,\n onToggleMotion,\n onSelectBackground,\n onToggleSecondary,\n urlParam,\n revealSelection,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleContrastAction = 'catalog-toggle-contrast',\n toggleMotionAction = 'catalog-toggle-motion',\n selectBackgroundAction = 'catalog-select-background',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n collapsed,\n sidebarSize,\n sidebarStorageKey,\n id: catalogId = 'kui-catalog',\n onUxReviewProject,\n }: WireCatalogOptions,\n): () => void {\n let disposed = false;\n const view = root.ownerDocument.defaultView;\n if (onUxReviewProject && view)\n void loadUxReviewProject(view.location.href, (input, init) =>\n view.fetch(input, init),\n ).then((directory) => {\n if (directory && !disposed) onUxReviewProject(directory);\n });\n let cancelReveal: (() => void) | undefined;\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n // An open overlay sidebar gives way to the entry just chosen from it.\n const rail = root.ownerDocument.getElementById(`${catalogId}-left-rail`);\n if (\n collapsed &&\n !collapsed.peek() &&\n rail &&\n root.ownerDocument.defaultView?.getComputedStyle(rail).position ===\n 'absolute'\n )\n collapsed.value = true;\n if (revealSelection) {\n cancelReveal?.();\n cancelReveal = revealCatalogEntry(\n root,\n id,\n revealSelection === true ? undefined : revealSelection,\n );\n }\n };\n\n const disposers: Array<() => void> = [\n wireScrollDividers(root),\n wireCatalogFilter(root),\n // Sidebar items AND the footer's related-entry popup-menu items both carry\n // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.\n delegate(\n root,\n 'click',\n `[data-action=\"${selectAction}\"]`,\n (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n },\n ),\n ];\n if (onToggleSidebar) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () =>\n onToggleSidebar(),\n ),\n );\n }\n if (onToggleTheme) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () =>\n onToggleTheme(),\n ),\n );\n }\n if (onToggleContrast) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleContrastAction}\"]`, () =>\n onToggleContrast(),\n ),\n );\n }\n if (onToggleMotion) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleMotionAction}\"]`, () =>\n onToggleMotion(),\n ),\n );\n }\n if (onSelectBackground) {\n disposers.push(\n delegate(\n root,\n 'click',\n `[data-action=\"${selectBackgroundAction}\"]`,\n (_event, element) => {\n const style = element.getAttribute('data-background-choice');\n if (\n style === 'checkerboard' ||\n style === 'vertical-stripes' ||\n style === 'layout-guide' ||\n style === 'surface' ||\n style === 'sunken'\n )\n onSelectBackground(style);\n },\n ),\n );\n }\n if (onToggleSecondary) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () =>\n onToggleSecondary(),\n ),\n );\n }\n if (collapsed || sidebarSize)\n disposers.push(\n wireWorkbench(root, {\n id: catalogId,\n panels: {\n leftRail: {\n collapsed,\n size: sidebarSize,\n storageKey: sidebarStorageKey,\n },\n },\n }),\n );\n if (onUxReviewProject)\n // The capture context names the page the review came from. The URL can\n // change after the link rendered (`urlParam`), so stamp it as the link is\n // followed; the browser navigates to the href as it stands after the click.\n disposers.push(\n delegate(\n root,\n 'click',\n 'a.kui-toolbar-action-link[href^=\"uxreview://capture\"]',\n (_event, element) => {\n const link = element as HTMLAnchorElement;\n const url = new URL(link.href);\n const context = (url.searchParams.get('context') ?? '').replace(\n /\\nPage: [^\\n]*$/,\n '',\n );\n url.searchParams.set(\n 'context',\n `${context}\\nPage: ${link.ownerDocument.location.href}`,\n );\n link.href = url.href;\n },\n ),\n );\n return () => {\n disposed = true;\n cancelReveal?.();\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
|
package/dist/wire-tab-bars.js
CHANGED
package/docs/catalog.md
CHANGED
|
@@ -465,6 +465,57 @@ revealCatalogEntry(app, initial, { block: "center" });
|
|
|
465
465
|
the toggle, the app applies the theme (the shell reads `theme` to show the toggle's
|
|
466
466
|
opposite-state label). Omit `theme` to hide the toggle entirely.
|
|
467
467
|
|
|
468
|
+
## UX Review capture
|
|
469
|
+
|
|
470
|
+
On a local Vite dev or preview server the catalog can offer a **Capture region**
|
|
471
|
+
header link that starts a UX Review region screenshot, preset with the project
|
|
472
|
+
and the active entry. Static hosting has no local project, so the link never
|
|
473
|
+
appears there. Three pieces wire it:
|
|
474
|
+
|
|
475
|
+
```ts
|
|
476
|
+
// vite.config.ts — Node only; never part of the browser bundle.
|
|
477
|
+
import { uxReviewPlugin } from "@kerfjs/ui/vite-ux-review";
|
|
478
|
+
|
|
479
|
+
export default defineConfig({ plugins: [uxReviewPlugin()] });
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
```tsx
|
|
483
|
+
const uxReviewProject = signal<string | null>(null);
|
|
484
|
+
|
|
485
|
+
mount(app, () => (
|
|
486
|
+
<Catalog
|
|
487
|
+
/* …the props above… */
|
|
488
|
+
uxReview={
|
|
489
|
+
uxReviewProject.value
|
|
490
|
+
? { projectDirectory: uxReviewProject.value }
|
|
491
|
+
: undefined
|
|
492
|
+
}
|
|
493
|
+
/>
|
|
494
|
+
));
|
|
495
|
+
|
|
496
|
+
wireCatalog(app, {
|
|
497
|
+
onSelect: (id) => (active.value = id),
|
|
498
|
+
onUxReviewProject: (directory) => (uxReviewProject.value = directory),
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
- **`uxReviewPlugin(options?)`** answers `GET …/__ux-review/project.json` with
|
|
503
|
+
`{ projectDirectory }` on the dev and preview servers, at any base path. The
|
|
504
|
+
directory defaults to the repository holding Vite's `root` (the nearest
|
|
505
|
+
ancestor with `.git`, or `root` itself); pass `projectDirectory` (resolved
|
|
506
|
+
against `root`) to name another.
|
|
507
|
+
- **`wireCatalog`'s `onUxReviewProject`** asks the page's server once and
|
|
508
|
+
reports the directory. It is never called on static hosting or a server
|
|
509
|
+
without the plugin. While wired, following the link stamps the current page
|
|
510
|
+
URL into the capture context.
|
|
511
|
+
- **`Catalog`'s `uxReview`** (`{ projectDirectory, name? }`) renders the link
|
|
512
|
+
after `headerActions`. The capture is titled `<name>: <entry>`, and its
|
|
513
|
+
context names the catalog, the entry's section, and the entry; `name`
|
|
514
|
+
defaults to `brand.title`.
|
|
515
|
+
|
|
516
|
+
The browser helpers behind it, `uxReviewCaptureUrl` and `loadUxReviewProject`,
|
|
517
|
+
are exported from `@kerfjs/ui/catalog` for a capture link placed elsewhere.
|
|
518
|
+
|
|
468
519
|
## Custom action names
|
|
469
520
|
|
|
470
521
|
The shell emits `data-action="catalog-select"` (sidebar items),
|
|
@@ -758,6 +758,12 @@ whole-tab starts after scroll and selection changes, reserving end room so the
|
|
|
758
758
|
last peer can align there. The application still owns selection and tab order.
|
|
759
759
|
ResizeObserver notifications coalesce snap measurements into the next animation
|
|
760
760
|
frame, so inset writes cannot resize the observed strip during observer delivery.
|
|
761
|
+
`wireTabBars` watches the whole root it is given, but a mutation re-measures only
|
|
762
|
+
the bars it touched: a change inside a bar, a newly inserted bar, or a `dir`
|
|
763
|
+
change on an element above one. Content elsewhere in the root (terminal output,
|
|
764
|
+
list updates, streamed text) triggers no style or layout reads. A pinned strip
|
|
765
|
+
re-measures when its own size changes, so a layout change outside the bar still
|
|
766
|
+
reaches it.
|
|
761
767
|
|
|
762
768
|
CSS, the generated wrappers that make it
|
|
763
769
|
reachable, and the registration module are the package's only declared side
|