vireuikit 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,148 @@
1
- export { A11Y_NONE, ATOM_AT_REST, BUTTON_SIZES, CONTROL_IDLE, CONTROL_SIZES, DEFAULT_GROWTH_TIMING, MENU_CLOSED, MENU_CORNERS, MENU_DROP_BODY, MENU_ITEM_FILL, MIN_POINTER_TARGET_DP, MIN_TOUCH_TARGET_DP, POINTER_INPUT, PRESS_IDLE, TOUCH_INPUT, TYPEAHEAD_SECONDS, atomAtRest, atomBody, atomLift, atomMaterial, controlTargets, createControlVisual, createGrowthState, defaultSize, growthFrame, growthInk, growthPhase, hitSlop, iconButtonSize, menuActivity, menuBody, menuCorner, menuGlass, menuItemFill, menuMaterial, menuPlacement, menuSize, menuSurfaceFrame, minTarget, nestedRadius, presentationFor, pressResponse, resolveA11y, sameA11y, settleControlVisual, smoothstep, stepControl, stepControlVisual, stepFocus, stepGrowth, stepHover, stepMenu, stepPress, stepSpring, visualAtomState } from './chunk-AR2AM3BR.js';
1
+ import { classifyWindow, widthAtLeast, windowSegments, surfaceRegion, BUTTON_SIZES } from './chunk-7A3XUTXH.js';
2
+ export { A11Y_NONE, ATOM_AT_REST, BAR_SLOTS, BUTTON_SIZES, CONTROL_IDLE, CONTROL_SIZES, DEFAULT_GROWTH_TIMING, FORM_MORPH_TIMING, HEIGHT_BREAKPOINTS, HEIGHT_CLASSES, INPUT_MIXED, INPUT_POINTER, INPUT_TABLET_KEYBOARD, INPUT_TOUCH, MENU_CLOSED, MENU_CORNERS, MENU_DROP_BODY, MENU_ITEM_FILL, MINIMIZE_JITTER, MINIMIZE_THRESHOLD, MIN_POINTER_TARGET_DP, MIN_TOUCH_TARGET_DP, NAV_MINIMIZE_TIMING, NAV_MORPH_TIMING, NAV_SIZES, POINTER_INPUT, POSTURE_FLAT, PRESS_IDLE, TOUCH_INPUT, TYPEAHEAD_SECONDS, WIDTH_BREAKPOINTS, WIDTH_CLASSES, adaptiveContext, aimNavMorph, atomAtRest, atomBody, atomLift, atomMaterial, bottomAccessory, cancelNavDrag, clampScrollTop, classifyWindow, controlTargets, createControlVisual, createFormMorph, createGrowthState, createMinimize, createNavDrag, createNavMorph, currentNavFrame, defaultSize, expandMinimize, fixedNavigationForm, formMorph, formMorphFrame, growthFrame, growthInk, growthPhase, halfOpen, heightClass, hitSlop, hitTarget, hoversWith, iconButtonSize, intersectRect, menuActivity, menuBody, menuCorner, menuGlass, menuItemFill, menuMaterial, menuPlacement, menuSize, menuSurfaceFrame, minTarget, moveNavDrag, navDragFrame, navLayout, navMorphFrame, navigationForm, nestedRadius, postureFromSegments, presentationFor, pressNavDrag, pressResponse, rebaseFormMorph, rectContains, releaseNavDrag, resolveA11y, sameA11y, segmentOf, settleControlVisual, sizeTier, smoothstep, stepControl, stepControlVisual, stepFocus, stepFormMorph, stepGrowth, stepHover, stepMenu, stepMinimize, stepNavDrag, stepNavMorph, stepPress, stepSpring, surfaceRegion, visualAtomState, widthAtLeast, widthClass, windowSegments } from './chunk-7A3XUTXH.js';
3
+
4
+ // src/kit/adaptive/panes.ts
5
+ var LIST_PANE_WIDTH = { expanded: 360, large: 412 };
6
+ var SUPPORTING_PANE_MAX_WIDTH = 400;
7
+ function splitFor(ctx, sparse) {
8
+ const { width, height } = classifyWindow(ctx.window);
9
+ if (ctx.posture.kind === "book") return { kind: "fold", gap: ctx.posture.fold.bounds };
10
+ if (height === "compact") return null;
11
+ if (widthAtLeast(width, "large")) return { kind: "fixed", list: LIST_PANE_WIDTH.large };
12
+ if (width === "expanded") return { kind: "fixed", list: LIST_PANE_WIDTH.expanded };
13
+ return width === "medium" && sparse ? { kind: "half" } : null;
14
+ }
15
+ function paneLayout(ctx, state) {
16
+ const split = splitFor(ctx, state.sparse ?? false);
17
+ if (split) {
18
+ return { panes: 2, visible: ["list", "detail"], split, placeholder: !state.selected, back: false, selectionShown: state.selected };
19
+ }
20
+ const detail = state.selected && state.showing === "detail";
21
+ return { panes: 1, visible: [detail ? "detail" : "list"], split: null, placeholder: false, back: detail, selectionShown: false };
22
+ }
23
+ function supportingPane(ctx, state) {
24
+ const width = classifyWindow(ctx.window).width;
25
+ const base = { edge: "trailing", maxWidth: SUPPORTING_PANE_MAX_WIDTH, remembered: state.pinned };
26
+ if (widthAtLeast(width, "large")) return { ...base, form: state.pinned || state.open ? "beside" : "hidden", scrim: false };
27
+ if (width === "expanded" && state.open) return { ...base, form: "over", scrim: true };
28
+ return { ...base, form: "hidden", scrim: false };
29
+ }
30
+ function postureSplit(ctx) {
31
+ if (ctx.posture.kind === "flat") return { kind: "single" };
32
+ const segments = windowSegments(ctx);
33
+ if (segments.length < 2) return { kind: "single" };
34
+ return { kind: "split", axis: ctx.posture.kind === "tabletop" ? "horizontal" : "vertical", primary: segments[0], secondary: segments[1] };
35
+ }
36
+
37
+ // src/kit/adaptive/presentation.ts
38
+ var SURFACE_KINDS = ["menu", "picker", "popover", "sheet", "modal-sheet", "alert", "dialog"];
39
+ var BOTTOM_SHEET_MAX_WIDTH = 640;
40
+ var SIDE_SHEET_MAX_WIDTH = 400;
41
+ var DIALOG_MAX_WIDTH = 560;
42
+ function baseForm(kind, ctx) {
43
+ const { width, height } = classifyWindow(ctx.window);
44
+ const compactWidth = width === "compact";
45
+ const plain = { fromSource: false, scrim: false, compresses: false, maxWidth: null };
46
+ const sheet = (form) => height === "compact" ? { ...plain, form: "full-screen", scrim: true } : form;
47
+ switch (kind) {
48
+ case "menu":
49
+ return { ...plain, form: "grown", fromSource: true };
50
+ case "popover":
51
+ return compactWidth ? sheet({ ...plain, form: "bottom-sheet", fromSource: true, scrim: true }) : { ...plain, form: "popover", fromSource: true };
52
+ case "picker":
53
+ case "modal-sheet":
54
+ return sheet(
55
+ compactWidth ? { ...plain, form: "bottom-sheet", scrim: true } : { ...plain, form: "centered-sheet", scrim: true, maxWidth: DIALOG_MAX_WIDTH }
56
+ );
57
+ case "sheet":
58
+ if (width === "xlarge") return { ...plain, form: "side-pane", compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };
59
+ if (widthAtLeast(width, "expanded")) return { ...plain, form: "side-sheet", compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };
60
+ return sheet({ ...plain, form: "bottom-sheet", maxWidth: compactWidth ? null : BOTTOM_SHEET_MAX_WIDTH });
61
+ case "alert":
62
+ return { ...plain, form: "alert", scrim: true, maxWidth: DIALOG_MAX_WIDTH };
63
+ case "dialog":
64
+ return compactWidth ? { ...plain, form: "full-screen", scrim: true } : { ...plain, form: "basic-dialog", scrim: true, maxWidth: DIALOG_MAX_WIDTH };
65
+ }
66
+ }
67
+ function presentationForm(kind, ctx, source) {
68
+ const base = baseForm(kind, ctx);
69
+ const side = base.form === "side-sheet" || base.form === "side-pane";
70
+ const region = side && ctx.posture.kind === "book" ? windowSegments(ctx).at(-1) : surfaceRegion(ctx, source);
71
+ return { ...base, region };
72
+ }
73
+
74
+ // src/kit/adaptive/toolbar.ts
75
+ var TOOLBAR_GAP = 8;
76
+ var MORE_ID = "\u2026";
77
+ function toolbarPlacement(ctx) {
78
+ const { width, height } = classifyWindow(ctx.window);
79
+ if (height === "compact") {
80
+ return { edge: "trailing", orientation: "vertical", maxGroups: Number.POSITIVE_INFINITY };
81
+ }
82
+ if (width === "compact") return { edge: "bottom", orientation: "horizontal", maxGroups: 3 };
83
+ return { edge: "top", orientation: "horizontal", maxGroups: Number.POSITIVE_INFINITY };
84
+ }
85
+ function toolbarLayout(items, options) {
86
+ const orientation = options.orientation ?? "horizontal";
87
+ const gap = options.gap ?? TOOLBAR_GAP;
88
+ const maxGroups = options.maxGroups ?? Number.POSITIVE_INFINITY;
89
+ const side = BUTTON_SIZES[options.tier].height;
90
+ const vertical = orientation === "vertical";
91
+ const aside = vertical ? items.filter((i) => i.kind === "text").map((i) => i.id) : [];
92
+ const pool = items.map((item, index) => ({ item, index })).filter((e) => !vertical || e.item.kind === "icon");
93
+ const extentOf = (item) => item.kind === "icon" ? side : item.width ?? 3 * side;
94
+ const lay = (visible2, merge, more) => {
95
+ const ordered = [...visible2.filter((e) => !e.item.primary)];
96
+ const capsules = [];
97
+ let open = null;
98
+ const close = () => {
99
+ if (open) capsules.push({ kind: open.kind, items: open.items, extent: open.extent });
100
+ open = null;
101
+ };
102
+ for (const { item } of ordered) {
103
+ const kind = item.kind;
104
+ const group = item.group ?? `kind:${kind}`;
105
+ if (merge && open && open.kind === kind && open.group === group) {
106
+ open.items.push(item.id);
107
+ open.extent += extentOf(item);
108
+ } else {
109
+ close();
110
+ open = { kind, group, items: [item.id], extent: extentOf(item) };
111
+ }
112
+ }
113
+ close();
114
+ if (more) capsules.push({ kind: "more", items: [MORE_ID], extent: side });
115
+ for (const { item } of visible2) if (item.primary) capsules.push({ kind: "primary", items: [item.id], extent: extentOf(item) });
116
+ const extent = capsules.reduce((sum, c) => sum + c.extent, 0) + gap * Math.max(capsules.length - 1, 0);
117
+ return { capsules, extent, fits: extent <= options.available && capsules.length <= maxGroups };
118
+ };
119
+ const done = (stage, laid2, overflow) => ({
120
+ orientation,
121
+ stage,
122
+ capsules: laid2.capsules,
123
+ overflow,
124
+ aside,
125
+ extent: laid2.extent,
126
+ fits: laid2.fits
127
+ });
128
+ const separate = lay(pool, false, false);
129
+ if (separate.fits) return done("separate", separate, []);
130
+ const merged = lay(pool, true, false);
131
+ if (merged.fits) return done("merged", merged, []);
132
+ const candidates = pool.filter((e) => !e.item.badge && !e.item.primary).sort((a, b) => (a.item.priority ?? 0) - (b.item.priority ?? 0) || b.index - a.index);
133
+ let visible = pool;
134
+ const hidden = [];
135
+ let laid = merged;
136
+ for (const candidate of candidates) {
137
+ visible = visible.filter((e) => e !== candidate);
138
+ hidden.push(candidate);
139
+ laid = lay(visible, true, true);
140
+ if (laid.fits) break;
141
+ }
142
+ if (!hidden.length) return done("merged", merged, []);
143
+ return done("overflow", laid, hidden.sort((a, b) => a.index - b.index).map((e) => e.item.id));
144
+ }
145
+
146
+ export { BOTTOM_SHEET_MAX_WIDTH, DIALOG_MAX_WIDTH, LIST_PANE_WIDTH, MORE_ID, SIDE_SHEET_MAX_WIDTH, SUPPORTING_PANE_MAX_WIDTH, SURFACE_KINDS, TOOLBAR_GAP, paneLayout, postureSplit, presentationForm, supportingPane, toolbarLayout, toolbarPlacement };
2
147
  //# sourceMappingURL=index.js.map
3
148
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
1
+ {"version":3,"sources":["../src/kit/adaptive/panes.ts","../src/kit/adaptive/presentation.ts","../src/kit/adaptive/toolbar.ts"],"names":["visible","laid"],"mappings":"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","file":"index.js","sourcesContent":["// Panes: list-detail, the supporting pane, and how a half-open posture splits the window.\nimport type { Rect } from '../geometry';\nimport { classifyWindow, widthAtLeast } from './classes';\nimport { windowSegments, type AdaptiveContext } from './context';\n\n/** The fixed list pane beside the detail (Material 3 list-detail). */\nexport const LIST_PANE_WIDTH = { expanded: 360, large: 412 } as const;\nexport const SUPPORTING_PANE_MAX_WIDTH = 400;\n\nexport type ListDetailState = {\n selected: boolean;\n /** The pane the person is on while one pane shows; the detail needs a selection. Set it to `detail` on a selection. */\n showing: 'list' | 'detail';\n /** Sparse content may split a medium window in half. */\n sparse?: boolean;\n};\n\nexport type PaneSplit =\n | { kind: 'fixed'; list: number }\n | { kind: 'half' }\n /** Book posture: the fold is the spacer between the panes. */\n | { kind: 'fold'; gap: Rect };\n\nexport type PaneLayout = {\n panes: 1 | 2;\n visible: readonly ('list' | 'detail')[];\n split: PaneSplit | null;\n /** Two panes and nothing selected: the detail shows its empty state. */\n placeholder: boolean;\n /** Back exists only where the detail replaces the list. */\n back: boolean;\n /** The selection is drawn in the list only while both panes show. */\n selectionShown: boolean;\n};\n\nfunction splitFor(ctx: AdaptiveContext, sparse: boolean): PaneSplit | null {\n const { width, height } = classifyWindow(ctx.window);\n if (ctx.posture.kind === 'book') return { kind: 'fold', gap: ctx.posture.fold.bounds };\n // Two panes are not practical on a compact height (Android canonical layouts).\n if (height === 'compact') return null;\n if (widthAtLeast(width, 'large')) return { kind: 'fixed', list: LIST_PANE_WIDTH.large };\n if (width === 'expanded') return { kind: 'fixed', list: LIST_PANE_WIDTH.expanded };\n return width === 'medium' && sparse ? { kind: 'half' } : null;\n}\n\nexport function paneLayout(ctx: AdaptiveContext, state: ListDetailState): PaneLayout {\n const split = splitFor(ctx, state.sparse ?? false);\n if (split) {\n return { panes: 2, visible: ['list', 'detail'], split, placeholder: !state.selected, back: false, selectionShown: state.selected };\n }\n const detail = state.selected && state.showing === 'detail';\n return { panes: 1, visible: [detail ? 'detail' : 'list'], split: null, placeholder: false, back: detail, selectionShown: false };\n}\n\nexport type SupportingPaneState = {\n /** The person keeps it beside the content; remembered across classes, so it returns when the window widens again. */\n pinned: boolean;\n /** Shown on demand; over the content on an expanded window. */\n open: boolean;\n};\n\nexport type SupportingPaneForm = {\n form: 'beside' | 'over' | 'hidden';\n edge: 'trailing';\n maxWidth: number;\n /** Over the content it dims what it covers, like a modal side sheet. */\n scrim: boolean;\n remembered: boolean;\n};\n\nexport function supportingPane(ctx: AdaptiveContext, state: SupportingPaneState): SupportingPaneForm {\n const width = classifyWindow(ctx.window).width;\n const base = { edge: 'trailing' as const, maxWidth: SUPPORTING_PANE_MAX_WIDTH, remembered: state.pinned };\n if (widthAtLeast(width, 'large')) return { ...base, form: state.pinned || state.open ? 'beside' : 'hidden', scrim: false };\n if (width === 'expanded' && state.open) return { ...base, form: 'over', scrim: true };\n return { ...base, form: 'hidden', scrim: false };\n}\n\nexport type PostureSplit =\n | { kind: 'single' }\n /** Tabletop: what is watched above the fold, its controls below. Book: the leading half and the trailing one. */\n | { kind: 'split'; axis: 'horizontal' | 'vertical'; primary: Rect; secondary: Rect };\n\nexport function postureSplit(ctx: AdaptiveContext): PostureSplit {\n if (ctx.posture.kind === 'flat') return { kind: 'single' };\n const segments = windowSegments(ctx);\n if (segments.length < 2) return { kind: 'single' };\n return { kind: 'split', axis: ctx.posture.kind === 'tabletop' ? 'horizontal' : 'vertical', primary: segments[0]!, secondary: segments[1]! };\n}\n","// What a transient surface becomes on each class. A form change keeps the surface open; only its form flows.\nimport type { Rect } from '../geometry';\nimport { classifyWindow, widthAtLeast } from './classes';\nimport { surfaceRegion, windowSegments, type AdaptiveContext } from './context';\n\n/**\n * `menu`: closes after one choice and grows out of its trigger on every class. `picker`: a long choice with a search,\n * which is not a menu but a sheet. `popover`: several picks, stays open. `sheet`: supplementary content beside a\n * parallel task. `modal-sheet`: a modal task with a form. `alert`: a dialog without fields. `dialog`: one with fields.\n */\nexport type SurfaceKind = 'menu' | 'picker' | 'popover' | 'sheet' | 'modal-sheet' | 'alert' | 'dialog';\n\nexport const SURFACE_KINDS: readonly SurfaceKind[] = ['menu', 'picker', 'popover', 'sheet', 'modal-sheet', 'alert', 'dialog'];\n\nexport type PresentationFormKind =\n | 'grown'\n | 'popover'\n | 'bottom-sheet'\n | 'centered-sheet'\n | 'side-sheet'\n | 'side-pane'\n | 'full-screen'\n | 'basic-dialog'\n | 'alert';\n\nexport type PresentationForm = {\n form: PresentationFormKind;\n /** It grows out of its source (and runs back into it). */\n fromSource: boolean;\n /** What lies behind is dimmed and inert. */\n scrim: boolean;\n /** It narrows the main area instead of covering it. */\n compresses: boolean;\n /** Its widest extent in dp; null where it fills its region. */\n maxWidth: number | null;\n /** Where it lives: its source's segment, or the largest; side sheets in book posture take the trailing one. */\n region: Rect;\n};\n\n/** A bottom sheet on a medium window stops growing here and centres (Material 3 bottom sheets). */\nexport const BOTTOM_SHEET_MAX_WIDTH = 640;\n/** A side sheet, and the third pane it becomes on an xlarge window. */\nexport const SIDE_SHEET_MAX_WIDTH = 400;\n/** A centred sheet and a basic dialog (Material 3 dialogs). */\nexport const DIALOG_MAX_WIDTH = 560;\n\ntype Base = Omit<PresentationForm, 'region'>;\n\nfunction baseForm(kind: SurfaceKind, ctx: AdaptiveContext): Base {\n const { width, height } = classifyWindow(ctx.window);\n const compactWidth = width === 'compact';\n const plain = { fromSource: false, scrim: false, compresses: false, maxWidth: null };\n // A sheet whose height is the constraint fills the window on a compact height; a side sheet is full height already.\n const sheet = (form: Base): Base => (height === 'compact' ? { ...plain, form: 'full-screen', scrim: true } : form);\n switch (kind) {\n case 'menu':\n return { ...plain, form: 'grown', fromSource: true };\n case 'popover':\n return compactWidth ? sheet({ ...plain, form: 'bottom-sheet', fromSource: true, scrim: true }) : { ...plain, form: 'popover', fromSource: true };\n case 'picker':\n case 'modal-sheet':\n return sheet(\n compactWidth\n ? { ...plain, form: 'bottom-sheet', scrim: true }\n : { ...plain, form: 'centered-sheet', scrim: true, maxWidth: DIALOG_MAX_WIDTH },\n );\n case 'sheet':\n if (width === 'xlarge') return { ...plain, form: 'side-pane', compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };\n if (widthAtLeast(width, 'expanded')) return { ...plain, form: 'side-sheet', compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };\n return sheet({ ...plain, form: 'bottom-sheet', maxWidth: compactWidth ? null : BOTTOM_SHEET_MAX_WIDTH });\n case 'alert':\n return { ...plain, form: 'alert', scrim: true, maxWidth: DIALOG_MAX_WIDTH };\n case 'dialog':\n return compactWidth ? { ...plain, form: 'full-screen', scrim: true } : { ...plain, form: 'basic-dialog', scrim: true, maxWidth: DIALOG_MAX_WIDTH };\n }\n}\n\nexport function presentationForm(kind: SurfaceKind, ctx: AdaptiveContext, source?: Rect): PresentationForm {\n const base = baseForm(kind, ctx);\n const side = base.form === 'side-sheet' || base.form === 'side-pane';\n const region = side && ctx.posture.kind === 'book' ? windowSegments(ctx).at(-1)! : surfaceRegion(ctx, source);\n return { ...base, region };\n}\n","// A toolbar fits its container: neighbours merge into one capsule first, then the lowest priorities go into \"…\".\n// Functions stay; only how many are visible changes, so every item is on the toolbar, in \"…\" or in the text row.\nimport { BUTTON_SIZES, type ControlSize } from '../sizes';\nimport { classifyWindow } from './classes';\nimport type { AdaptiveContext } from './context';\n\nexport type ToolbarItem = {\n id: string;\n kind: 'icon' | 'text';\n /** Higher stays visible longer; 0 by default. */\n priority?: number;\n /** A status on the item: it stays visible at any width. */\n badge?: boolean;\n /** The one tinted main action. It goes to the trailing end and never overflows. */\n primary?: boolean;\n /** Items of one group merge into one capsule; without it, neighbours of a kind share a group. */\n group?: string;\n /** A text item's width in dp, measured by the target; three tier heights by default. An icon is the tier's square. */\n width?: number;\n};\n\nexport type ToolbarOrientation = 'horizontal' | 'vertical';\n\nexport type ToolbarOptions = {\n /** The container's extent along the toolbar, dp. */\n available: number;\n tier: ControlSize;\n orientation?: ToolbarOrientation;\n maxGroups?: number;\n gap?: number;\n};\n\nexport type ToolbarCapsule = { kind: 'icon' | 'text' | 'more' | 'primary'; items: readonly string[]; extent: number };\n\nexport type ToolbarLayout = {\n orientation: ToolbarOrientation;\n stage: 'separate' | 'merged' | 'overflow';\n capsules: readonly ToolbarCapsule[];\n /** In \"…\", in their order. \"…\" exists only when this is not empty. */\n overflow: readonly string[];\n /** Text items of a vertical toolbar: text does not stand vertical, so they stay in a horizontal row. */\n aside: readonly string[];\n extent: number;\n /** False only when what may not overflow (status, the main action) is wider than the container. */\n fits: boolean;\n};\n\nexport const TOOLBAR_GAP = 8;\nexport const MORE_ID = '…';\n\nexport type ToolbarPlacement = { edge: 'bottom' | 'top' | 'trailing'; orientation: ToolbarOrientation; maxGroups: number };\n\n/** The window decides where a toolbar sits; its layout then follows its own container. */\nexport function toolbarPlacement(ctx: AdaptiveContext): ToolbarPlacement {\n const { width, height } = classifyWindow(ctx.window);\n if (height === 'compact') {\n return { edge: 'trailing', orientation: 'vertical', maxGroups: Number.POSITIVE_INFINITY };\n }\n if (width === 'compact') return { edge: 'bottom', orientation: 'horizontal', maxGroups: 3 };\n return { edge: 'top', orientation: 'horizontal', maxGroups: Number.POSITIVE_INFINITY };\n}\n\ntype Entry = { item: ToolbarItem; index: number };\n\nexport function toolbarLayout(items: readonly ToolbarItem[], options: ToolbarOptions): ToolbarLayout {\n const orientation = options.orientation ?? 'horizontal';\n const gap = options.gap ?? TOOLBAR_GAP;\n const maxGroups = options.maxGroups ?? Number.POSITIVE_INFINITY;\n const side = BUTTON_SIZES[options.tier].height;\n const vertical = orientation === 'vertical';\n const aside = vertical ? items.filter((i) => i.kind === 'text').map((i) => i.id) : [];\n const pool: Entry[] = items.map((item, index) => ({ item, index })).filter((e) => !vertical || e.item.kind === 'icon');\n const extentOf = (item: ToolbarItem) => (item.kind === 'icon' ? side : (item.width ?? 3 * side));\n\n const lay = (visible: readonly Entry[], merge: boolean, more: boolean) => {\n const ordered = [...visible.filter((e) => !e.item.primary)];\n const capsules: ToolbarCapsule[] = [];\n let open: { kind: 'icon' | 'text'; group: string; items: string[]; extent: number } | null = null;\n const close = () => {\n if (open) capsules.push({ kind: open.kind, items: open.items, extent: open.extent });\n open = null;\n };\n for (const { item } of ordered) {\n const kind = item.kind;\n const group = item.group ?? `kind:${kind}`;\n if (merge && open && open.kind === kind && open.group === group) {\n open.items.push(item.id);\n open.extent += extentOf(item);\n } else {\n close();\n open = { kind, group, items: [item.id], extent: extentOf(item) };\n }\n }\n close();\n if (more) capsules.push({ kind: 'more', items: [MORE_ID], extent: side });\n for (const { item } of visible) if (item.primary) capsules.push({ kind: 'primary', items: [item.id], extent: extentOf(item) });\n const extent = capsules.reduce((sum, c) => sum + c.extent, 0) + gap * Math.max(capsules.length - 1, 0);\n return { capsules, extent, fits: extent <= options.available && capsules.length <= maxGroups };\n };\n\n const done = (stage: ToolbarLayout['stage'], laid: ReturnType<typeof lay>, overflow: readonly string[]): ToolbarLayout => ({\n orientation,\n stage,\n capsules: laid.capsules,\n overflow,\n aside,\n extent: laid.extent,\n fits: laid.fits,\n });\n\n const separate = lay(pool, false, false);\n if (separate.fits) return done('separate', separate, []);\n const merged = lay(pool, true, false);\n if (merged.fits) return done('merged', merged, []);\n\n // Lowest priority first; among equals, the one nearest the trailing end.\n const candidates = pool\n .filter((e) => !e.item.badge && !e.item.primary)\n .sort((a, b) => (a.item.priority ?? 0) - (b.item.priority ?? 0) || b.index - a.index);\n let visible = pool;\n const hidden: Entry[] = [];\n let laid = merged;\n for (const candidate of candidates) {\n visible = visible.filter((e) => e !== candidate);\n hidden.push(candidate);\n laid = lay(visible, true, true);\n if (laid.fits) break;\n }\n if (!hidden.length) return done('merged', merged, []);\n return done('overflow', laid, hidden.sort((a, b) => a.index - b.index).map((e) => e.item.id));\n}\n"]}