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/README.md +120 -5
- package/dist/chunk-7A3XUTXH.js +1314 -0
- package/dist/chunk-7A3XUTXH.js.map +1 -0
- package/dist/context-DQaAcRq-.d.cts +104 -0
- package/dist/context-DQaAcRq-.d.ts +104 -0
- package/dist/index.cjs +1155 -186
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +585 -127
- package/dist/index.d.ts +585 -127
- package/dist/index.js +146 -1
- package/dist/index.js.map +1 -1
- package/dist/web.cjs +2240 -130
- package/dist/web.cjs.map +1 -1
- package/dist/web.d.cts +37 -3
- package/dist/web.d.ts +37 -3
- package/dist/web.js +1460 -48
- 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 +0 -557
- package/dist/chunk-AR2AM3BR.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -27,135 +27,129 @@ function presentationFor(mode, wantsGlass) {
|
|
|
27
27
|
function sameA11y(a, b) {
|
|
28
28
|
return a.reduceTransparency === b.reduceTransparency && a.increaseContrast === b.increaseContrast && a.reduceMotion === b.reduceMotion;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
};
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return 1;
|
|
42
|
-
default:
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
30
|
+
|
|
31
|
+
// src/kit/adaptive/classes.ts
|
|
32
|
+
var WIDTH_CLASSES = ["compact", "medium", "expanded", "large", "xlarge"];
|
|
33
|
+
var HEIGHT_CLASSES = ["compact", "medium", "expanded"];
|
|
34
|
+
var WIDTH_BREAKPOINTS = { medium: 600, expanded: 840, large: 1200, xlarge: 1600 };
|
|
35
|
+
var HEIGHT_BREAKPOINTS = { medium: 480, expanded: 900 };
|
|
36
|
+
function widthClass(width) {
|
|
37
|
+
if (!(width >= WIDTH_BREAKPOINTS.medium)) return "compact";
|
|
38
|
+
if (width < WIDTH_BREAKPOINTS.expanded) return "medium";
|
|
39
|
+
if (width < WIDTH_BREAKPOINTS.large) return "expanded";
|
|
40
|
+
return width < WIDTH_BREAKPOINTS.xlarge ? "large" : "xlarge";
|
|
45
41
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (state.disabled) return 0;
|
|
50
|
-
const clamp2 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
51
|
-
return Math.max(clamp2(state.selected), clamp2(state.pressed), clamp2(state.hovered) * HOVER_GAIN);
|
|
42
|
+
function heightClass(height) {
|
|
43
|
+
if (!(height >= HEIGHT_BREAKPOINTS.medium)) return "compact";
|
|
44
|
+
return height < HEIGHT_BREAKPOINTS.expanded ? "medium" : "expanded";
|
|
52
45
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
if (!state.disabled) return out;
|
|
56
|
-
return { ...out, presence: out.presence * DISABLED_PRESENCE };
|
|
46
|
+
function classifyWindow(size) {
|
|
47
|
+
return { width: widthClass(size.width), height: heightClass(size.height) };
|
|
57
48
|
}
|
|
58
|
-
function
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
var REST_BODY = { alpha: 0.25, white: 0.15, lift: 0.1 };
|
|
62
|
-
var PRESSED_BODY = { alpha: 0.34, white: 0.35, lift: 0 };
|
|
63
|
-
var FROST_LIFT = 0.26;
|
|
64
|
-
function atomBody(base, state = ATOM_AT_REST, frost = false) {
|
|
65
|
-
const on = activation(state);
|
|
66
|
-
const mix2 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
|
|
67
|
-
let alpha = mix2("alpha");
|
|
68
|
-
let white = mix2("white");
|
|
69
|
-
let lift = mix2("lift");
|
|
70
|
-
if (state.disabled) alpha *= DISABLED_PRESENCE;
|
|
71
|
-
if (frost) {
|
|
72
|
-
const density = vireglass.applyAccessibility(vireglass.resolveOptics(base), {
|
|
73
|
-
reduceTransparency: true,
|
|
74
|
-
increaseContrast: false,
|
|
75
|
-
reduceMotion: false
|
|
76
|
-
}).bodyDensity;
|
|
77
|
-
alpha = Math.max(alpha, density);
|
|
78
|
-
white = 0;
|
|
79
|
-
lift = FROST_LIFT;
|
|
80
|
-
}
|
|
81
|
-
return { alpha, white, lift };
|
|
49
|
+
function widthAtLeast(value, minimum) {
|
|
50
|
+
return WIDTH_CLASSES.indexOf(value) >= WIDTH_CLASSES.indexOf(minimum);
|
|
82
51
|
}
|
|
83
52
|
|
|
84
|
-
// src/kit/
|
|
85
|
-
function
|
|
86
|
-
|
|
53
|
+
// src/kit/geometry.ts
|
|
54
|
+
function intersectRect(a, b) {
|
|
55
|
+
const x = Math.max(a.x, b.x);
|
|
56
|
+
const y = Math.max(a.y, b.y);
|
|
57
|
+
const right = Math.min(a.x + a.width, b.x + b.width);
|
|
58
|
+
const bottom = Math.min(a.y + a.height, b.y + b.height);
|
|
59
|
+
return right > x && bottom > y ? { x, y, width: right - x, height: bottom - y } : null;
|
|
87
60
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
function stepHover(event, canHover) {
|
|
91
|
-
return canHover && event.type === "enter";
|
|
61
|
+
function rectContains(outer, inner, tolerance = 0) {
|
|
62
|
+
return inner.x >= outer.x - tolerance && inner.y >= outer.y - tolerance && inner.x + inner.width <= outer.x + outer.width + tolerance && inner.y + inner.height <= outer.y + outer.height + tolerance;
|
|
92
63
|
}
|
|
93
64
|
|
|
94
|
-
// src/kit/
|
|
95
|
-
var
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
65
|
+
// src/kit/adaptive/context.ts
|
|
66
|
+
var INPUT_TOUCH = { primary: "coarse", anyCoarse: true, hover: false, keyboard: false };
|
|
67
|
+
var INPUT_POINTER = { primary: "fine", anyCoarse: false, hover: true, keyboard: true };
|
|
68
|
+
var INPUT_MIXED = { primary: "fine", anyCoarse: true, hover: true, keyboard: true };
|
|
69
|
+
var INPUT_TABLET_KEYBOARD = { primary: "coarse", anyCoarse: true, hover: true, keyboard: true };
|
|
70
|
+
var POSTURE_FLAT = { kind: "flat" };
|
|
71
|
+
function adaptiveContext(partial) {
|
|
72
|
+
return {
|
|
73
|
+
window: partial.window,
|
|
74
|
+
input: partial.input ?? INPUT_POINTER,
|
|
75
|
+
posture: partial.posture ?? POSTURE_FLAT,
|
|
76
|
+
reduceMotion: partial.reduceMotion ?? false
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function halfOpen(kind, window, options = {}) {
|
|
80
|
+
const band = Math.max(options.band ?? 0, 0);
|
|
81
|
+
const span = kind === "tabletop" ? window.height : window.width;
|
|
82
|
+
const at = options.at ?? span / 2;
|
|
83
|
+
const start = at - band / 2;
|
|
84
|
+
const bounds = kind === "tabletop" ? { x: 0, y: start, width: window.width, height: band } : { x: start, y: 0, width: band, height: window.height };
|
|
85
|
+
return { kind, fold: { bounds, occluding: options.occluding ?? band > 0 } };
|
|
86
|
+
}
|
|
87
|
+
function windowSegments(ctx) {
|
|
88
|
+
const { width, height } = ctx.window;
|
|
89
|
+
const whole = { x: 0, y: 0, width, height };
|
|
90
|
+
if (ctx.posture.kind === "flat") return [whole];
|
|
91
|
+
const { bounds, occluding } = ctx.posture.fold;
|
|
92
|
+
const tabletop = ctx.posture.kind === "tabletop";
|
|
93
|
+
const span = tabletop ? height : width;
|
|
94
|
+
const lo = Math.min(Math.max(tabletop ? bounds.y : bounds.x, 0), span);
|
|
95
|
+
const hi = Math.min(Math.max(lo + (tabletop ? bounds.height : bounds.width), lo), span);
|
|
96
|
+
const end = occluding ? lo : (lo + hi) / 2;
|
|
97
|
+
const start = occluding ? hi : (lo + hi) / 2;
|
|
98
|
+
const parts = tabletop ? [
|
|
99
|
+
{ x: 0, y: 0, width, height: end },
|
|
100
|
+
{ x: 0, y: start, width, height: height - start }
|
|
101
|
+
] : [
|
|
102
|
+
{ x: 0, y: 0, width: end, height },
|
|
103
|
+
{ x: start, y: 0, width: width - start, height }
|
|
104
|
+
];
|
|
105
|
+
const kept = parts.filter((r) => r.width > 0 && r.height > 0);
|
|
106
|
+
return kept.length ? kept : [whole];
|
|
107
|
+
}
|
|
108
|
+
function postureFromSegments(window, segments) {
|
|
109
|
+
if (segments.length !== 2) return POSTURE_FLAT;
|
|
110
|
+
const [a, b] = [...segments].sort((p, q) => p.x - q.x || p.y - q.y);
|
|
111
|
+
const gapX = b.x - (a.x + a.width);
|
|
112
|
+
if (gapX >= 0 && a.y < b.y + b.height && b.y < a.y + a.height) {
|
|
113
|
+
return { kind: "book", fold: { bounds: { x: a.x + a.width, y: 0, width: gapX, height: window.height }, occluding: gapX > 0 } };
|
|
108
114
|
}
|
|
115
|
+
const [top, bottom] = [...segments].sort((p, q) => p.y - q.y);
|
|
116
|
+
const gapY = bottom.y - (top.y + top.height);
|
|
117
|
+
if (gapY >= 0) {
|
|
118
|
+
return { kind: "tabletop", fold: { bounds: { x: 0, y: top.y + top.height, width: window.width, height: gapY }, occluding: gapY > 0 } };
|
|
119
|
+
}
|
|
120
|
+
return POSTURE_FLAT;
|
|
109
121
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
case "up":
|
|
124
|
-
case "cancel": {
|
|
125
|
-
const next = stepPress(state.press, event, !state.disabled);
|
|
126
|
-
return { state: { ...state, press: next.state }, activated: next.activated };
|
|
127
|
-
}
|
|
128
|
-
case "leave": {
|
|
129
|
-
const next = stepPress(state.press, event, !state.disabled);
|
|
130
|
-
return { state: { ...state, press: next.state, hovered: false }, activated: false };
|
|
122
|
+
var area = (r) => r ? r.width * r.height : 0;
|
|
123
|
+
function segmentOf(box2, segments) {
|
|
124
|
+
const cx = box2.x + box2.width / 2;
|
|
125
|
+
const cy = box2.y + box2.height / 2;
|
|
126
|
+
const holding = segments.find((s) => cx >= s.x && cx < s.x + s.width && cy >= s.y && cy < s.y + s.height);
|
|
127
|
+
if (holding) return holding;
|
|
128
|
+
let best;
|
|
129
|
+
let most = 0;
|
|
130
|
+
for (const s of segments) {
|
|
131
|
+
const overlap = area(intersectRect(s, box2));
|
|
132
|
+
if (overlap > most) {
|
|
133
|
+
most = overlap;
|
|
134
|
+
best = s;
|
|
131
135
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
state: { ...state, press, focused: !state.disabled && stepFocus(event) },
|
|
142
|
-
activated: false
|
|
143
|
-
};
|
|
136
|
+
}
|
|
137
|
+
if (best) return best;
|
|
138
|
+
let nearest;
|
|
139
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
140
|
+
for (const s of segments) {
|
|
141
|
+
const d = Math.hypot(s.x + s.width / 2 - cx, s.y + s.height / 2 - cy);
|
|
142
|
+
if (d < distance) {
|
|
143
|
+
distance = d;
|
|
144
|
+
nearest = s;
|
|
144
145
|
}
|
|
145
|
-
case "disable":
|
|
146
|
-
return {
|
|
147
|
-
state: event.disabled ? { press: PRESS_IDLE, hovered: false, focused: false, disabled: true } : { ...state, disabled: false },
|
|
148
|
-
activated: false
|
|
149
|
-
};
|
|
150
146
|
}
|
|
147
|
+
return nearest;
|
|
151
148
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
focus: state.focused ? 1 : 0,
|
|
157
|
-
disabled: state.disabled
|
|
158
|
-
};
|
|
149
|
+
function surfaceRegion(ctx, source) {
|
|
150
|
+
const segments = windowSegments(ctx);
|
|
151
|
+
if (source) return segmentOf(source, segments) ?? segments[0];
|
|
152
|
+
return segments.reduce((best, s) => area(s) > area(best) ? s : best, segments[0]);
|
|
159
153
|
}
|
|
160
154
|
function stepSpring(s, dt, response, damping, instant = false) {
|
|
161
155
|
if (instant) {
|
|
@@ -209,7 +203,7 @@ function growthFrame(source, target, anchor, phase) {
|
|
|
209
203
|
const len = Math.hypot(dx, dy) || 1;
|
|
210
204
|
const sourceSize = vireglass.halfMinDp(source.geometry) * 2;
|
|
211
205
|
const lift = DROP_LIFT * sourceSize;
|
|
212
|
-
const
|
|
206
|
+
const drop2 = {
|
|
213
207
|
x: source.center.x + dx / len * lift,
|
|
214
208
|
y: source.center.y + dy / len * lift
|
|
215
209
|
};
|
|
@@ -228,13 +222,13 @@ function growthFrame(source, target, anchor, phase) {
|
|
|
228
222
|
);
|
|
229
223
|
const shrunk = {
|
|
230
224
|
geometry: shrunkGeometry,
|
|
231
|
-
center: { x: mix(source.center.x,
|
|
225
|
+
center: { x: mix(source.center.x, drop2.x, a), y: mix(source.center.y, drop2.y, a) }
|
|
232
226
|
};
|
|
233
227
|
const seed = d * Math.sqrt(a);
|
|
234
228
|
const cornerX = Math.sign(anchor.x - source.center.x);
|
|
235
229
|
const cornerY = Math.sign(anchor.y - source.center.y);
|
|
236
|
-
const edgeX = mix(
|
|
237
|
-
const edgeY = mix(
|
|
230
|
+
const edgeX = mix(drop2.x + cornerX * (seed / 2), anchor.x, Math.min(b, 1));
|
|
231
|
+
const edgeY = mix(drop2.y + cornerY * (seed / 2), anchor.y, Math.min(b, 1));
|
|
238
232
|
const width = mix(seed, target.geometry.width, b);
|
|
239
233
|
const height = mix(seed, target.geometry.height, b);
|
|
240
234
|
const grownGeometry = { width, height, cornerRadius: 0 };
|
|
@@ -311,6 +305,424 @@ function stepGrowth(state, dt, open, reduceMotion = false, timing = DEFAULT_GROW
|
|
|
311
305
|
return movedShrink || movedGrow;
|
|
312
306
|
}
|
|
313
307
|
|
|
308
|
+
// src/kit/adaptive/form-morph.ts
|
|
309
|
+
var HIDDEN = /* @__PURE__ */ new Set(["hidden", "closed", "none"]);
|
|
310
|
+
function formMorph(change, ctx, options = {}) {
|
|
311
|
+
const instant = ctx.reduceMotion;
|
|
312
|
+
if (options.anchorGone) return { ...change, mode: "retract", staysOpen: false, instant };
|
|
313
|
+
if (change.from === change.to) return { ...change, mode: "none", staysOpen: true, instant };
|
|
314
|
+
const mode = HIDDEN.has(change.from) ? "appear" : HIDDEN.has(change.to) ? "disappear" : "reshape";
|
|
315
|
+
return { ...change, mode, staysOpen: mode !== "disappear", instant };
|
|
316
|
+
}
|
|
317
|
+
var FORM_MORPH_TIMING = { response: DEFAULT_GROWTH_TIMING.growOpen, damping: 1 };
|
|
318
|
+
var createFormMorph = () => ({ t: { x: 0, v: 0, target: 0 }, fromInk: 1 });
|
|
319
|
+
function stepFormMorph(state, dt, toward, instant = false) {
|
|
320
|
+
state.t.target = toward;
|
|
321
|
+
return stepSpring(state.t, dt, FORM_MORPH_TIMING.response, FORM_MORPH_TIMING.damping, instant);
|
|
322
|
+
}
|
|
323
|
+
var OUT_END = 0.45;
|
|
324
|
+
var IN_START = 0.55;
|
|
325
|
+
function formMorphFrame(from, to, state) {
|
|
326
|
+
const t = Math.min(Math.max(state.t.x, 0), 1);
|
|
327
|
+
const mix3 = (a, b) => a + (b - a) * t;
|
|
328
|
+
return {
|
|
329
|
+
box: {
|
|
330
|
+
x: mix3(from.x, to.x),
|
|
331
|
+
y: mix3(from.y, to.y),
|
|
332
|
+
width: mix3(from.width, to.width),
|
|
333
|
+
height: mix3(from.height, to.height),
|
|
334
|
+
radius: mix3(from.radius, to.radius)
|
|
335
|
+
},
|
|
336
|
+
ink: inkAt(state, t),
|
|
337
|
+
showing: t < 0.5 ? "from" : "to"
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function inkAt(state, t) {
|
|
341
|
+
return { outgoing: (state.fromInk ?? 1) * (1 - smoothstep(0, OUT_END, t)), incoming: smoothstep(IN_START, 1, t) };
|
|
342
|
+
}
|
|
343
|
+
function rebaseFormMorph(state) {
|
|
344
|
+
const t = Math.min(Math.max(state.t.x, 0), 1);
|
|
345
|
+
const ink = inkAt(state, t);
|
|
346
|
+
state.fromInk = t < 0.5 ? ink.outgoing : ink.incoming;
|
|
347
|
+
state.t = { x: 0, v: 0, target: 0 };
|
|
348
|
+
}
|
|
349
|
+
var CONTROL_SIZES = ["touch", "small", "medium", "large", "xlarge"];
|
|
350
|
+
var MIN_TOUCH_TARGET_DP = 44;
|
|
351
|
+
var MIN_POINTER_TARGET_DP = 24;
|
|
352
|
+
var capsule = (height, paddingX) => ({
|
|
353
|
+
height,
|
|
354
|
+
paddingX,
|
|
355
|
+
icon: Math.round(height / 2),
|
|
356
|
+
radius: height / 2,
|
|
357
|
+
shape: "capsule"
|
|
358
|
+
});
|
|
359
|
+
var rounded = (height, paddingX) => ({
|
|
360
|
+
height,
|
|
361
|
+
paddingX,
|
|
362
|
+
icon: Math.round(height / 2),
|
|
363
|
+
radius: height / 4,
|
|
364
|
+
shape: "rounded"
|
|
365
|
+
});
|
|
366
|
+
var BUTTON_SIZES = {
|
|
367
|
+
touch: capsule(44, 20),
|
|
368
|
+
small: rounded(24, 10),
|
|
369
|
+
medium: rounded(32, 14),
|
|
370
|
+
large: capsule(40, 18),
|
|
371
|
+
xlarge: capsule(48, 22)
|
|
372
|
+
};
|
|
373
|
+
function iconButtonSize(size) {
|
|
374
|
+
const spec = BUTTON_SIZES[size];
|
|
375
|
+
return { ...spec, width: spec.height, paddingX: 0 };
|
|
376
|
+
}
|
|
377
|
+
var MENU_PADDING = 0.3;
|
|
378
|
+
function menuSize(size) {
|
|
379
|
+
const spec = BUTTON_SIZES[size];
|
|
380
|
+
const padding = Math.round(spec.height * MENU_PADDING);
|
|
381
|
+
return {
|
|
382
|
+
row: spec.height,
|
|
383
|
+
padding,
|
|
384
|
+
itemRadius: spec.radius,
|
|
385
|
+
radius: spec.radius + padding,
|
|
386
|
+
icon: spec.icon,
|
|
387
|
+
labelPad: spec.paddingX
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function nestedRadius(outerRadius, inset2, minimum = 0) {
|
|
391
|
+
return vireglass.concentricRadius(outerRadius, inset2, minimum);
|
|
392
|
+
}
|
|
393
|
+
function defaultSize(input) {
|
|
394
|
+
return input.touch ? "touch" : "medium";
|
|
395
|
+
}
|
|
396
|
+
function hitSlop(bodyDp, minTargetDp) {
|
|
397
|
+
return bodyDp >= minTargetDp ? 0 : (minTargetDp - bodyDp) / 2;
|
|
398
|
+
}
|
|
399
|
+
function minTarget(input) {
|
|
400
|
+
return input.touch ? MIN_TOUCH_TARGET_DP : MIN_POINTER_TARGET_DP;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/kit/adaptive/input.ts
|
|
404
|
+
function sizeTier(input) {
|
|
405
|
+
return defaultSize({ touch: input.primary === "coarse" });
|
|
406
|
+
}
|
|
407
|
+
function hitTarget(input) {
|
|
408
|
+
return minTarget({ touch: input.anyCoarse });
|
|
409
|
+
}
|
|
410
|
+
function hoversWith(pointerType) {
|
|
411
|
+
return pointerType !== "touch";
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/kit/adaptive/navigation.ts
|
|
415
|
+
var BAR_SLOTS = 5;
|
|
416
|
+
function formFor(ctx, spec) {
|
|
417
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
418
|
+
if (ctx.posture.kind === "tabletop") return "bar";
|
|
419
|
+
if (widthAtLeast(width, "large")) return spec.sidebarCollapsed ? "rail" : "sidebar";
|
|
420
|
+
if (height === "compact") return "rail";
|
|
421
|
+
if (width === "compact") return "bar";
|
|
422
|
+
if (width === "medium" && (spec.panes ?? 1) > 1 && ctx.posture.kind !== "book") return "bar";
|
|
423
|
+
return "rail";
|
|
424
|
+
}
|
|
425
|
+
function navigationForm(ctx, spec) {
|
|
426
|
+
const form = formFor(ctx, spec);
|
|
427
|
+
const width = classifyWindow(ctx.window).width;
|
|
428
|
+
const large = widthAtLeast(width, "large");
|
|
429
|
+
const slots = BAR_SLOTS - (spec.search ? 1 : 0);
|
|
430
|
+
const primary = Math.max(0, Math.min(spec.primary, slots));
|
|
431
|
+
const secondary = Math.max(0, spec.secondary ?? 0) + Math.max(0, spec.primary - primary);
|
|
432
|
+
let search = null;
|
|
433
|
+
if (spec.search) {
|
|
434
|
+
const shortcut = large || ctx.input.keyboard;
|
|
435
|
+
if (large) search = { form: "field", place: form === "sidebar" ? "sidebar" : "toolbar", shortcut };
|
|
436
|
+
else search = form === "bar" ? { form: "button", place: "bar", shortcut } : { form: "item", place: "rail", shortcut };
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
form,
|
|
440
|
+
edge: form === "bar" ? "bottom" : "leading",
|
|
441
|
+
primary,
|
|
442
|
+
secondary,
|
|
443
|
+
secondaryVisible: form === "sidebar",
|
|
444
|
+
search,
|
|
445
|
+
expandable: form !== "rail" ? null : large ? "beside" : width === "expanded" ? "over" : null,
|
|
446
|
+
collapsible: form === "sidebar"
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function bottomAccessory(nav, state = {}) {
|
|
450
|
+
if (nav.form !== "bar") return { placement: "content-bottom", compact: false };
|
|
451
|
+
return state.barMinimized ? { placement: "in-bar", compact: true } : { placement: "above-bar", compact: false };
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/kit/adaptive/panes.ts
|
|
455
|
+
var LIST_PANE_WIDTH = { expanded: 360, large: 412 };
|
|
456
|
+
var SUPPORTING_PANE_MAX_WIDTH = 400;
|
|
457
|
+
function splitFor(ctx, sparse) {
|
|
458
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
459
|
+
if (ctx.posture.kind === "book") return { kind: "fold", gap: ctx.posture.fold.bounds };
|
|
460
|
+
if (height === "compact") return null;
|
|
461
|
+
if (widthAtLeast(width, "large")) return { kind: "fixed", list: LIST_PANE_WIDTH.large };
|
|
462
|
+
if (width === "expanded") return { kind: "fixed", list: LIST_PANE_WIDTH.expanded };
|
|
463
|
+
return width === "medium" && sparse ? { kind: "half" } : null;
|
|
464
|
+
}
|
|
465
|
+
function paneLayout(ctx, state) {
|
|
466
|
+
const split = splitFor(ctx, state.sparse ?? false);
|
|
467
|
+
if (split) {
|
|
468
|
+
return { panes: 2, visible: ["list", "detail"], split, placeholder: !state.selected, back: false, selectionShown: state.selected };
|
|
469
|
+
}
|
|
470
|
+
const detail = state.selected && state.showing === "detail";
|
|
471
|
+
return { panes: 1, visible: [detail ? "detail" : "list"], split: null, placeholder: false, back: detail, selectionShown: false };
|
|
472
|
+
}
|
|
473
|
+
function supportingPane(ctx, state) {
|
|
474
|
+
const width = classifyWindow(ctx.window).width;
|
|
475
|
+
const base = { edge: "trailing", maxWidth: SUPPORTING_PANE_MAX_WIDTH, remembered: state.pinned };
|
|
476
|
+
if (widthAtLeast(width, "large")) return { ...base, form: state.pinned || state.open ? "beside" : "hidden", scrim: false };
|
|
477
|
+
if (width === "expanded" && state.open) return { ...base, form: "over", scrim: true };
|
|
478
|
+
return { ...base, form: "hidden", scrim: false };
|
|
479
|
+
}
|
|
480
|
+
function postureSplit(ctx) {
|
|
481
|
+
if (ctx.posture.kind === "flat") return { kind: "single" };
|
|
482
|
+
const segments = windowSegments(ctx);
|
|
483
|
+
if (segments.length < 2) return { kind: "single" };
|
|
484
|
+
return { kind: "split", axis: ctx.posture.kind === "tabletop" ? "horizontal" : "vertical", primary: segments[0], secondary: segments[1] };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/kit/adaptive/presentation.ts
|
|
488
|
+
var SURFACE_KINDS = ["menu", "picker", "popover", "sheet", "modal-sheet", "alert", "dialog"];
|
|
489
|
+
var BOTTOM_SHEET_MAX_WIDTH = 640;
|
|
490
|
+
var SIDE_SHEET_MAX_WIDTH = 400;
|
|
491
|
+
var DIALOG_MAX_WIDTH = 560;
|
|
492
|
+
function baseForm(kind, ctx) {
|
|
493
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
494
|
+
const compactWidth = width === "compact";
|
|
495
|
+
const plain = { fromSource: false, scrim: false, compresses: false, maxWidth: null };
|
|
496
|
+
const sheet = (form) => height === "compact" ? { ...plain, form: "full-screen", scrim: true } : form;
|
|
497
|
+
switch (kind) {
|
|
498
|
+
case "menu":
|
|
499
|
+
return { ...plain, form: "grown", fromSource: true };
|
|
500
|
+
case "popover":
|
|
501
|
+
return compactWidth ? sheet({ ...plain, form: "bottom-sheet", fromSource: true, scrim: true }) : { ...plain, form: "popover", fromSource: true };
|
|
502
|
+
case "picker":
|
|
503
|
+
case "modal-sheet":
|
|
504
|
+
return sheet(
|
|
505
|
+
compactWidth ? { ...plain, form: "bottom-sheet", scrim: true } : { ...plain, form: "centered-sheet", scrim: true, maxWidth: DIALOG_MAX_WIDTH }
|
|
506
|
+
);
|
|
507
|
+
case "sheet":
|
|
508
|
+
if (width === "xlarge") return { ...plain, form: "side-pane", compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };
|
|
509
|
+
if (widthAtLeast(width, "expanded")) return { ...plain, form: "side-sheet", compresses: true, maxWidth: SIDE_SHEET_MAX_WIDTH };
|
|
510
|
+
return sheet({ ...plain, form: "bottom-sheet", maxWidth: compactWidth ? null : BOTTOM_SHEET_MAX_WIDTH });
|
|
511
|
+
case "alert":
|
|
512
|
+
return { ...plain, form: "alert", scrim: true, maxWidth: DIALOG_MAX_WIDTH };
|
|
513
|
+
case "dialog":
|
|
514
|
+
return compactWidth ? { ...plain, form: "full-screen", scrim: true } : { ...plain, form: "basic-dialog", scrim: true, maxWidth: DIALOG_MAX_WIDTH };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function presentationForm(kind, ctx, source) {
|
|
518
|
+
const base = baseForm(kind, ctx);
|
|
519
|
+
const side = base.form === "side-sheet" || base.form === "side-pane";
|
|
520
|
+
const region = side && ctx.posture.kind === "book" ? windowSegments(ctx).at(-1) : surfaceRegion(ctx, source);
|
|
521
|
+
return { ...base, region };
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/kit/adaptive/toolbar.ts
|
|
525
|
+
var TOOLBAR_GAP = 8;
|
|
526
|
+
var MORE_ID = "\u2026";
|
|
527
|
+
function toolbarPlacement(ctx) {
|
|
528
|
+
const { width, height } = classifyWindow(ctx.window);
|
|
529
|
+
if (height === "compact") {
|
|
530
|
+
return { edge: "trailing", orientation: "vertical", maxGroups: Number.POSITIVE_INFINITY };
|
|
531
|
+
}
|
|
532
|
+
if (width === "compact") return { edge: "bottom", orientation: "horizontal", maxGroups: 3 };
|
|
533
|
+
return { edge: "top", orientation: "horizontal", maxGroups: Number.POSITIVE_INFINITY };
|
|
534
|
+
}
|
|
535
|
+
function toolbarLayout(items, options) {
|
|
536
|
+
const orientation = options.orientation ?? "horizontal";
|
|
537
|
+
const gap = options.gap ?? TOOLBAR_GAP;
|
|
538
|
+
const maxGroups = options.maxGroups ?? Number.POSITIVE_INFINITY;
|
|
539
|
+
const side = BUTTON_SIZES[options.tier].height;
|
|
540
|
+
const vertical = orientation === "vertical";
|
|
541
|
+
const aside = vertical ? items.filter((i) => i.kind === "text").map((i) => i.id) : [];
|
|
542
|
+
const pool = items.map((item, index) => ({ item, index })).filter((e) => !vertical || e.item.kind === "icon");
|
|
543
|
+
const extentOf = (item) => item.kind === "icon" ? side : item.width ?? 3 * side;
|
|
544
|
+
const lay = (visible2, merge, more) => {
|
|
545
|
+
const ordered = [...visible2.filter((e) => !e.item.primary)];
|
|
546
|
+
const capsules = [];
|
|
547
|
+
let open = null;
|
|
548
|
+
const close = () => {
|
|
549
|
+
if (open) capsules.push({ kind: open.kind, items: open.items, extent: open.extent });
|
|
550
|
+
open = null;
|
|
551
|
+
};
|
|
552
|
+
for (const { item } of ordered) {
|
|
553
|
+
const kind = item.kind;
|
|
554
|
+
const group = item.group ?? `kind:${kind}`;
|
|
555
|
+
if (merge && open && open.kind === kind && open.group === group) {
|
|
556
|
+
open.items.push(item.id);
|
|
557
|
+
open.extent += extentOf(item);
|
|
558
|
+
} else {
|
|
559
|
+
close();
|
|
560
|
+
open = { kind, group, items: [item.id], extent: extentOf(item) };
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
close();
|
|
564
|
+
if (more) capsules.push({ kind: "more", items: [MORE_ID], extent: side });
|
|
565
|
+
for (const { item } of visible2) if (item.primary) capsules.push({ kind: "primary", items: [item.id], extent: extentOf(item) });
|
|
566
|
+
const extent = capsules.reduce((sum, c) => sum + c.extent, 0) + gap * Math.max(capsules.length - 1, 0);
|
|
567
|
+
return { capsules, extent, fits: extent <= options.available && capsules.length <= maxGroups };
|
|
568
|
+
};
|
|
569
|
+
const done = (stage, laid2, overflow) => ({
|
|
570
|
+
orientation,
|
|
571
|
+
stage,
|
|
572
|
+
capsules: laid2.capsules,
|
|
573
|
+
overflow,
|
|
574
|
+
aside,
|
|
575
|
+
extent: laid2.extent,
|
|
576
|
+
fits: laid2.fits
|
|
577
|
+
});
|
|
578
|
+
const separate = lay(pool, false, false);
|
|
579
|
+
if (separate.fits) return done("separate", separate, []);
|
|
580
|
+
const merged = lay(pool, true, false);
|
|
581
|
+
if (merged.fits) return done("merged", merged, []);
|
|
582
|
+
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);
|
|
583
|
+
let visible = pool;
|
|
584
|
+
const hidden = [];
|
|
585
|
+
let laid = merged;
|
|
586
|
+
for (const candidate of candidates) {
|
|
587
|
+
visible = visible.filter((e) => e !== candidate);
|
|
588
|
+
hidden.push(candidate);
|
|
589
|
+
laid = lay(visible, true, true);
|
|
590
|
+
if (laid.fits) break;
|
|
591
|
+
}
|
|
592
|
+
if (!hidden.length) return done("merged", merged, []);
|
|
593
|
+
return done("overflow", laid, hidden.sort((a, b) => a.index - b.index).map((e) => e.item.id));
|
|
594
|
+
}
|
|
595
|
+
var ATOM_AT_REST = {
|
|
596
|
+
pressed: 0,
|
|
597
|
+
hovered: 0,
|
|
598
|
+
selected: 0,
|
|
599
|
+
disabled: false
|
|
600
|
+
};
|
|
601
|
+
function atomLift(kind) {
|
|
602
|
+
switch (kind) {
|
|
603
|
+
case "iconButton":
|
|
604
|
+
case "switch":
|
|
605
|
+
case "slider":
|
|
606
|
+
return 1;
|
|
607
|
+
default:
|
|
608
|
+
return 0;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
var DISABLED_PRESENCE = 0.25;
|
|
612
|
+
var HOVER_GAIN = 0.35;
|
|
613
|
+
function activation(state) {
|
|
614
|
+
if (state.disabled) return 0;
|
|
615
|
+
const clamp2 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
616
|
+
return Math.max(clamp2(state.selected), clamp2(state.pressed), clamp2(state.hovered) * HOVER_GAIN);
|
|
617
|
+
}
|
|
618
|
+
function atomMaterial(base, kind, state = ATOM_AT_REST) {
|
|
619
|
+
const out = vireglass.activeMaterial(base, activation(state));
|
|
620
|
+
if (!state.disabled) return out;
|
|
621
|
+
return { ...out, presence: out.presence * DISABLED_PRESENCE };
|
|
622
|
+
}
|
|
623
|
+
function atomAtRest(state) {
|
|
624
|
+
return state.pressed === 0 && state.hovered === 0 && state.selected === 0 && state.disabled === false;
|
|
625
|
+
}
|
|
626
|
+
var REST_BODY = { alpha: 0.25, white: 0.15, lift: 0.1 };
|
|
627
|
+
var PRESSED_BODY = { alpha: 0.34, white: 0.35, lift: 0 };
|
|
628
|
+
var FROST_LIFT = 0.26;
|
|
629
|
+
function atomBody(base, state = ATOM_AT_REST, frost = false) {
|
|
630
|
+
const on = activation(state);
|
|
631
|
+
const mix3 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
|
|
632
|
+
let alpha = mix3("alpha");
|
|
633
|
+
let white = mix3("white");
|
|
634
|
+
let lift = mix3("lift");
|
|
635
|
+
if (state.disabled) alpha *= DISABLED_PRESENCE;
|
|
636
|
+
if (frost) {
|
|
637
|
+
const density = vireglass.applyAccessibility(vireglass.resolveOptics(base), {
|
|
638
|
+
reduceTransparency: true,
|
|
639
|
+
increaseContrast: false,
|
|
640
|
+
reduceMotion: false
|
|
641
|
+
}).bodyDensity;
|
|
642
|
+
alpha = Math.max(alpha, density);
|
|
643
|
+
white = 0;
|
|
644
|
+
lift = FROST_LIFT;
|
|
645
|
+
}
|
|
646
|
+
return { alpha, white, lift };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// src/kit/behavior/focus.ts
|
|
650
|
+
function stepFocus(event) {
|
|
651
|
+
return event.type === "focus" && event.visible;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/kit/behavior/hover.ts
|
|
655
|
+
function stepHover(event, canHover) {
|
|
656
|
+
return canHover && event.type === "enter";
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// src/kit/behavior/press.ts
|
|
660
|
+
var PRESS_IDLE = { by: null };
|
|
661
|
+
function stepPress(state, event, enabled2) {
|
|
662
|
+
switch (event.type) {
|
|
663
|
+
case "down":
|
|
664
|
+
if (!enabled2 || state.by !== null) return { state, activated: false };
|
|
665
|
+
return { state: { by: event.source }, activated: false };
|
|
666
|
+
case "up":
|
|
667
|
+
if (state.by !== event.source) return { state, activated: false };
|
|
668
|
+
return { state: PRESS_IDLE, activated: enabled2 };
|
|
669
|
+
case "cancel":
|
|
670
|
+
return { state: PRESS_IDLE, activated: false };
|
|
671
|
+
case "leave":
|
|
672
|
+
return state.by === "pointer" ? { state: PRESS_IDLE, activated: false } : { state, activated: false };
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/kit/behavior/control.ts
|
|
677
|
+
var TOUCH_INPUT = { hover: false };
|
|
678
|
+
var POINTER_INPUT = { hover: true };
|
|
679
|
+
var CONTROL_IDLE = {
|
|
680
|
+
press: PRESS_IDLE,
|
|
681
|
+
hovered: false,
|
|
682
|
+
focused: false,
|
|
683
|
+
disabled: false
|
|
684
|
+
};
|
|
685
|
+
function stepControl(state, event, caps) {
|
|
686
|
+
switch (event.type) {
|
|
687
|
+
case "down":
|
|
688
|
+
case "up":
|
|
689
|
+
case "cancel": {
|
|
690
|
+
const next = stepPress(state.press, event, !state.disabled);
|
|
691
|
+
return { state: { ...state, press: next.state }, activated: next.activated };
|
|
692
|
+
}
|
|
693
|
+
case "leave": {
|
|
694
|
+
const next = stepPress(state.press, event, !state.disabled);
|
|
695
|
+
return { state: { ...state, press: next.state, hovered: false }, activated: false };
|
|
696
|
+
}
|
|
697
|
+
case "enter":
|
|
698
|
+
return {
|
|
699
|
+
state: { ...state, hovered: !state.disabled && stepHover(event, caps.hover) },
|
|
700
|
+
activated: false
|
|
701
|
+
};
|
|
702
|
+
case "focus":
|
|
703
|
+
case "blur": {
|
|
704
|
+
const press = event.type === "blur" && state.press.by === "keyboard" ? PRESS_IDLE : state.press;
|
|
705
|
+
return {
|
|
706
|
+
state: { ...state, press, focused: !state.disabled && stepFocus(event) },
|
|
707
|
+
activated: false
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
case "disable":
|
|
711
|
+
return {
|
|
712
|
+
state: event.disabled ? { press: PRESS_IDLE, hovered: false, focused: false, disabled: true } : { ...state, disabled: false },
|
|
713
|
+
activated: false
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function controlTargets(state) {
|
|
718
|
+
return {
|
|
719
|
+
pressed: state.press.by !== null ? 1 : 0,
|
|
720
|
+
hovered: state.hovered ? 1 : 0,
|
|
721
|
+
focus: state.focused ? 1 : 0,
|
|
722
|
+
disabled: state.disabled
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
314
726
|
// src/kit/behavior/visual.ts
|
|
315
727
|
var createControlVisual = () => ({
|
|
316
728
|
pressed: { x: 0, v: 0, target: 0 },
|
|
@@ -403,40 +815,46 @@ var MENU_CORNERS = {
|
|
|
403
815
|
"bottom-left": { x: -1, y: 1 },
|
|
404
816
|
"bottom-right": { x: 1, y: 1 }
|
|
405
817
|
};
|
|
406
|
-
function menuCorner(trigger,
|
|
818
|
+
function menuCorner(trigger, region) {
|
|
819
|
+
const x0 = region.x ?? 0;
|
|
820
|
+
const y0 = region.y ?? 0;
|
|
407
821
|
return {
|
|
408
|
-
x: trigger.x + trigger.width / 2 <=
|
|
409
|
-
y: trigger.y + trigger.height / 2 <=
|
|
822
|
+
x: trigger.x + trigger.width / 2 <= x0 + region.width / 2 ? -1 : 1,
|
|
823
|
+
y: trigger.y + trigger.height / 2 <= y0 + region.height / 2 ? -1 : 1
|
|
410
824
|
};
|
|
411
825
|
}
|
|
412
826
|
function along(start, length, size, side) {
|
|
413
827
|
return side < 0 ? start : start + length - size;
|
|
414
828
|
}
|
|
415
|
-
function fits(at, size,
|
|
416
|
-
return at >= margin && at + size <=
|
|
829
|
+
function fits(at, size, lo, hi, margin) {
|
|
830
|
+
return at >= lo + margin && at + size <= hi - margin;
|
|
417
831
|
}
|
|
418
832
|
var clamp = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
|
|
419
|
-
function menuPlacement(trigger, menu,
|
|
833
|
+
function menuPlacement(trigger, menu, region, options = {}) {
|
|
420
834
|
const margin = options.margin ?? 8;
|
|
421
|
-
const
|
|
422
|
-
const
|
|
423
|
-
const
|
|
835
|
+
const x0 = region.x ?? 0;
|
|
836
|
+
const y0 = region.y ?? 0;
|
|
837
|
+
const x1 = x0 + region.width;
|
|
838
|
+
const y1 = y0 + region.height;
|
|
839
|
+
const width = Math.max(0, Math.min(menu.width, region.width - 2 * margin));
|
|
840
|
+
const height = Math.max(0, Math.min(menu.height, region.height - 2 * margin));
|
|
841
|
+
const corner = { ...options.corner ?? menuCorner(trigger, region) };
|
|
424
842
|
let x = along(trigger.x, trigger.width, width, corner.x);
|
|
425
843
|
let y = along(trigger.y, trigger.height, height, corner.y);
|
|
426
844
|
if (!options.corner) {
|
|
427
845
|
const fx = along(trigger.x, trigger.width, width, corner.x === -1 ? 1 : -1);
|
|
428
|
-
if (!fits(x, width,
|
|
846
|
+
if (!fits(x, width, x0, x1, margin) && fits(fx, width, x0, x1, margin)) {
|
|
429
847
|
corner.x = corner.x === -1 ? 1 : -1;
|
|
430
848
|
x = fx;
|
|
431
849
|
}
|
|
432
850
|
const fy = along(trigger.y, trigger.height, height, corner.y === -1 ? 1 : -1);
|
|
433
|
-
if (!fits(y, height,
|
|
851
|
+
if (!fits(y, height, y0, y1, margin) && fits(fy, height, y0, y1, margin)) {
|
|
434
852
|
corner.y = corner.y === -1 ? 1 : -1;
|
|
435
853
|
y = fy;
|
|
436
854
|
}
|
|
437
855
|
}
|
|
438
|
-
x = clamp(x, margin,
|
|
439
|
-
y = clamp(y, margin,
|
|
856
|
+
x = clamp(x, x0 + margin, x1 - margin - width);
|
|
857
|
+
y = clamp(y, y0 + margin, y1 - margin - height);
|
|
440
858
|
const rect = { x, y, width, height };
|
|
441
859
|
return {
|
|
442
860
|
rect,
|
|
@@ -450,7 +868,7 @@ function menuActivity(phase) {
|
|
|
450
868
|
const grow = Math.min(Math.max(phase.grow, 0), 1);
|
|
451
869
|
return shrink * (1 - grow);
|
|
452
870
|
}
|
|
453
|
-
function menuSurfaceFrame(trigger, menu, anchor, phase) {
|
|
871
|
+
function menuSurfaceFrame(trigger, menu, anchor, phase, bounds) {
|
|
454
872
|
const frame = growthFrame(
|
|
455
873
|
{ geometry: { width: trigger.width, height: trigger.height, cornerRadius: trigger.radius }, center: center(trigger) },
|
|
456
874
|
{ geometry: { width: menu.width, height: menu.height, cornerRadius: menu.radius }, center: center(menu) },
|
|
@@ -458,17 +876,25 @@ function menuSurfaceFrame(trigger, menu, anchor, phase) {
|
|
|
458
876
|
phase
|
|
459
877
|
);
|
|
460
878
|
const { geometry, center: c } = frame.body;
|
|
461
|
-
|
|
879
|
+
let box2 = {
|
|
462
880
|
x: c.x - geometry.width / 2,
|
|
463
881
|
y: c.y - geometry.height / 2,
|
|
464
882
|
width: geometry.width,
|
|
465
883
|
height: geometry.height,
|
|
466
884
|
radius: geometry.cornerRadius
|
|
467
885
|
};
|
|
886
|
+
let morph = frame.merge;
|
|
887
|
+
const held = bounds ? intersectRect(box2, bounds) : null;
|
|
888
|
+
if (held && (held.width < box2.width || held.height < box2.height)) {
|
|
889
|
+
const was = center(box2);
|
|
890
|
+
const now = center(held);
|
|
891
|
+
box2 = { ...held, radius: Math.min(box2.radius, held.width / 2, held.height / 2) };
|
|
892
|
+
if (morph) morph = { ...morph, offsetX: morph.offsetX + was.x - now.x, offsetY: morph.offsetY + was.y - now.y };
|
|
893
|
+
}
|
|
468
894
|
return {
|
|
469
|
-
box,
|
|
470
|
-
morph
|
|
471
|
-
content: { x: menu.x -
|
|
895
|
+
box: box2,
|
|
896
|
+
morph,
|
|
897
|
+
content: { x: menu.x - box2.x, y: menu.y - box2.y },
|
|
472
898
|
ink: frame.ink,
|
|
473
899
|
activity: menuActivity(phase)
|
|
474
900
|
};
|
|
@@ -484,8 +910,8 @@ function menuBody(base, activity, frost = false) {
|
|
|
484
910
|
const rest = atomBody(base, ATOM_AT_REST, frost);
|
|
485
911
|
if (frost) return rest;
|
|
486
912
|
const k = Math.min(Math.max(activity, 0), 1);
|
|
487
|
-
const
|
|
488
|
-
return { alpha:
|
|
913
|
+
const mix3 = (key) => rest[key] + (MENU_DROP_BODY[key] - rest[key]) * k;
|
|
914
|
+
return { alpha: mix3("alpha"), white: mix3("white"), lift: mix3("lift") };
|
|
489
915
|
}
|
|
490
916
|
var MENU_ITEM_FILL = { active: 0.1, pressed: 0.18 };
|
|
491
917
|
function menuItemFill(state) {
|
|
@@ -494,95 +920,612 @@ function menuItemFill(state) {
|
|
|
494
920
|
return state.active ? MENU_ITEM_FILL.active : 0;
|
|
495
921
|
}
|
|
496
922
|
|
|
497
|
-
// src/kit/
|
|
498
|
-
var
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
923
|
+
// src/kit/nav/layout.ts
|
|
924
|
+
var NAV_SIZES = {
|
|
925
|
+
bar: { height: 56, item: 74, minItem: 44, inset: 3, margin: 16, searchGap: 8, minimized: 44 },
|
|
926
|
+
rail: { width: 80, item: 56, gap: 4, padding: 8, margin: 8, head: 48 },
|
|
927
|
+
sidebar: { width: 280, row: { touch: 44, pointer: 36 }, padding: 8, margin: 8, head: 48, separator: 12 },
|
|
928
|
+
accessory: { height: 48, gap: 8, inline: 44, content: 56, margin: 8 },
|
|
929
|
+
lens: { inflate: 6 },
|
|
930
|
+
toggle: 44
|
|
931
|
+
};
|
|
932
|
+
var box = (x, y, width, height, radius) => ({ x, y, width, height, radius });
|
|
933
|
+
function barLayout(input, items, minimized) {
|
|
934
|
+
const { bar, accessory } = NAV_SIZES;
|
|
935
|
+
const { width, height } = input.window;
|
|
936
|
+
const bottom = Math.max(input.bottomInset ?? 0, bar.margin);
|
|
937
|
+
const hasSearch = input.search?.form === "button";
|
|
938
|
+
const placement = input.accessory ? bottomAccessory({ form: "bar" }, { barMinimized: minimized }).placement : null;
|
|
939
|
+
const selected = input.selected >= 0 && input.selected < items ? input.selected : -1;
|
|
940
|
+
if (minimized) {
|
|
941
|
+
const centre = height - bottom - bar.height / 2;
|
|
942
|
+
const d = bar.minimized;
|
|
943
|
+
const body2 = box(bar.margin, centre - d / 2, d, d, d / 2);
|
|
944
|
+
const search2 = hasSearch ? box(width - bar.margin - d, body2.y, d, d, d / 2) : null;
|
|
945
|
+
let acc2 = null;
|
|
946
|
+
if (placement) {
|
|
947
|
+
const x2 = body2.x + d + accessory.gap;
|
|
948
|
+
const right = search2 ? search2.x - accessory.gap : width - bar.margin;
|
|
949
|
+
acc2 = box(x2, body2.y, Math.max(0, right - x2), accessory.inline, accessory.inline / 2);
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
form: "bar",
|
|
953
|
+
minimized: true,
|
|
954
|
+
body: body2,
|
|
955
|
+
items: Array.from({ length: items }, () => ({ ...body2 })),
|
|
956
|
+
pill: selected >= 0 ? { ...body2 } : null,
|
|
957
|
+
search: search2,
|
|
958
|
+
accessory: acc2,
|
|
959
|
+
toggle: null,
|
|
960
|
+
accessoryPlacement: placement,
|
|
961
|
+
insets: { bottom: height - body2.y, start: 0 }
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
const searchBlock = hasSearch ? bar.height + bar.searchGap : 0;
|
|
965
|
+
const fit = items > 0 ? (width - 2 * bar.margin - searchBlock - 2 * bar.inset) / items : bar.item;
|
|
966
|
+
const itemWidth = Math.max(bar.minItem, Math.min(bar.item, fit));
|
|
967
|
+
const bodyWidth = items * itemWidth + 2 * bar.inset;
|
|
968
|
+
const x = Math.max(bar.margin, (width - bodyWidth - searchBlock) / 2);
|
|
969
|
+
const body = box(x, height - bottom - bar.height, bodyWidth, bar.height, bar.height / 2);
|
|
970
|
+
const cellHeight = bar.height - 2 * bar.inset;
|
|
971
|
+
const cells = Array.from(
|
|
972
|
+
{ length: items },
|
|
973
|
+
(_, i) => box(x + bar.inset + i * itemWidth, body.y + bar.inset, itemWidth, cellHeight, cellHeight / 2)
|
|
974
|
+
);
|
|
975
|
+
const search = hasSearch ? box(x + bodyWidth + bar.searchGap, body.y, bar.height, bar.height, bar.height / 2) : null;
|
|
976
|
+
let acc = null;
|
|
977
|
+
if (placement) {
|
|
978
|
+
acc = box(body.x, body.y - accessory.gap - accessory.height, bodyWidth + searchBlock, accessory.height, accessory.height / 2);
|
|
979
|
+
}
|
|
980
|
+
return {
|
|
981
|
+
form: "bar",
|
|
982
|
+
minimized: false,
|
|
983
|
+
body,
|
|
984
|
+
items: cells,
|
|
985
|
+
pill: selected >= 0 ? { ...cells[selected] } : null,
|
|
986
|
+
search,
|
|
987
|
+
accessory: acc,
|
|
988
|
+
toggle: null,
|
|
989
|
+
accessoryPlacement: placement,
|
|
990
|
+
insets: { bottom: height - body.y + (acc ? accessory.height + accessory.gap : 0), start: 0 }
|
|
991
|
+
};
|
|
502
992
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
993
|
+
function contentAccessory(input, start) {
|
|
994
|
+
const { accessory } = NAV_SIZES;
|
|
995
|
+
const base = input.bottomInset ?? 0;
|
|
996
|
+
if (!input.accessory) return { box: null, bottom: base };
|
|
997
|
+
const bottom = Math.max(base, accessory.margin);
|
|
998
|
+
const width = Math.max(0, input.window.width - start - accessory.margin);
|
|
999
|
+
const acc = box(start, input.window.height - bottom - accessory.content, width, accessory.content, accessory.content / 2);
|
|
1000
|
+
return { box: acc, bottom: bottom + accessory.content };
|
|
1001
|
+
}
|
|
1002
|
+
function headToggle(body, padding, head, dx) {
|
|
1003
|
+
const d = NAV_SIZES.toggle;
|
|
1004
|
+
return box(body.x + dx, body.y + padding + (head - d) / 2, d, d, d / 2);
|
|
1005
|
+
}
|
|
1006
|
+
function inset(b, d) {
|
|
1007
|
+
return box(b.x + d, b.y + d, b.width - 2 * d, b.height - 2 * d, (Math.min(b.width, b.height) - 2 * d) / 2);
|
|
1008
|
+
}
|
|
1009
|
+
function railLayout(input, items) {
|
|
1010
|
+
const { rail, bar } = NAV_SIZES;
|
|
1011
|
+
const hasSearch = input.search?.form === "item";
|
|
1012
|
+
const n = items + (hasSearch ? 1 : 0);
|
|
1013
|
+
const head = input.toggle ? rail.head : 0;
|
|
1014
|
+
const natural = rail.padding * 2 + head + n * rail.item + Math.max(n - 1, 0) * rail.gap;
|
|
1015
|
+
const height = Math.min(natural, Math.max(input.window.height - 2 * rail.margin, 0));
|
|
1016
|
+
const body = box(rail.margin, rail.margin, rail.width, height, 28);
|
|
1017
|
+
const cell = (i) => box(body.x + rail.padding, body.y + rail.padding + head + i * (rail.item + rail.gap), rail.width - 2 * rail.padding, rail.item, 24);
|
|
1018
|
+
const cells = Array.from({ length: items }, (_, i) => cell(i));
|
|
1019
|
+
const selected = input.selected >= 0 && input.selected < items ? input.selected : -1;
|
|
1020
|
+
const start = body.x + body.width + rail.margin;
|
|
1021
|
+
const acc = contentAccessory(input, start);
|
|
1022
|
+
return {
|
|
1023
|
+
form: "rail",
|
|
1024
|
+
minimized: false,
|
|
1025
|
+
body,
|
|
1026
|
+
items: cells,
|
|
1027
|
+
pill: selected >= 0 ? inset(cells[selected], bar.inset) : null,
|
|
1028
|
+
search: hasSearch ? cell(items) : null,
|
|
1029
|
+
accessory: acc.box,
|
|
1030
|
+
toggle: input.toggle ? headToggle(body, rail.padding, rail.head, (rail.width - NAV_SIZES.toggle) / 2) : null,
|
|
1031
|
+
accessoryPlacement: acc.box ? "content-bottom" : null,
|
|
1032
|
+
insets: { bottom: acc.bottom, start }
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function sidebarLayout(input, items, secondary) {
|
|
1036
|
+
const { sidebar } = NAV_SIZES;
|
|
1037
|
+
const row = sidebar.row[input.tier];
|
|
1038
|
+
const body = box(sidebar.margin, sidebar.margin, sidebar.width, input.window.height - 2 * sidebar.margin, 24);
|
|
1039
|
+
const rowWidth = sidebar.width - 2 * sidebar.padding;
|
|
1040
|
+
const rowAt = (y2) => box(body.x + sidebar.padding, y2, rowWidth, row, row / 2);
|
|
1041
|
+
let y = body.y + sidebar.padding + (input.toggle ? sidebar.head : 0);
|
|
1042
|
+
let search = null;
|
|
1043
|
+
if (input.search?.form === "field") {
|
|
1044
|
+
search = rowAt(y);
|
|
1045
|
+
y += row;
|
|
1046
|
+
}
|
|
1047
|
+
const cells = [];
|
|
1048
|
+
for (let i = 0; i < items; i += 1, y += row) cells.push(rowAt(y));
|
|
1049
|
+
if (secondary > 0) y += sidebar.separator;
|
|
1050
|
+
for (let i = 0; i < secondary; i += 1, y += row) cells.push(rowAt(y));
|
|
1051
|
+
const selected = input.selected >= 0 && input.selected < cells.length ? input.selected : -1;
|
|
1052
|
+
const start = body.x + body.width + sidebar.margin;
|
|
1053
|
+
const acc = contentAccessory(input, start);
|
|
1054
|
+
return {
|
|
1055
|
+
form: "sidebar",
|
|
1056
|
+
minimized: false,
|
|
1057
|
+
body,
|
|
1058
|
+
items: cells,
|
|
1059
|
+
pill: selected >= 0 ? { ...cells[selected] } : null,
|
|
1060
|
+
search,
|
|
1061
|
+
accessory: acc.box,
|
|
1062
|
+
toggle: input.toggle ? headToggle(body, sidebar.padding, sidebar.head, sidebar.width - sidebar.padding - NAV_SIZES.toggle) : null,
|
|
1063
|
+
accessoryPlacement: acc.box ? "content-bottom" : null,
|
|
1064
|
+
insets: { bottom: acc.bottom, start }
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
function navLayout(input) {
|
|
1068
|
+
const items = Math.max(0, Math.floor(input.items));
|
|
1069
|
+
const secondary = Math.max(0, Math.floor(input.secondary));
|
|
1070
|
+
if (input.form === "bar") return barLayout(input, items, !!input.minimized);
|
|
1071
|
+
return input.form === "rail" ? railLayout(input, items) : sidebarLayout(input, items, secondary);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/kit/nav/drag.ts
|
|
1075
|
+
var MAX_STRETCH = 0.25;
|
|
1076
|
+
var SATURATION_SPEED = 1500;
|
|
1077
|
+
var STRETCH_TAU = 0.05;
|
|
1078
|
+
var OVERHANG = 4;
|
|
1079
|
+
var LAND = { response: 0.2, damping: 0.8 };
|
|
1080
|
+
var LIFT = { response: 0.2, damping: 1 };
|
|
1081
|
+
var createNavDrag = () => ({
|
|
1082
|
+
active: false,
|
|
1083
|
+
x: { x: 0, v: 0, target: 0 },
|
|
1084
|
+
lift: { x: 0, v: 0, target: 0 },
|
|
1085
|
+
stretch: 0,
|
|
1086
|
+
index: 0,
|
|
1087
|
+
lastX: 0
|
|
512
1088
|
});
|
|
513
|
-
var
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
1089
|
+
var draggable = (layout) => layout.form === "bar" && !layout.minimized && layout.items.length > 0;
|
|
1090
|
+
function lensSize(layout) {
|
|
1091
|
+
const cell = layout.items[0];
|
|
1092
|
+
const pad = 2 * NAV_SIZES.lens.inflate;
|
|
1093
|
+
return { width: cell.width + pad, height: cell.height + pad };
|
|
1094
|
+
}
|
|
1095
|
+
function clampCentre(layout, x) {
|
|
1096
|
+
const half = lensSize(layout).width / 2;
|
|
1097
|
+
const { body } = layout;
|
|
1098
|
+
return Math.min(Math.max(x, body.x + half - OVERHANG), body.x + body.width - half + OVERHANG);
|
|
1099
|
+
}
|
|
1100
|
+
function indexAt(layout, x) {
|
|
1101
|
+
const first2 = layout.items[0];
|
|
1102
|
+
const i = Math.floor((x - first2.x) / first2.width);
|
|
1103
|
+
return Math.min(Math.max(i, 0), layout.items.length - 1);
|
|
1104
|
+
}
|
|
1105
|
+
var centreOf = (layout, index) => layout.items[index].x + layout.items[index].width / 2;
|
|
1106
|
+
function drop(state) {
|
|
1107
|
+
state.active = false;
|
|
1108
|
+
state.lift.x = 0;
|
|
1109
|
+
state.lift.v = 0;
|
|
1110
|
+
state.lift.target = 0;
|
|
1111
|
+
}
|
|
1112
|
+
function settleTowards(state, layout, index) {
|
|
1113
|
+
state.active = false;
|
|
1114
|
+
state.index = index;
|
|
1115
|
+
state.x.target = centreOf(layout, index);
|
|
1116
|
+
state.lift.target = 0;
|
|
1117
|
+
}
|
|
1118
|
+
function pressNavDrag(state, layout, pointerX) {
|
|
1119
|
+
if (!draggable(layout)) return;
|
|
1120
|
+
const x = clampCentre(layout, pointerX);
|
|
1121
|
+
state.active = true;
|
|
1122
|
+
state.x.x = x;
|
|
1123
|
+
state.x.v = 0;
|
|
1124
|
+
state.x.target = x;
|
|
1125
|
+
state.lift.target = 1;
|
|
1126
|
+
state.stretch = 0;
|
|
1127
|
+
state.lastX = pointerX;
|
|
1128
|
+
state.index = indexAt(layout, x);
|
|
1129
|
+
}
|
|
1130
|
+
function moveNavDrag(state, layout, pointerX, dt) {
|
|
1131
|
+
if (!state.active || !draggable(layout)) return;
|
|
1132
|
+
const x = clampCentre(layout, pointerX);
|
|
1133
|
+
state.x.x = x;
|
|
1134
|
+
state.x.v = 0;
|
|
1135
|
+
state.x.target = x;
|
|
1136
|
+
const speed = dt > 0 ? Math.abs(pointerX - state.lastX) / dt : 0;
|
|
1137
|
+
const goal = MAX_STRETCH * Math.min(speed / SATURATION_SPEED, 1);
|
|
1138
|
+
state.stretch += (goal - state.stretch) * (dt > 0 ? 1 - Math.exp(-dt / STRETCH_TAU) : 1);
|
|
1139
|
+
state.lastX = pointerX;
|
|
1140
|
+
state.index = indexAt(layout, x);
|
|
1141
|
+
}
|
|
1142
|
+
function releaseNavDrag(state, layout) {
|
|
1143
|
+
if (!draggable(layout)) {
|
|
1144
|
+
drop(state);
|
|
1145
|
+
return state.index;
|
|
1146
|
+
}
|
|
1147
|
+
const index = indexAt(layout, state.x.x);
|
|
1148
|
+
settleTowards(state, layout, index);
|
|
1149
|
+
return index;
|
|
1150
|
+
}
|
|
1151
|
+
function cancelNavDrag(state, layout, selected) {
|
|
1152
|
+
if (!draggable(layout)) {
|
|
1153
|
+
drop(state);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const index = selected >= 0 && selected < layout.items.length ? selected : indexAt(layout, state.x.x);
|
|
1157
|
+
settleTowards(state, layout, index);
|
|
1158
|
+
}
|
|
1159
|
+
function stepNavDrag(state, dt, reduceMotion = false) {
|
|
1160
|
+
let moving = state.active;
|
|
1161
|
+
if (state.stretch > 0) {
|
|
1162
|
+
state.stretch *= Math.exp(-Math.max(dt, 0) / STRETCH_TAU);
|
|
1163
|
+
if (reduceMotion || state.stretch < 1e-3) state.stretch = 0;
|
|
1164
|
+
else moving = true;
|
|
1165
|
+
}
|
|
1166
|
+
if (!state.active && stepSpring(state.x, dt, LAND.response, LAND.damping, reduceMotion)) moving = true;
|
|
1167
|
+
if (stepSpring(state.lift, dt, LIFT.response, LIFT.damping, reduceMotion)) moving = true;
|
|
1168
|
+
return moving;
|
|
1169
|
+
}
|
|
1170
|
+
function navDragFrame(state, layout) {
|
|
1171
|
+
if (!draggable(layout)) return null;
|
|
1172
|
+
const lift = Math.min(Math.max(state.lift.x, 0), 1);
|
|
1173
|
+
if (!state.active && lift === 0 && state.x.x === state.x.target) return null;
|
|
1174
|
+
const size = lensSize(layout);
|
|
1175
|
+
const width = size.width * (1 + state.stretch);
|
|
1176
|
+
const height = size.height * (1 - state.stretch / 2);
|
|
1177
|
+
const cy = layout.body.y + layout.body.height / 2;
|
|
1178
|
+
return {
|
|
1179
|
+
lens: { x: state.x.x - width / 2, y: cy - height / 2, width, height, radius: height / 2 },
|
|
1180
|
+
lift,
|
|
1181
|
+
hover: indexAt(layout, state.x.x)
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/kit/nav/form.ts
|
|
1186
|
+
var SEARCH = {
|
|
1187
|
+
bar: { form: "button", place: "bar" },
|
|
1188
|
+
rail: { form: "item", place: "rail" },
|
|
1189
|
+
sidebar: { form: "field", place: "sidebar" }
|
|
1190
|
+
};
|
|
1191
|
+
function fixedNavigationForm(nav, form) {
|
|
1192
|
+
if (nav.form === form) return nav;
|
|
1193
|
+
return {
|
|
1194
|
+
...nav,
|
|
1195
|
+
form,
|
|
1196
|
+
edge: form === "bar" ? "bottom" : "leading",
|
|
1197
|
+
secondaryVisible: form === "sidebar",
|
|
1198
|
+
search: nav.search ? { ...nav.search, ...SEARCH[form] } : null,
|
|
1199
|
+
expandable: form === "rail" ? nav.expandable : null,
|
|
1200
|
+
collapsible: form === "sidebar"
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// src/kit/nav/minimize.ts
|
|
1205
|
+
var MINIMIZE_THRESHOLD = 24;
|
|
1206
|
+
var MINIMIZE_JITTER = 4;
|
|
1207
|
+
var createMinimize = (scrollTop = 0) => ({ minimized: false, run: 0, last: scrollTop });
|
|
1208
|
+
function stepMinimize(state, scrollTop) {
|
|
1209
|
+
const delta = scrollTop - state.last;
|
|
1210
|
+
state.last = scrollTop;
|
|
1211
|
+
if (scrollTop <= 0) {
|
|
1212
|
+
state.minimized = false;
|
|
1213
|
+
state.run = 0;
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
if (delta === 0) return state.minimized;
|
|
1217
|
+
const along2 = Math.sign(delta) === Math.sign(state.run) || Math.abs(delta) < MINIMIZE_JITTER;
|
|
1218
|
+
state.run = along2 ? state.run + delta : delta;
|
|
1219
|
+
if (state.run >= MINIMIZE_THRESHOLD) state.minimized = true;
|
|
1220
|
+
else if (state.run <= -MINIMIZE_THRESHOLD) state.minimized = false;
|
|
1221
|
+
return state.minimized;
|
|
1222
|
+
}
|
|
1223
|
+
var clampScrollTop = (scrollTop, scrollHeight, clientHeight) => Math.min(Math.max(scrollTop, 0), Math.max(scrollHeight - clientHeight, 0));
|
|
1224
|
+
function expandMinimize(state) {
|
|
1225
|
+
state.minimized = false;
|
|
1226
|
+
state.run = 0;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// src/kit/nav/morph.ts
|
|
1230
|
+
var NAV_MORPH_TIMING = { response: 1.06, damping: 1 };
|
|
1231
|
+
var NAV_MINIMIZE_TIMING = FORM_MORPH_TIMING;
|
|
1232
|
+
var LIFT_END = 0.12;
|
|
1233
|
+
var CONTRACT = [0.08, 0.38];
|
|
1234
|
+
var TRAVEL = [0.36, 0.56];
|
|
1235
|
+
var GROW = [0.52, 0.86];
|
|
1236
|
+
var SETTLE_START = 0.82;
|
|
1237
|
+
var INK_OUT = [0.08, 0.28];
|
|
1238
|
+
var INK_IN = [0.62, 0.86];
|
|
1239
|
+
var SWAP_AT = 0.46;
|
|
1240
|
+
var SMOOTHING = 24;
|
|
1241
|
+
var SEED = 8;
|
|
1242
|
+
var STAND_IN = 44;
|
|
1243
|
+
var clamp01 = (x) => Math.min(Math.max(x, 0), 1);
|
|
1244
|
+
var mix2 = (a, b, t) => a + (b - a) * t;
|
|
1245
|
+
var mixBox = (a, b, t) => ({
|
|
1246
|
+
x: mix2(a.x, b.x, t),
|
|
1247
|
+
y: mix2(a.y, b.y, t),
|
|
1248
|
+
width: mix2(a.width, b.width, t),
|
|
1249
|
+
height: mix2(a.height, b.height, t),
|
|
1250
|
+
radius: mix2(a.radius, b.radius, t)
|
|
519
1251
|
});
|
|
520
|
-
var
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
1252
|
+
var inflate = (b, d) => ({
|
|
1253
|
+
x: b.x - d,
|
|
1254
|
+
y: b.y - d,
|
|
1255
|
+
width: b.width + 2 * d,
|
|
1256
|
+
height: b.height + 2 * d,
|
|
1257
|
+
radius: b.radius + d
|
|
1258
|
+
});
|
|
1259
|
+
var centred = (cx, cy, size) => ({ x: cx - size / 2, y: cy - size / 2, width: size, height: size, radius: size / 2 });
|
|
1260
|
+
var trailingEnd = (body, size) => centred(body.x + body.width - body.height / 2, body.y + body.height / 2, size);
|
|
1261
|
+
var pillOrStandIn = (layout) => layout.pill ?? centred(layout.body.x + layout.body.width / 2, layout.body.y + layout.body.height / 2, STAND_IN);
|
|
1262
|
+
var separateSearch = (layout) => layout.form === "bar" ? layout.search : null;
|
|
1263
|
+
function restFrame(layout, side) {
|
|
1264
|
+
const search = separateSearch(layout);
|
|
1265
|
+
return {
|
|
1266
|
+
phase: "rest",
|
|
1267
|
+
body: layout.body,
|
|
1268
|
+
second: search ? { box: search, smoothing: 0 } : null,
|
|
1269
|
+
lens: null,
|
|
1270
|
+
ink: side === "from" ? { outgoing: 1, incoming: 0 } : { outgoing: 0, incoming: 1 },
|
|
1271
|
+
showing: side
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
var layoutOrigin = (layout) => {
|
|
1275
|
+
const search = separateSearch(layout);
|
|
1276
|
+
return {
|
|
1277
|
+
body: layout.body,
|
|
1278
|
+
second: search ? { box: search, smoothing: 0 } : null,
|
|
1279
|
+
lens: { box: pillOrStandIn(layout), lift: 0 },
|
|
1280
|
+
ink: 1,
|
|
1281
|
+
lead: 0,
|
|
1282
|
+
hold: null
|
|
1283
|
+
};
|
|
526
1284
|
};
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
1285
|
+
var frameOrigin = (frame) => ({
|
|
1286
|
+
body: frame.body,
|
|
1287
|
+
second: frame.second,
|
|
1288
|
+
lens: frame.lens ?? { box: centred(frame.body.x + frame.body.width / 2, frame.body.y + frame.body.height / 2, STAND_IN), lift: 0 },
|
|
1289
|
+
ink: Math.max(frame.ink.outgoing, frame.ink.incoming),
|
|
1290
|
+
lead: CONTRACT[0],
|
|
1291
|
+
hold: null
|
|
1292
|
+
});
|
|
1293
|
+
function phaseAt(tau) {
|
|
1294
|
+
if (tau < CONTRACT[0]) return "lift";
|
|
1295
|
+
if (tau < TRAVEL[0]) return "contract";
|
|
1296
|
+
if (tau < GROW[0]) return "travel";
|
|
1297
|
+
return tau < SETTLE_START ? "grow" : "settle";
|
|
530
1298
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
const
|
|
1299
|
+
function lensFrame(o, to, t) {
|
|
1300
|
+
const tau = o.lead + (1 - o.lead) * t;
|
|
1301
|
+
const u = smoothstep(o.lead, LIFT_END, tau);
|
|
1302
|
+
const base = inflate(o.lens.box, NAV_SIZES.lens.inflate * (1 - o.lens.lift) * u);
|
|
1303
|
+
const lift = mix2(o.lens.lift, 1, u);
|
|
1304
|
+
const toPill = pillOrStandIn(to);
|
|
1305
|
+
const k = smoothstep(TRAVEL[0], TRAVEL[1], tau);
|
|
1306
|
+
const kk = o.hold ? clamp01((k - o.hold.k0) / (1 - o.hold.k0)) : k;
|
|
1307
|
+
const travelling = mixBox(o.hold?.box ?? base, inflate(toPill, NAV_SIZES.lens.inflate), kk);
|
|
1308
|
+
const c = smoothstep(CONTRACT[0], CONTRACT[1], tau);
|
|
1309
|
+
const g = smoothstep(GROW[0], GROW[1], tau);
|
|
1310
|
+
const s = smoothstep(SETTLE_START, 1, tau);
|
|
1311
|
+
const contracted = mixBox(o.body, travelling, c);
|
|
1312
|
+
const body = mixBox(contracted, to.body, g);
|
|
1313
|
+
let second = null;
|
|
1314
|
+
const budding = separateSearch(to);
|
|
1315
|
+
if (o.second && c < 1) {
|
|
1316
|
+
second = {
|
|
1317
|
+
box: mixBox(o.second.box, trailingEnd(contracted, 0), c),
|
|
1318
|
+
smoothing: mix2(o.second.smoothing, SMOOTHING, c)
|
|
1319
|
+
};
|
|
1320
|
+
} else if (budding && g > 0) {
|
|
1321
|
+
second = { box: mixBox(trailingEnd(body, SEED), budding, g), smoothing: SMOOTHING * (1 - g) };
|
|
1322
|
+
}
|
|
535
1323
|
return {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1324
|
+
phase: phaseAt(tau),
|
|
1325
|
+
body,
|
|
1326
|
+
second,
|
|
1327
|
+
lens: { box: mixBox(travelling, toPill, s), lift: lift * (1 - s) },
|
|
1328
|
+
ink: {
|
|
1329
|
+
outgoing: o.ink * (1 - smoothstep(INK_OUT[0], INK_OUT[1], tau)),
|
|
1330
|
+
incoming: smoothstep(INK_IN[0], INK_IN[1], tau)
|
|
1331
|
+
},
|
|
1332
|
+
showing: tau < SWAP_AT ? "from" : "to"
|
|
542
1333
|
};
|
|
543
1334
|
}
|
|
544
|
-
function
|
|
545
|
-
|
|
1335
|
+
function contractFrame(o, to, t) {
|
|
1336
|
+
const body = mixBox(o.body, to.body, t);
|
|
1337
|
+
const target = separateSearch(to);
|
|
1338
|
+
let second = null;
|
|
1339
|
+
if (o.second && target) {
|
|
1340
|
+
second = { box: mixBox(o.second.box, target, t), smoothing: mix2(o.second.smoothing, 0, t) };
|
|
1341
|
+
} else if (o.second) {
|
|
1342
|
+
second = { box: mixBox(o.second.box, trailingEnd(body, 0), t), smoothing: mix2(o.second.smoothing, SMOOTHING, t) };
|
|
1343
|
+
} else if (target) {
|
|
1344
|
+
second = { box: mixBox(trailingEnd(body, SEED), target, t), smoothing: SMOOTHING * (1 - t) };
|
|
1345
|
+
}
|
|
1346
|
+
return {
|
|
1347
|
+
phase: "contract",
|
|
1348
|
+
body,
|
|
1349
|
+
second,
|
|
1350
|
+
lens: null,
|
|
1351
|
+
ink: { outgoing: o.ink * (1 - smoothstep(0, 0.45, t)), incoming: smoothstep(0.55, 1, t) },
|
|
1352
|
+
showing: t < 0.5 ? "from" : "to"
|
|
1353
|
+
};
|
|
546
1354
|
}
|
|
547
|
-
|
|
548
|
-
|
|
1355
|
+
var frameFrom = (o, to, t, via) => via === "lens" ? lensFrame(o, to, t) : contractFrame(o, to, t);
|
|
1356
|
+
function navMorphFrame(from, to, t, via = "lens") {
|
|
1357
|
+
if (t <= 0) return restFrame(from, "from");
|
|
1358
|
+
if (t >= 1) return restFrame(to, "to");
|
|
1359
|
+
return frameFrom(layoutOrigin(from), to, t, via);
|
|
549
1360
|
}
|
|
550
|
-
|
|
551
|
-
|
|
1361
|
+
var createNavMorph = (at) => ({
|
|
1362
|
+
t: { x: 0, v: 0, target: 0 },
|
|
1363
|
+
from: at,
|
|
1364
|
+
to: at,
|
|
1365
|
+
via: "lens",
|
|
1366
|
+
pivot: null,
|
|
1367
|
+
pivotAt: 0
|
|
1368
|
+
});
|
|
1369
|
+
var sameForm = (a, b) => a.form === b.form && a.minimized === b.minimized;
|
|
1370
|
+
var atRest = (s) => s.t.v === 0 && s.t.x === s.t.target && (s.t.x === 0 || s.t.x === 1);
|
|
1371
|
+
function currentNavFrame(state) {
|
|
1372
|
+
const x = clamp01(state.t.x);
|
|
1373
|
+
if (x >= 1) return restFrame(state.to, "to");
|
|
1374
|
+
const { pivot } = state;
|
|
1375
|
+
if (!pivot) return navMorphFrame(state.from, state.to, x, state.via);
|
|
1376
|
+
if (state.pivotAt > 0 && pivot.lens) {
|
|
1377
|
+
const origin = layoutOrigin(state.from);
|
|
1378
|
+
origin.hold = { box: pivot.lens.box, k0: smoothstep(TRAVEL[0], TRAVEL[1], state.pivotAt) };
|
|
1379
|
+
return lensFrame(origin, state.to, x);
|
|
1380
|
+
}
|
|
1381
|
+
return frameFrom(frameOrigin(pivot), state.to, x, state.via);
|
|
552
1382
|
}
|
|
553
|
-
function
|
|
554
|
-
|
|
1383
|
+
function aimNavMorph(state, next, via = "lens") {
|
|
1384
|
+
const { t } = state;
|
|
1385
|
+
if (atRest(state)) {
|
|
1386
|
+
const current = t.x >= 1 ? state.to : state.from;
|
|
1387
|
+
if (sameForm(current, next)) {
|
|
1388
|
+
if (t.x >= 1) state.to = next;
|
|
1389
|
+
else state.from = next;
|
|
1390
|
+
state.pivot = null;
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
state.from = current;
|
|
1394
|
+
state.to = next;
|
|
1395
|
+
state.via = via;
|
|
1396
|
+
state.pivot = null;
|
|
1397
|
+
state.pivotAt = 0;
|
|
1398
|
+
t.x = 0;
|
|
1399
|
+
t.v = 0;
|
|
1400
|
+
t.target = 1;
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (!state.pivot) {
|
|
1404
|
+
if (sameForm(next, state.from)) {
|
|
1405
|
+
state.from = next;
|
|
1406
|
+
t.target = 0;
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
if (sameForm(next, state.to)) {
|
|
1410
|
+
state.to = next;
|
|
1411
|
+
t.target = 1;
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
const x = clamp01(t.x);
|
|
1415
|
+
if (via === "lens" && state.via === "lens" && x < GROW[0]) {
|
|
1416
|
+
if (x >= TRAVEL[0]) {
|
|
1417
|
+
state.pivot = currentNavFrame(state);
|
|
1418
|
+
state.pivotAt = x;
|
|
1419
|
+
}
|
|
1420
|
+
state.to = next;
|
|
1421
|
+
t.target = 1;
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
} else if (sameForm(next, state.to)) {
|
|
1425
|
+
state.to = next;
|
|
1426
|
+
t.target = 1;
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
const frozen = currentNavFrame(state);
|
|
1430
|
+
if (frozen.showing === "to") state.from = state.to;
|
|
1431
|
+
state.pivot = frozen;
|
|
1432
|
+
state.pivotAt = 0;
|
|
1433
|
+
state.to = next;
|
|
1434
|
+
state.via = via;
|
|
1435
|
+
t.x = 0;
|
|
1436
|
+
t.v = 0;
|
|
1437
|
+
t.target = 1;
|
|
1438
|
+
}
|
|
1439
|
+
function stepNavMorph(state, dt, reduceMotion = false) {
|
|
1440
|
+
const timing = state.via === "lens" ? NAV_MORPH_TIMING : NAV_MINIMIZE_TIMING;
|
|
1441
|
+
const moved = stepSpring(state.t, dt, timing.response, timing.damping, reduceMotion);
|
|
1442
|
+
if (!moved && state.t.x >= 1) {
|
|
1443
|
+
state.pivot = null;
|
|
1444
|
+
state.pivotAt = 0;
|
|
1445
|
+
}
|
|
1446
|
+
return moved;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// src/kit/response.ts
|
|
1450
|
+
var LIFTING = { grow: 0.18, defocus: 1.6 };
|
|
1451
|
+
var SINKING = { grow: 0.06, defocus: 1 };
|
|
1452
|
+
function pressResponse(kind) {
|
|
1453
|
+
return atomLift(kind) === 1 ? LIFTING : SINKING;
|
|
555
1454
|
}
|
|
556
1455
|
|
|
557
1456
|
exports.A11Y_NONE = A11Y_NONE;
|
|
558
1457
|
exports.ATOM_AT_REST = ATOM_AT_REST;
|
|
1458
|
+
exports.BAR_SLOTS = BAR_SLOTS;
|
|
1459
|
+
exports.BOTTOM_SHEET_MAX_WIDTH = BOTTOM_SHEET_MAX_WIDTH;
|
|
559
1460
|
exports.BUTTON_SIZES = BUTTON_SIZES;
|
|
560
1461
|
exports.CONTROL_IDLE = CONTROL_IDLE;
|
|
561
1462
|
exports.CONTROL_SIZES = CONTROL_SIZES;
|
|
562
1463
|
exports.DEFAULT_GROWTH_TIMING = DEFAULT_GROWTH_TIMING;
|
|
1464
|
+
exports.DIALOG_MAX_WIDTH = DIALOG_MAX_WIDTH;
|
|
1465
|
+
exports.FORM_MORPH_TIMING = FORM_MORPH_TIMING;
|
|
1466
|
+
exports.HEIGHT_BREAKPOINTS = HEIGHT_BREAKPOINTS;
|
|
1467
|
+
exports.HEIGHT_CLASSES = HEIGHT_CLASSES;
|
|
1468
|
+
exports.INPUT_MIXED = INPUT_MIXED;
|
|
1469
|
+
exports.INPUT_POINTER = INPUT_POINTER;
|
|
1470
|
+
exports.INPUT_TABLET_KEYBOARD = INPUT_TABLET_KEYBOARD;
|
|
1471
|
+
exports.INPUT_TOUCH = INPUT_TOUCH;
|
|
1472
|
+
exports.LIST_PANE_WIDTH = LIST_PANE_WIDTH;
|
|
563
1473
|
exports.MENU_CLOSED = MENU_CLOSED;
|
|
564
1474
|
exports.MENU_CORNERS = MENU_CORNERS;
|
|
565
1475
|
exports.MENU_DROP_BODY = MENU_DROP_BODY;
|
|
566
1476
|
exports.MENU_ITEM_FILL = MENU_ITEM_FILL;
|
|
1477
|
+
exports.MINIMIZE_JITTER = MINIMIZE_JITTER;
|
|
1478
|
+
exports.MINIMIZE_THRESHOLD = MINIMIZE_THRESHOLD;
|
|
567
1479
|
exports.MIN_POINTER_TARGET_DP = MIN_POINTER_TARGET_DP;
|
|
568
1480
|
exports.MIN_TOUCH_TARGET_DP = MIN_TOUCH_TARGET_DP;
|
|
1481
|
+
exports.MORE_ID = MORE_ID;
|
|
1482
|
+
exports.NAV_MINIMIZE_TIMING = NAV_MINIMIZE_TIMING;
|
|
1483
|
+
exports.NAV_MORPH_TIMING = NAV_MORPH_TIMING;
|
|
1484
|
+
exports.NAV_SIZES = NAV_SIZES;
|
|
569
1485
|
exports.POINTER_INPUT = POINTER_INPUT;
|
|
1486
|
+
exports.POSTURE_FLAT = POSTURE_FLAT;
|
|
570
1487
|
exports.PRESS_IDLE = PRESS_IDLE;
|
|
1488
|
+
exports.SIDE_SHEET_MAX_WIDTH = SIDE_SHEET_MAX_WIDTH;
|
|
1489
|
+
exports.SUPPORTING_PANE_MAX_WIDTH = SUPPORTING_PANE_MAX_WIDTH;
|
|
1490
|
+
exports.SURFACE_KINDS = SURFACE_KINDS;
|
|
1491
|
+
exports.TOOLBAR_GAP = TOOLBAR_GAP;
|
|
571
1492
|
exports.TOUCH_INPUT = TOUCH_INPUT;
|
|
572
1493
|
exports.TYPEAHEAD_SECONDS = TYPEAHEAD_SECONDS;
|
|
1494
|
+
exports.WIDTH_BREAKPOINTS = WIDTH_BREAKPOINTS;
|
|
1495
|
+
exports.WIDTH_CLASSES = WIDTH_CLASSES;
|
|
1496
|
+
exports.adaptiveContext = adaptiveContext;
|
|
1497
|
+
exports.aimNavMorph = aimNavMorph;
|
|
573
1498
|
exports.atomAtRest = atomAtRest;
|
|
574
1499
|
exports.atomBody = atomBody;
|
|
575
1500
|
exports.atomLift = atomLift;
|
|
576
1501
|
exports.atomMaterial = atomMaterial;
|
|
1502
|
+
exports.bottomAccessory = bottomAccessory;
|
|
1503
|
+
exports.cancelNavDrag = cancelNavDrag;
|
|
1504
|
+
exports.clampScrollTop = clampScrollTop;
|
|
1505
|
+
exports.classifyWindow = classifyWindow;
|
|
577
1506
|
exports.controlTargets = controlTargets;
|
|
578
1507
|
exports.createControlVisual = createControlVisual;
|
|
1508
|
+
exports.createFormMorph = createFormMorph;
|
|
579
1509
|
exports.createGrowthState = createGrowthState;
|
|
1510
|
+
exports.createMinimize = createMinimize;
|
|
1511
|
+
exports.createNavDrag = createNavDrag;
|
|
1512
|
+
exports.createNavMorph = createNavMorph;
|
|
1513
|
+
exports.currentNavFrame = currentNavFrame;
|
|
580
1514
|
exports.defaultSize = defaultSize;
|
|
1515
|
+
exports.expandMinimize = expandMinimize;
|
|
1516
|
+
exports.fixedNavigationForm = fixedNavigationForm;
|
|
1517
|
+
exports.formMorph = formMorph;
|
|
1518
|
+
exports.formMorphFrame = formMorphFrame;
|
|
581
1519
|
exports.growthFrame = growthFrame;
|
|
582
1520
|
exports.growthInk = growthInk;
|
|
583
1521
|
exports.growthPhase = growthPhase;
|
|
1522
|
+
exports.halfOpen = halfOpen;
|
|
1523
|
+
exports.heightClass = heightClass;
|
|
584
1524
|
exports.hitSlop = hitSlop;
|
|
1525
|
+
exports.hitTarget = hitTarget;
|
|
1526
|
+
exports.hoversWith = hoversWith;
|
|
585
1527
|
exports.iconButtonSize = iconButtonSize;
|
|
1528
|
+
exports.intersectRect = intersectRect;
|
|
586
1529
|
exports.menuActivity = menuActivity;
|
|
587
1530
|
exports.menuBody = menuBody;
|
|
588
1531
|
exports.menuCorner = menuCorner;
|
|
@@ -593,21 +1536,47 @@ exports.menuPlacement = menuPlacement;
|
|
|
593
1536
|
exports.menuSize = menuSize;
|
|
594
1537
|
exports.menuSurfaceFrame = menuSurfaceFrame;
|
|
595
1538
|
exports.minTarget = minTarget;
|
|
1539
|
+
exports.moveNavDrag = moveNavDrag;
|
|
1540
|
+
exports.navDragFrame = navDragFrame;
|
|
1541
|
+
exports.navLayout = navLayout;
|
|
1542
|
+
exports.navMorphFrame = navMorphFrame;
|
|
1543
|
+
exports.navigationForm = navigationForm;
|
|
596
1544
|
exports.nestedRadius = nestedRadius;
|
|
1545
|
+
exports.paneLayout = paneLayout;
|
|
1546
|
+
exports.postureFromSegments = postureFromSegments;
|
|
1547
|
+
exports.postureSplit = postureSplit;
|
|
597
1548
|
exports.presentationFor = presentationFor;
|
|
1549
|
+
exports.presentationForm = presentationForm;
|
|
1550
|
+
exports.pressNavDrag = pressNavDrag;
|
|
598
1551
|
exports.pressResponse = pressResponse;
|
|
1552
|
+
exports.rebaseFormMorph = rebaseFormMorph;
|
|
1553
|
+
exports.rectContains = rectContains;
|
|
1554
|
+
exports.releaseNavDrag = releaseNavDrag;
|
|
599
1555
|
exports.resolveA11y = resolveA11y;
|
|
600
1556
|
exports.sameA11y = sameA11y;
|
|
1557
|
+
exports.segmentOf = segmentOf;
|
|
601
1558
|
exports.settleControlVisual = settleControlVisual;
|
|
1559
|
+
exports.sizeTier = sizeTier;
|
|
602
1560
|
exports.smoothstep = smoothstep;
|
|
603
1561
|
exports.stepControl = stepControl;
|
|
604
1562
|
exports.stepControlVisual = stepControlVisual;
|
|
605
1563
|
exports.stepFocus = stepFocus;
|
|
1564
|
+
exports.stepFormMorph = stepFormMorph;
|
|
606
1565
|
exports.stepGrowth = stepGrowth;
|
|
607
1566
|
exports.stepHover = stepHover;
|
|
608
1567
|
exports.stepMenu = stepMenu;
|
|
1568
|
+
exports.stepMinimize = stepMinimize;
|
|
1569
|
+
exports.stepNavDrag = stepNavDrag;
|
|
1570
|
+
exports.stepNavMorph = stepNavMorph;
|
|
609
1571
|
exports.stepPress = stepPress;
|
|
610
1572
|
exports.stepSpring = stepSpring;
|
|
1573
|
+
exports.supportingPane = supportingPane;
|
|
1574
|
+
exports.surfaceRegion = surfaceRegion;
|
|
1575
|
+
exports.toolbarLayout = toolbarLayout;
|
|
1576
|
+
exports.toolbarPlacement = toolbarPlacement;
|
|
611
1577
|
exports.visualAtomState = visualAtomState;
|
|
1578
|
+
exports.widthAtLeast = widthAtLeast;
|
|
1579
|
+
exports.widthClass = widthClass;
|
|
1580
|
+
exports.windowSegments = windowSegments;
|
|
612
1581
|
//# sourceMappingURL=index.cjs.map
|
|
613
1582
|
//# sourceMappingURL=index.cjs.map
|