vireuikit 0.7.0 → 0.8.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/README.md +52 -5
- package/dist/{chunk-AR2AM3BR.js → chunk-VY5MD3CX.js} +320 -193
- package/dist/chunk-VY5MD3CX.js.map +1 -0
- package/dist/context-DQaAcRq-.d.cts +104 -0
- package/dist/context-DQaAcRq-.d.ts +104 -0
- package/dist/index.cjs +604 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +401 -127
- package/dist/index.d.ts +401 -127
- package/dist/index.js +250 -1
- package/dist/index.js.map +1 -1
- package/dist/web.cjs +297 -99
- package/dist/web.cjs.map +1 -1
- package/dist/web.d.cts +28 -2
- package/dist/web.d.ts +28 -2
- package/dist/web.js +148 -33
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
- package/dist/a11y-D3SS74I3.d.cts +0 -25
- package/dist/a11y-D3SS74I3.d.ts +0 -25
- package/dist/chunk-AR2AM3BR.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,252 @@
|
|
|
1
|
-
|
|
1
|
+
import { DEFAULT_GROWTH_TIMING, stepSpring, smoothstep, windowSegments, surfaceRegion, BUTTON_SIZES } from './chunk-VY5MD3CX.js';
|
|
2
|
+
export { A11Y_NONE, ATOM_AT_REST, BUTTON_SIZES, CONTROL_IDLE, CONTROL_SIZES, DEFAULT_GROWTH_TIMING, INPUT_MIXED, INPUT_POINTER, INPUT_TABLET_KEYBOARD, INPUT_TOUCH, MENU_CLOSED, MENU_CORNERS, MENU_DROP_BODY, MENU_ITEM_FILL, MIN_POINTER_TARGET_DP, MIN_TOUCH_TARGET_DP, POINTER_INPUT, POSTURE_FLAT, PRESS_IDLE, TOUCH_INPUT, TYPEAHEAD_SECONDS, adaptiveContext, atomAtRest, atomBody, atomLift, atomMaterial, controlTargets, createControlVisual, createGrowthState, defaultSize, growthFrame, growthInk, growthPhase, halfOpen, hitSlop, hitTarget, hoversWith, iconButtonSize, intersectRect, menuActivity, menuBody, menuCorner, menuGlass, menuItemFill, menuMaterial, menuPlacement, menuSize, menuSurfaceFrame, minTarget, nestedRadius, postureFromSegments, presentationFor, pressResponse, rectContains, resolveA11y, sameA11y, segmentOf, settleControlVisual, sizeTier, smoothstep, stepControl, stepControlVisual, stepFocus, stepGrowth, stepHover, stepMenu, stepPress, stepSpring, surfaceRegion, visualAtomState, windowSegments } from './chunk-VY5MD3CX.js';
|
|
3
|
+
|
|
4
|
+
// src/kit/adaptive/classes.ts
|
|
5
|
+
var WIDTH_CLASSES = ["compact", "medium", "expanded", "large", "xlarge"];
|
|
6
|
+
var HEIGHT_CLASSES = ["compact", "medium", "expanded"];
|
|
7
|
+
var WIDTH_BREAKPOINTS = { medium: 600, expanded: 840, large: 1200, xlarge: 1600 };
|
|
8
|
+
var HEIGHT_BREAKPOINTS = { medium: 480, expanded: 900 };
|
|
9
|
+
function widthClass(width) {
|
|
10
|
+
if (!(width >= WIDTH_BREAKPOINTS.medium)) return "compact";
|
|
11
|
+
if (width < WIDTH_BREAKPOINTS.expanded) return "medium";
|
|
12
|
+
if (width < WIDTH_BREAKPOINTS.large) return "expanded";
|
|
13
|
+
return width < WIDTH_BREAKPOINTS.xlarge ? "large" : "xlarge";
|
|
14
|
+
}
|
|
15
|
+
function heightClass(height) {
|
|
16
|
+
if (!(height >= HEIGHT_BREAKPOINTS.medium)) return "compact";
|
|
17
|
+
return height < HEIGHT_BREAKPOINTS.expanded ? "medium" : "expanded";
|
|
18
|
+
}
|
|
19
|
+
function classifyWindow(size) {
|
|
20
|
+
return { width: widthClass(size.width), height: heightClass(size.height) };
|
|
21
|
+
}
|
|
22
|
+
function widthAtLeast(value, minimum) {
|
|
23
|
+
return WIDTH_CLASSES.indexOf(value) >= WIDTH_CLASSES.indexOf(minimum);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/kit/adaptive/form-morph.ts
|
|
27
|
+
var HIDDEN = /* @__PURE__ */ new Set(["hidden", "closed", "none"]);
|
|
28
|
+
function formMorph(change, ctx, options = {}) {
|
|
29
|
+
const instant = ctx.reduceMotion;
|
|
30
|
+
if (options.anchorGone) return { ...change, mode: "retract", staysOpen: false, instant };
|
|
31
|
+
if (change.from === change.to) return { ...change, mode: "none", staysOpen: true, instant };
|
|
32
|
+
const mode = HIDDEN.has(change.from) ? "appear" : HIDDEN.has(change.to) ? "disappear" : "reshape";
|
|
33
|
+
return { ...change, mode, staysOpen: mode !== "disappear", instant };
|
|
34
|
+
}
|
|
35
|
+
var FORM_MORPH_TIMING = { response: DEFAULT_GROWTH_TIMING.growOpen, damping: 1 };
|
|
36
|
+
var createFormMorph = () => ({ t: { x: 0, v: 0, target: 0 }, fromInk: 1 });
|
|
37
|
+
function stepFormMorph(state, dt, toward, instant = false) {
|
|
38
|
+
state.t.target = toward;
|
|
39
|
+
return stepSpring(state.t, dt, FORM_MORPH_TIMING.response, FORM_MORPH_TIMING.damping, instant);
|
|
40
|
+
}
|
|
41
|
+
var OUT_END = 0.45;
|
|
42
|
+
var IN_START = 0.55;
|
|
43
|
+
function formMorphFrame(from, to, state) {
|
|
44
|
+
const t = Math.min(Math.max(state.t.x, 0), 1);
|
|
45
|
+
const mix = (a, b) => a + (b - a) * t;
|
|
46
|
+
return {
|
|
47
|
+
box: {
|
|
48
|
+
x: mix(from.x, to.x),
|
|
49
|
+
y: mix(from.y, to.y),
|
|
50
|
+
width: mix(from.width, to.width),
|
|
51
|
+
height: mix(from.height, to.height),
|
|
52
|
+
radius: mix(from.radius, to.radius)
|
|
53
|
+
},
|
|
54
|
+
ink: inkAt(state, t),
|
|
55
|
+
showing: t < 0.5 ? "from" : "to"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function inkAt(state, t) {
|
|
59
|
+
return { outgoing: (state.fromInk ?? 1) * (1 - smoothstep(0, OUT_END, t)), incoming: smoothstep(IN_START, 1, t) };
|
|
60
|
+
}
|
|
61
|
+
function rebaseFormMorph(state) {
|
|
62
|
+
const t = Math.min(Math.max(state.t.x, 0), 1);
|
|
63
|
+
const ink = inkAt(state, t);
|
|
64
|
+
state.fromInk = t < 0.5 ? ink.outgoing : ink.incoming;
|
|
65
|
+
state.t = { x: 0, v: 0, target: 0 };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/kit/adaptive/navigation.ts
|
|
69
|
+
var BAR_SLOTS = 5;
|
|
70
|
+
function formFor(ctx, spec) {
|
|
71
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
72
|
+
if (ctx.posture.kind === "tabletop") return "bar";
|
|
73
|
+
if (widthAtLeast(width, "large")) return spec.sidebarCollapsed ? "rail" : "sidebar";
|
|
74
|
+
if (height === "compact") return "rail";
|
|
75
|
+
if (width === "compact") return "bar";
|
|
76
|
+
if (width === "medium" && (spec.panes ?? 1) > 1 && ctx.posture.kind !== "book") return "bar";
|
|
77
|
+
return "rail";
|
|
78
|
+
}
|
|
79
|
+
function navigationForm(ctx, spec) {
|
|
80
|
+
const form = formFor(ctx, spec);
|
|
81
|
+
const width = classifyWindow(ctx.window).width;
|
|
82
|
+
const large = widthAtLeast(width, "large");
|
|
83
|
+
const slots = BAR_SLOTS - (spec.search ? 1 : 0);
|
|
84
|
+
const primary = Math.max(0, Math.min(spec.primary, slots));
|
|
85
|
+
const secondary = Math.max(0, spec.secondary ?? 0) + Math.max(0, spec.primary - primary);
|
|
86
|
+
let search = null;
|
|
87
|
+
if (spec.search) {
|
|
88
|
+
const shortcut = large || ctx.input.keyboard;
|
|
89
|
+
if (large) search = { form: "field", place: form === "sidebar" ? "sidebar" : "toolbar", shortcut };
|
|
90
|
+
else search = form === "bar" ? { form: "button", place: "bar", shortcut } : { form: "item", place: "rail", shortcut };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
form,
|
|
94
|
+
edge: form === "bar" ? "bottom" : "leading",
|
|
95
|
+
primary,
|
|
96
|
+
secondary,
|
|
97
|
+
secondaryVisible: form === "sidebar",
|
|
98
|
+
search,
|
|
99
|
+
expandable: form !== "rail" ? null : large ? "beside" : width === "expanded" ? "over" : null,
|
|
100
|
+
collapsible: form === "sidebar"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function bottomAccessory(nav, state = {}) {
|
|
104
|
+
if (nav.form !== "bar") return { placement: "content-bottom", compact: false };
|
|
105
|
+
return state.barMinimized ? { placement: "in-bar", compact: true } : { placement: "above-bar", compact: false };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/kit/adaptive/panes.ts
|
|
109
|
+
var LIST_PANE_WIDTH = { expanded: 360, large: 412 };
|
|
110
|
+
var SUPPORTING_PANE_MAX_WIDTH = 400;
|
|
111
|
+
function splitFor(ctx, sparse) {
|
|
112
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
113
|
+
if (ctx.posture.kind === "book") return { kind: "fold", gap: ctx.posture.fold.bounds };
|
|
114
|
+
if (height === "compact") return null;
|
|
115
|
+
if (widthAtLeast(width, "large")) return { kind: "fixed", list: LIST_PANE_WIDTH.large };
|
|
116
|
+
if (width === "expanded") return { kind: "fixed", list: LIST_PANE_WIDTH.expanded };
|
|
117
|
+
return width === "medium" && sparse ? { kind: "half" } : null;
|
|
118
|
+
}
|
|
119
|
+
function paneLayout(ctx, state) {
|
|
120
|
+
const split = splitFor(ctx, state.sparse ?? false);
|
|
121
|
+
if (split) {
|
|
122
|
+
return { panes: 2, visible: ["list", "detail"], split, placeholder: !state.selected, back: false, selectionShown: state.selected };
|
|
123
|
+
}
|
|
124
|
+
const detail = state.selected && state.showing === "detail";
|
|
125
|
+
return { panes: 1, visible: [detail ? "detail" : "list"], split: null, placeholder: false, back: detail, selectionShown: false };
|
|
126
|
+
}
|
|
127
|
+
function supportingPane(ctx, state) {
|
|
128
|
+
const width = classifyWindow(ctx.window).width;
|
|
129
|
+
const base = { edge: "trailing", maxWidth: SUPPORTING_PANE_MAX_WIDTH, remembered: state.pinned };
|
|
130
|
+
if (widthAtLeast(width, "large")) return { ...base, form: state.pinned || state.open ? "beside" : "hidden", scrim: false };
|
|
131
|
+
if (width === "expanded" && state.open) return { ...base, form: "over", scrim: true };
|
|
132
|
+
return { ...base, form: "hidden", scrim: false };
|
|
133
|
+
}
|
|
134
|
+
function postureSplit(ctx) {
|
|
135
|
+
if (ctx.posture.kind === "flat") return { kind: "single" };
|
|
136
|
+
const segments = windowSegments(ctx);
|
|
137
|
+
if (segments.length < 2) return { kind: "single" };
|
|
138
|
+
return { kind: "split", axis: ctx.posture.kind === "tabletop" ? "horizontal" : "vertical", primary: segments[0], secondary: segments[1] };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/kit/adaptive/presentation.ts
|
|
142
|
+
var SURFACE_KINDS = ["menu", "picker", "popover", "sheet", "modal-sheet", "alert", "dialog"];
|
|
143
|
+
var BOTTOM_SHEET_MAX_WIDTH = 640;
|
|
144
|
+
var SIDE_SHEET_MAX_WIDTH = 400;
|
|
145
|
+
var DIALOG_MAX_WIDTH = 560;
|
|
146
|
+
function baseForm(kind, ctx) {
|
|
147
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
148
|
+
const compactWidth = width === "compact";
|
|
149
|
+
const plain = { fromSource: false, scrim: false, compresses: false, maxWidth: null };
|
|
150
|
+
const sheet = (form) => height === "compact" ? { ...plain, form: "full-screen", scrim: true } : form;
|
|
151
|
+
switch (kind) {
|
|
152
|
+
case "menu":
|
|
153
|
+
return { ...plain, form: "grown", fromSource: true };
|
|
154
|
+
case "popover":
|
|
155
|
+
return compactWidth ? sheet({ ...plain, form: "bottom-sheet", fromSource: true, scrim: true }) : { ...plain, form: "popover", fromSource: true };
|
|
156
|
+
case "picker":
|
|
157
|
+
case "modal-sheet":
|
|
158
|
+
return sheet(
|
|
159
|
+
compactWidth ? { ...plain, form: "bottom-sheet", scrim: true } : { ...plain, form: "centered-sheet", scrim: true, maxWidth: DIALOG_MAX_WIDTH }
|
|
160
|
+
);
|
|
161
|
+
case "sheet":
|
|
162
|
+
if (width === "xlarge") return { ...plain, form: "side-pane", compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };
|
|
163
|
+
if (widthAtLeast(width, "expanded")) return { ...plain, form: "side-sheet", compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };
|
|
164
|
+
return sheet({ ...plain, form: "bottom-sheet", maxWidth: compactWidth ? null : BOTTOM_SHEET_MAX_WIDTH });
|
|
165
|
+
case "alert":
|
|
166
|
+
return { ...plain, form: "alert", scrim: true, maxWidth: DIALOG_MAX_WIDTH };
|
|
167
|
+
case "dialog":
|
|
168
|
+
return compactWidth ? { ...plain, form: "full-screen", scrim: true } : { ...plain, form: "basic-dialog", scrim: true, maxWidth: DIALOG_MAX_WIDTH };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function presentationForm(kind, ctx, source) {
|
|
172
|
+
const base = baseForm(kind, ctx);
|
|
173
|
+
const side = base.form === "side-sheet" || base.form === "side-pane";
|
|
174
|
+
const region = side && ctx.posture.kind === "book" ? windowSegments(ctx).at(-1) : surfaceRegion(ctx, source);
|
|
175
|
+
return { ...base, region };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/kit/adaptive/toolbar.ts
|
|
179
|
+
var TOOLBAR_GAP = 8;
|
|
180
|
+
var MORE_ID = "\u2026";
|
|
181
|
+
function toolbarPlacement(ctx) {
|
|
182
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
183
|
+
if (height === "compact") {
|
|
184
|
+
return { edge: "trailing", orientation: "vertical", maxGroups: Number.POSITIVE_INFINITY };
|
|
185
|
+
}
|
|
186
|
+
if (width === "compact") return { edge: "bottom", orientation: "horizontal", maxGroups: 3 };
|
|
187
|
+
return { edge: "top", orientation: "horizontal", maxGroups: Number.POSITIVE_INFINITY };
|
|
188
|
+
}
|
|
189
|
+
function toolbarLayout(items, options) {
|
|
190
|
+
const orientation = options.orientation ?? "horizontal";
|
|
191
|
+
const gap = options.gap ?? TOOLBAR_GAP;
|
|
192
|
+
const maxGroups = options.maxGroups ?? Number.POSITIVE_INFINITY;
|
|
193
|
+
const side = BUTTON_SIZES[options.tier].height;
|
|
194
|
+
const vertical = orientation === "vertical";
|
|
195
|
+
const aside = vertical ? items.filter((i) => i.kind === "text").map((i) => i.id) : [];
|
|
196
|
+
const pool = items.map((item, index) => ({ item, index })).filter((e) => !vertical || e.item.kind === "icon");
|
|
197
|
+
const extentOf = (item) => item.kind === "icon" ? side : item.width ?? 3 * side;
|
|
198
|
+
const lay = (visible2, merge, more) => {
|
|
199
|
+
const ordered = [...visible2.filter((e) => !e.item.primary)];
|
|
200
|
+
const capsules = [];
|
|
201
|
+
let open = null;
|
|
202
|
+
const close = () => {
|
|
203
|
+
if (open) capsules.push({ kind: open.kind, items: open.items, extent: open.extent });
|
|
204
|
+
open = null;
|
|
205
|
+
};
|
|
206
|
+
for (const { item } of ordered) {
|
|
207
|
+
const kind = item.kind;
|
|
208
|
+
const group = item.group ?? `kind:${kind}`;
|
|
209
|
+
if (merge && open && open.kind === kind && open.group === group) {
|
|
210
|
+
open.items.push(item.id);
|
|
211
|
+
open.extent += extentOf(item);
|
|
212
|
+
} else {
|
|
213
|
+
close();
|
|
214
|
+
open = { kind, group, items: [item.id], extent: extentOf(item) };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
close();
|
|
218
|
+
if (more) capsules.push({ kind: "more", items: [MORE_ID], extent: side });
|
|
219
|
+
for (const { item } of visible2) if (item.primary) capsules.push({ kind: "primary", items: [item.id], extent: extentOf(item) });
|
|
220
|
+
const extent = capsules.reduce((sum, c) => sum + c.extent, 0) + gap * Math.max(capsules.length - 1, 0);
|
|
221
|
+
return { capsules, extent, fits: extent <= options.available && capsules.length <= maxGroups };
|
|
222
|
+
};
|
|
223
|
+
const done = (stage, laid2, overflow) => ({
|
|
224
|
+
orientation,
|
|
225
|
+
stage,
|
|
226
|
+
capsules: laid2.capsules,
|
|
227
|
+
overflow,
|
|
228
|
+
aside,
|
|
229
|
+
extent: laid2.extent,
|
|
230
|
+
fits: laid2.fits
|
|
231
|
+
});
|
|
232
|
+
const separate = lay(pool, false, false);
|
|
233
|
+
if (separate.fits) return done("separate", separate, []);
|
|
234
|
+
const merged = lay(pool, true, false);
|
|
235
|
+
if (merged.fits) return done("merged", merged, []);
|
|
236
|
+
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);
|
|
237
|
+
let visible = pool;
|
|
238
|
+
const hidden = [];
|
|
239
|
+
let laid = merged;
|
|
240
|
+
for (const candidate of candidates) {
|
|
241
|
+
visible = visible.filter((e) => e !== candidate);
|
|
242
|
+
hidden.push(candidate);
|
|
243
|
+
laid = lay(visible, true, true);
|
|
244
|
+
if (laid.fits) break;
|
|
245
|
+
}
|
|
246
|
+
if (!hidden.length) return done("merged", merged, []);
|
|
247
|
+
return done("overflow", laid, hidden.sort((a, b) => a.index - b.index).map((e) => e.item.id));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { BAR_SLOTS, BOTTOM_SHEET_MAX_WIDTH, DIALOG_MAX_WIDTH, FORM_MORPH_TIMING, HEIGHT_BREAKPOINTS, HEIGHT_CLASSES, LIST_PANE_WIDTH, MORE_ID, SIDE_SHEET_MAX_WIDTH, SUPPORTING_PANE_MAX_WIDTH, SURFACE_KINDS, TOOLBAR_GAP, WIDTH_BREAKPOINTS, WIDTH_CLASSES, bottomAccessory, classifyWindow, createFormMorph, formMorph, formMorphFrame, heightClass, navigationForm, paneLayout, postureSplit, presentationForm, rebaseFormMorph, stepFormMorph, supportingPane, toolbarLayout, toolbarPlacement, widthAtLeast, widthClass };
|
|
2
251
|
//# sourceMappingURL=index.js.map
|
|
3
252
|
//# 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/classes.ts","../src/kit/adaptive/form-morph.ts","../src/kit/adaptive/navigation.ts","../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":["// Window classes: every threshold in one place. Width from the window, never from the device.\nimport type { Size } from '../geometry';\n\nexport type WidthClass = 'compact' | 'medium' | 'expanded' | 'large' | 'xlarge';\nexport type HeightClass = 'compact' | 'medium' | 'expanded';\n\nexport const WIDTH_CLASSES: readonly WidthClass[] = ['compact', 'medium', 'expanded', 'large', 'xlarge'];\nexport const HEIGHT_CLASSES: readonly HeightClass[] = ['compact', 'medium', 'expanded'];\n\n/** The lower bound of each class in dp; compact starts at 0. */\nexport const WIDTH_BREAKPOINTS = { medium: 600, expanded: 840, large: 1200, xlarge: 1600 } as const;\nexport const HEIGHT_BREAKPOINTS = { medium: 480, expanded: 900 } as const;\n\nexport type WindowClass = { width: WidthClass; height: HeightClass };\n\nexport function widthClass(width: number): WidthClass {\n if (!(width >= WIDTH_BREAKPOINTS.medium)) return 'compact';\n if (width < WIDTH_BREAKPOINTS.expanded) return 'medium';\n if (width < WIDTH_BREAKPOINTS.large) return 'expanded';\n return width < WIDTH_BREAKPOINTS.xlarge ? 'large' : 'xlarge';\n}\n\nexport function heightClass(height: number): HeightClass {\n if (!(height >= HEIGHT_BREAKPOINTS.medium)) return 'compact';\n return height < HEIGHT_BREAKPOINTS.expanded ? 'medium' : 'expanded';\n}\n\nexport function classifyWindow(size: Size): WindowClass {\n return { width: widthClass(size.width), height: heightClass(size.height) };\n}\n\nexport function widthAtLeast(value: WidthClass, minimum: WidthClass): boolean {\n return WIDTH_CLASSES.indexOf(value) >= WIDTH_CLASSES.indexOf(minimum);\n}\n","// A class change flows a body from one form into the next: one body reshaped on the growth's springs, never a swap\n// or a crossfade. Turning back mid-way runs the same spring the other way; reduced motion lands in one frame.\nimport type { Rect } from '../geometry';\nimport { DEFAULT_GROWTH_TIMING, smoothstep, stepSpring, type Spring } from '../morph';\nimport type { AdaptiveContext } from './context';\n\nexport type MorphBody = 'navigation' | 'surface' | 'panes' | 'supporting' | 'accessory' | 'toolbar';\n\nexport type FormChange = { body: MorphBody; from: string; to: string };\n\nexport type FormMorphDescriptor = FormChange & {\n /**\n * `reshape`: the one body changes its box and its content refocuses. `appear` and `disappear`: the body comes and goes by\n * modulating its lens, never by fading. `retract`: its anchor is gone, so it runs back into the anchor's new owner and\n * closes. `none`: nothing moves.\n */\n mode: 'none' | 'reshape' | 'appear' | 'disappear' | 'retract';\n /** An open surface stays open through a change of form. */\n staysOpen: boolean;\n instant: boolean;\n};\n\nconst HIDDEN = new Set(['hidden', 'closed', 'none']);\n\nexport function formMorph(change: FormChange, ctx: Pick<AdaptiveContext, 'reduceMotion'>, options: { anchorGone?: boolean } = {}): FormMorphDescriptor {\n const instant = ctx.reduceMotion;\n if (options.anchorGone) return { ...change, mode: 'retract', staysOpen: false, instant };\n if (change.from === change.to) return { ...change, mode: 'none', staysOpen: true, instant };\n const mode = HIDDEN.has(change.from) ? 'appear' : HIDDEN.has(change.to) ? 'disappear' : 'reshape';\n return { ...change, mode, staysOpen: mode !== 'disappear', instant };\n}\n\n/** The menu's opening response, so a class change moves at the pace of the growth; critically damped, because a\n * layout that overshoots would bounce off the window's edge. */\nexport const FORM_MORPH_TIMING = { response: DEFAULT_GROWTH_TIMING.growOpen, damping: 1 } as const;\n\n/** 0 is the form it comes from, 1 the form it goes to. */\nexport type FormMorphState = { t: Spring; fromInk?: number };\n\nexport const createFormMorph = (): FormMorphState => ({ t: { x: 0, v: 0, target: 0 }, fromInk: 1 });\n\n/** Steps toward a form; true while still moving. Called with the other end mid-way, it turns back from where it is. */\nexport function stepFormMorph(state: FormMorphState, dt: number, toward: 0 | 1, instant = false): boolean {\n state.t.target = toward;\n return stepSpring(state.t, dt, FORM_MORPH_TIMING.response, FORM_MORPH_TIMING.damping, instant);\n}\n\nexport type MorphBox = Rect & { radius: number };\n\nexport type FormMorphFrame = {\n box: MorphBox;\n /** The content defocuses out of the old form and into the new one; both are 0 around the middle. */\n ink: { outgoing: number; incoming: number };\n /** Which form's content shows. */\n showing: 'from' | 'to';\n};\n\nconst OUT_END = 0.45;\nconst IN_START = 0.55;\n\nexport function formMorphFrame(from: MorphBox, to: MorphBox, state: FormMorphState): FormMorphFrame {\n const t = Math.min(Math.max(state.t.x, 0), 1);\n const mix = (a: number, b: number) => a + (b - a) * t;\n return {\n box: {\n x: mix(from.x, to.x),\n y: mix(from.y, to.y),\n width: mix(from.width, to.width),\n height: mix(from.height, to.height),\n radius: mix(from.radius, to.radius),\n },\n ink: inkAt(state, t),\n showing: t < 0.5 ? 'from' : 'to',\n };\n}\n\nfunction inkAt(state: FormMorphState, t: number): FormMorphFrame['ink'] {\n return { outgoing: (state.fromInk ?? 1) * (1 - smoothstep(0, OUT_END, t)), incoming: smoothstep(IN_START, 1, t) };\n}\n\n/**\n * A third form while a morph is under way: the box it is in now becomes the new start and the content showing now\n * leaves from the ink it has, so nothing jumps. A rebased start is a moment, not a form; the morph only goes on from it.\n */\nexport function rebaseFormMorph(state: FormMorphState): void {\n const t = Math.min(Math.max(state.t.x, 0), 1);\n const ink = inkAt(state, t);\n state.fromInk = t < 0.5 ? ink.outgoing : ink.incoming;\n state.t = { x: 0, v: 0, target: 0 };\n}\n","// Navigation is one body in three forms: a bar at the bottom, a rail and a sidebar at the leading edge.\nimport { classifyWindow, widthAtLeast } from './classes';\nimport type { AdaptiveContext } from './context';\n\nexport type NavigationSpec = {\n /** Destinations shown on every form. */\n primary: number;\n /** Destinations shown only in the sidebar and in a rail expanded over the content. */\n secondary?: number;\n search?: boolean;\n /** The person collapsed the sidebar into a rail; the choice is remembered. */\n sidebarCollapsed?: boolean;\n /** Panes the content shows; two panes on a medium window take the bar. */\n panes?: number;\n};\n\nexport type NavigationFormKind = 'bar' | 'rail' | 'sidebar';\n\nexport type SearchForm = {\n /** A button at the bar's trailing end, an item of the rail, or a persistent field. */\n form: 'button' | 'item' | 'field';\n place: 'bar' | 'rail' | 'sidebar' | 'toolbar';\n /** A keyboard shortcut focuses it. */\n shortcut: boolean;\n};\n\nexport type NavigationForm = {\n form: NavigationFormKind;\n edge: 'bottom' | 'leading';\n /** The same primary destinations on every form, so the body that changes form keeps its items. */\n primary: number;\n /** The secondary destinations plus those the bar has no slot for. */\n secondary: number;\n secondaryVisible: boolean;\n search: SearchForm | null;\n /** A rail that opens wider: over the content on an expanded window, beside it when the person collapsed the sidebar. */\n expandable: 'over' | 'beside' | null;\n /** A sidebar the person can collapse into a rail. */\n collapsible: boolean;\n};\n\n/** Slots on the bar, the search button included. There is no \"More\" slot. */\nexport const BAR_SLOTS = 5;\n\nfunction formFor(ctx: AdaptiveContext, spec: NavigationSpec): NavigationFormKind {\n const { width, height } = classifyWindow(ctx.window);\n if (ctx.posture.kind === 'tabletop') return 'bar';\n if (widthAtLeast(width, 'large')) return spec.sidebarCollapsed ? 'rail' : 'sidebar';\n // A bottom bar would take the height a compact window has least of; a sidebar would not, hence below large only.\n if (height === 'compact') return 'rail';\n if (width === 'compact') return 'bar';\n if (width === 'medium' && (spec.panes ?? 1) > 1 && ctx.posture.kind !== 'book') return 'bar';\n return 'rail';\n}\n\nexport function navigationForm(ctx: AdaptiveContext, spec: NavigationSpec): NavigationForm {\n const form = formFor(ctx, spec);\n const width = classifyWindow(ctx.window).width;\n const large = widthAtLeast(width, 'large');\n const slots = BAR_SLOTS - (spec.search ? 1 : 0);\n const primary = Math.max(0, Math.min(spec.primary, slots));\n const secondary = Math.max(0, spec.secondary ?? 0) + Math.max(0, spec.primary - primary);\n let search: SearchForm | null = null;\n if (spec.search) {\n const shortcut = large || ctx.input.keyboard;\n if (large) search = { form: 'field', place: form === 'sidebar' ? 'sidebar' : 'toolbar', shortcut };\n else search = form === 'bar' ? { form: 'button', place: 'bar', shortcut } : { form: 'item', place: 'rail', shortcut };\n }\n return {\n form,\n edge: form === 'bar' ? 'bottom' : 'leading',\n primary,\n secondary,\n secondaryVisible: form === 'sidebar',\n search,\n expandable: form !== 'rail' ? null : large ? 'beside' : width === 'expanded' ? 'over' : null,\n collapsible: form === 'sidebar',\n };\n}\n\nexport type BottomAccessoryForm = {\n /** Above the bar; in the bar's row once it has minimised; along the bottom of the content beside a rail or sidebar. */\n placement: 'above-bar' | 'in-bar' | 'content-bottom';\n /** In the bar's row only the transport fits. */\n compact: boolean;\n};\n\nexport function bottomAccessory(nav: Pick<NavigationForm, 'form'>, state: { barMinimized?: boolean } = {}): BottomAccessoryForm {\n if (nav.form !== 'bar') return { placement: 'content-bottom', compact: false };\n return state.barMinimized ? { placement: 'in-bar', compact: true } : { placement: 'above-bar', compact: false };\n}\n","// 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"]}
|