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/README.md
CHANGED
|
@@ -51,10 +51,55 @@ import { MENU_CLOSED, menuPlacement, menuSurfaceFrame, stepMenu } from 'vireuiki
|
|
|
51
51
|
|
|
52
52
|
const step = stepMenu(MENU_CLOSED, { type: 'open', focus: 'first' }, items); // items: { label, disabled, focusable? }[]
|
|
53
53
|
// step.focus says where focus goes; a choice always closes the menu.
|
|
54
|
-
const { rect, anchor } = menuPlacement(triggerRect, menuSize,
|
|
54
|
+
const { rect, anchor } = menuPlacement(triggerRect, menuSize, segment); // the trigger's segment of the window, or the viewport
|
|
55
55
|
const surface = menuSurfaceFrame(triggerBox, { ...rect, radius }, anchor, growthPhase(state));
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Adaptive model
|
|
59
|
+
|
|
60
|
+
What every atom and pattern becomes across window classes, input and posture is one pure model, so every target
|
|
61
|
+
decides the same way. The context is the window in dp, the input in three layers (the primary pointer, any coarse
|
|
62
|
+
input, hover, a keyboard), the posture of a folding screen and reduced motion; the functions turn it into forms.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { adaptiveContext, halfOpen, INPUT_MIXED, navigationForm, paneLayout, presentationForm } from 'vireuikit';
|
|
66
|
+
|
|
67
|
+
const window = { width: 673, height: 841 };
|
|
68
|
+
const ctx = adaptiveContext({ window, input: INPUT_MIXED, posture: halfOpen('book', window) });
|
|
69
|
+
|
|
70
|
+
navigationForm(ctx, { primary: 4, secondary: 2, search: true }).form; // 'rail'
|
|
71
|
+
paneLayout(ctx, { selected: true, showing: 'detail' }).split; // { kind: 'fold', gap }: the panes split at the fold
|
|
72
|
+
presentationForm('sheet', ctx).form; // 'bottom-sheet'; a side sheet from 840 dp
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- `classifyWindow` holds every threshold: widths 600, 840, 1200 and 1600 dp, heights 480 and 900.
|
|
76
|
+
- `navigationForm`: a bar on a compact width and in tabletop, a rail on medium, expanded and a compact height, a
|
|
77
|
+
sidebar from large; the same primary destinations on every form, at most five bar slots with search, no "More".
|
|
78
|
+
`bottomAccessory` places a strip above the bar, in its row or along the content.
|
|
79
|
+
- `presentationForm` for a menu, picker, popover, sheet, modal sheet, alert or dialog: a popover is a sheet grown from
|
|
80
|
+
its source below 600, a supplementary sheet is a bottom sheet below 840 and a side sheet from there, sheets fill
|
|
81
|
+
the window on a compact height, and every surface lives in one segment, never across the fold.
|
|
82
|
+
- `paneLayout` (list-detail: one pane below 840, a 360 or 412 dp list beside the detail, split at the fold in book
|
|
83
|
+
posture), `supportingPane` (beside the content from large, over it on expanded, remembered), `postureSplit`.
|
|
84
|
+
- `toolbarLayout` fits a toolbar into its container: neighbours merge, then the lowest priority goes into "…";
|
|
85
|
+
badged items and the main action stay. `toolbarPlacement` says where the window puts it.
|
|
86
|
+
- `sizeTier` follows the primary pointer and `hitTarget` any coarse input, so a laptop with a touch screen gets the
|
|
87
|
+
pointer tier with a 44 dp hit area.
|
|
88
|
+
- `formMorph`, `stepFormMorph` and `formMorphFrame` flow one body into its new form on the growth's springs; turned
|
|
89
|
+
back mid-way it runs back from where it is, and under reduced motion it lands in one frame.
|
|
90
|
+
|
|
91
|
+
On the web the elements collect their part of the context themselves. A shell that lays itself out from the model
|
|
92
|
+
watches its root:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { setInputOverride, setViewportOverride, watchAdaptiveContext } from 'vireuikit/web';
|
|
96
|
+
|
|
97
|
+
const stop = watchAdaptiveContext(shell, (ctx) => render(ctx)); // told now and on every change
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`setInputOverride` lets a host that knows its input better decide the layers, and `setViewportOverride` makes an
|
|
101
|
+
element the window, with its own segments; the lab's adaptive stage simulates devices with both.
|
|
102
|
+
|
|
58
103
|
## Web elements
|
|
59
104
|
|
|
60
105
|
`vireuikit/web` upgrades native controls with custom elements. The control inside works before the
|
|
@@ -78,9 +123,10 @@ setA11yOverride({ reduceTransparency: true });
|
|
|
78
123
|
- `<vire-icon-button>` lifts into the glass under the finger; the lens response is visible while the
|
|
79
124
|
press is held.
|
|
80
125
|
- Attributes: `glass` (empty or `present`: a lens at rest; `touch`: a lens only under the finger; absent: no
|
|
81
|
-
glass), `size` (`touch`, `small`, `medium`, `large`, `xlarge`; by default `touch`
|
|
82
|
-
`medium` otherwise), `disabled`.
|
|
83
|
-
-
|
|
126
|
+
glass), `size` (`touch`, `small`, `medium`, `large`, `xlarge`; by default `touch` when the primary pointer is
|
|
127
|
+
coarse and `medium` otherwise), `disabled`.
|
|
128
|
+
- Wherever a coarse input is present, a touch screen on a laptop too, every size keeps a hit area of at least 44 dp
|
|
129
|
+
without changing layout. Hover comes from the pointer that entered: a mouse on a tablet hovers, a finger never does.
|
|
84
130
|
- The lens reads the backdrop from declared colours and cannot see an image, a video or a canvas. A host that knows
|
|
85
131
|
what lies under the control sets the element's `sample` (a `BackdropSample` from `vireglass`), at any time; `null`
|
|
86
132
|
hands it back to the probe.
|
|
@@ -145,7 +191,8 @@ setA11yOverride({ reduceTransparency: true });
|
|
|
145
191
|
`<vire-icon-button>`), so there is never glass on glass.
|
|
146
192
|
- Attributes: `glass` (empty, `present` or `touch`: the surface is a lens; absent or anything else: an opaque surface), `size` (as for the buttons; rows are as tall
|
|
147
193
|
as the tier), `corner` (`top-left`, `top-right`, `bottom-left`, `bottom-right`: the trigger corner the menu pins to;
|
|
148
|
-
by default the one facing the nearest
|
|
194
|
+
by default the one facing the nearest corner of the trigger's segment of the window). On a folding screen the menu
|
|
195
|
+
stays in its trigger's half, every frame of its growth included, and scrolls if it is taller. `sample` works as on the buttons; without one, the backdrop
|
|
149
196
|
under the menu is read once as it opens and held while it is out.
|
|
150
197
|
|
|
151
198
|
Sizes, behaviour state machines and accessibility modes are exported from `vireuikit` and shared by every
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { activeMaterial, applyAccessibility, resolveOptics,
|
|
1
|
+
import { halfMinDp, concentricRadius, activeMaterial, applyAccessibility, resolveOptics, VIREGLASS_SHEET_MATERIAL } from 'vireglass';
|
|
2
2
|
|
|
3
3
|
// src/kit/a11y.ts
|
|
4
4
|
var A11Y_NONE = {
|
|
@@ -25,135 +25,107 @@ function presentationFor(mode, wantsGlass) {
|
|
|
25
25
|
function sameA11y(a, b) {
|
|
26
26
|
return a.reduceTransparency === b.reduceTransparency && a.increaseContrast === b.increaseContrast && a.reduceMotion === b.reduceMotion;
|
|
27
27
|
}
|
|
28
|
-
var ATOM_AT_REST = {
|
|
29
|
-
pressed: 0,
|
|
30
|
-
hovered: 0,
|
|
31
|
-
selected: 0,
|
|
32
|
-
disabled: false
|
|
33
|
-
};
|
|
34
|
-
function atomLift(kind) {
|
|
35
|
-
switch (kind) {
|
|
36
|
-
case "iconButton":
|
|
37
|
-
case "switch":
|
|
38
|
-
case "slider":
|
|
39
|
-
return 1;
|
|
40
|
-
default:
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
var DISABLED_PRESENCE = 0.25;
|
|
45
|
-
var HOVER_GAIN = 0.35;
|
|
46
|
-
function activation(state) {
|
|
47
|
-
if (state.disabled) return 0;
|
|
48
|
-
const clamp2 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
49
|
-
return Math.max(clamp2(state.selected), clamp2(state.pressed), clamp2(state.hovered) * HOVER_GAIN);
|
|
50
|
-
}
|
|
51
|
-
function atomMaterial(base, kind, state = ATOM_AT_REST) {
|
|
52
|
-
const out = activeMaterial(base, activation(state));
|
|
53
|
-
if (!state.disabled) return out;
|
|
54
|
-
return { ...out, presence: out.presence * DISABLED_PRESENCE };
|
|
55
|
-
}
|
|
56
|
-
function atomAtRest(state) {
|
|
57
|
-
return state.pressed === 0 && state.hovered === 0 && state.selected === 0 && state.disabled === false;
|
|
58
|
-
}
|
|
59
|
-
var REST_BODY = { alpha: 0.25, white: 0.15, lift: 0.1 };
|
|
60
|
-
var PRESSED_BODY = { alpha: 0.34, white: 0.35, lift: 0 };
|
|
61
|
-
var FROST_LIFT = 0.26;
|
|
62
|
-
function atomBody(base, state = ATOM_AT_REST, frost = false) {
|
|
63
|
-
const on = activation(state);
|
|
64
|
-
const mix2 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
|
|
65
|
-
let alpha = mix2("alpha");
|
|
66
|
-
let white = mix2("white");
|
|
67
|
-
let lift = mix2("lift");
|
|
68
|
-
if (state.disabled) alpha *= DISABLED_PRESENCE;
|
|
69
|
-
if (frost) {
|
|
70
|
-
const density = applyAccessibility(resolveOptics(base), {
|
|
71
|
-
reduceTransparency: true,
|
|
72
|
-
increaseContrast: false,
|
|
73
|
-
reduceMotion: false
|
|
74
|
-
}).bodyDensity;
|
|
75
|
-
alpha = Math.max(alpha, density);
|
|
76
|
-
white = 0;
|
|
77
|
-
lift = FROST_LIFT;
|
|
78
|
-
}
|
|
79
|
-
return { alpha, white, lift };
|
|
80
|
-
}
|
|
81
28
|
|
|
82
|
-
// src/kit/
|
|
83
|
-
function
|
|
84
|
-
|
|
29
|
+
// src/kit/geometry.ts
|
|
30
|
+
function intersectRect(a, b) {
|
|
31
|
+
const x = Math.max(a.x, b.x);
|
|
32
|
+
const y = Math.max(a.y, b.y);
|
|
33
|
+
const right = Math.min(a.x + a.width, b.x + b.width);
|
|
34
|
+
const bottom = Math.min(a.y + a.height, b.y + b.height);
|
|
35
|
+
return right > x && bottom > y ? { x, y, width: right - x, height: bottom - y } : null;
|
|
85
36
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
function stepHover(event, canHover) {
|
|
89
|
-
return canHover && event.type === "enter";
|
|
37
|
+
function rectContains(outer, inner, tolerance = 0) {
|
|
38
|
+
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;
|
|
90
39
|
}
|
|
91
40
|
|
|
92
|
-
// src/kit/
|
|
93
|
-
var
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return state.by === "pointer" ? { state: PRESS_IDLE, activated: false } : { state, activated: false };
|
|
106
|
-
}
|
|
41
|
+
// src/kit/adaptive/context.ts
|
|
42
|
+
var INPUT_TOUCH = { primary: "coarse", anyCoarse: true, hover: false, keyboard: false };
|
|
43
|
+
var INPUT_POINTER = { primary: "fine", anyCoarse: false, hover: true, keyboard: true };
|
|
44
|
+
var INPUT_MIXED = { primary: "fine", anyCoarse: true, hover: true, keyboard: true };
|
|
45
|
+
var INPUT_TABLET_KEYBOARD = { primary: "coarse", anyCoarse: true, hover: true, keyboard: true };
|
|
46
|
+
var POSTURE_FLAT = { kind: "flat" };
|
|
47
|
+
function adaptiveContext(partial) {
|
|
48
|
+
return {
|
|
49
|
+
window: partial.window,
|
|
50
|
+
input: partial.input ?? INPUT_POINTER,
|
|
51
|
+
posture: partial.posture ?? POSTURE_FLAT,
|
|
52
|
+
reduceMotion: partial.reduceMotion ?? false
|
|
53
|
+
};
|
|
107
54
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
55
|
+
function halfOpen(kind, window, options = {}) {
|
|
56
|
+
const band = Math.max(options.band ?? 0, 0);
|
|
57
|
+
const span = kind === "tabletop" ? window.height : window.width;
|
|
58
|
+
const at = options.at ?? span / 2;
|
|
59
|
+
const start = at - band / 2;
|
|
60
|
+
const bounds = kind === "tabletop" ? { x: 0, y: start, width: window.width, height: band } : { x: start, y: 0, width: band, height: window.height };
|
|
61
|
+
return { kind, fold: { bounds, occluding: options.occluding ?? band > 0 } };
|
|
62
|
+
}
|
|
63
|
+
function windowSegments(ctx) {
|
|
64
|
+
const { width, height } = ctx.window;
|
|
65
|
+
const whole = { x: 0, y: 0, width, height };
|
|
66
|
+
if (ctx.posture.kind === "flat") return [whole];
|
|
67
|
+
const { bounds, occluding } = ctx.posture.fold;
|
|
68
|
+
const tabletop = ctx.posture.kind === "tabletop";
|
|
69
|
+
const span = tabletop ? height : width;
|
|
70
|
+
const lo = Math.min(Math.max(tabletop ? bounds.y : bounds.x, 0), span);
|
|
71
|
+
const hi = Math.min(Math.max(lo + (tabletop ? bounds.height : bounds.width), lo), span);
|
|
72
|
+
const end = occluding ? lo : (lo + hi) / 2;
|
|
73
|
+
const start = occluding ? hi : (lo + hi) / 2;
|
|
74
|
+
const parts = tabletop ? [
|
|
75
|
+
{ x: 0, y: 0, width, height: end },
|
|
76
|
+
{ x: 0, y: start, width, height: height - start }
|
|
77
|
+
] : [
|
|
78
|
+
{ x: 0, y: 0, width: end, height },
|
|
79
|
+
{ x: start, y: 0, width: width - start, height }
|
|
80
|
+
];
|
|
81
|
+
const kept = parts.filter((r) => r.width > 0 && r.height > 0);
|
|
82
|
+
return kept.length ? kept : [whole];
|
|
83
|
+
}
|
|
84
|
+
function postureFromSegments(window, segments) {
|
|
85
|
+
if (segments.length !== 2) return POSTURE_FLAT;
|
|
86
|
+
const [a, b] = [...segments].sort((p, q) => p.x - q.x || p.y - q.y);
|
|
87
|
+
const gapX = b.x - (a.x + a.width);
|
|
88
|
+
if (gapX >= 0 && a.y < b.y + b.height && b.y < a.y + a.height) {
|
|
89
|
+
return { kind: "book", fold: { bounds: { x: a.x + a.width, y: 0, width: gapX, height: window.height }, occluding: gapX > 0 } };
|
|
90
|
+
}
|
|
91
|
+
const [top, bottom] = [...segments].sort((p, q) => p.y - q.y);
|
|
92
|
+
const gapY = bottom.y - (top.y + top.height);
|
|
93
|
+
if (gapY >= 0) {
|
|
94
|
+
return { kind: "tabletop", fold: { bounds: { x: 0, y: top.y + top.height, width: window.width, height: gapY }, occluding: gapY > 0 } };
|
|
95
|
+
}
|
|
96
|
+
return POSTURE_FLAT;
|
|
97
|
+
}
|
|
98
|
+
var area = (r) => r ? r.width * r.height : 0;
|
|
99
|
+
function segmentOf(box, segments) {
|
|
100
|
+
const cx = box.x + box.width / 2;
|
|
101
|
+
const cy = box.y + box.height / 2;
|
|
102
|
+
const holding = segments.find((s) => cx >= s.x && cx < s.x + s.width && cy >= s.y && cy < s.y + s.height);
|
|
103
|
+
if (holding) return holding;
|
|
104
|
+
let best;
|
|
105
|
+
let most = 0;
|
|
106
|
+
for (const s of segments) {
|
|
107
|
+
const overlap = area(intersectRect(s, box));
|
|
108
|
+
if (overlap > most) {
|
|
109
|
+
most = overlap;
|
|
110
|
+
best = s;
|
|
129
111
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
state: { ...state, press, focused: !state.disabled && stepFocus(event) },
|
|
140
|
-
activated: false
|
|
141
|
-
};
|
|
112
|
+
}
|
|
113
|
+
if (best) return best;
|
|
114
|
+
let nearest;
|
|
115
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
116
|
+
for (const s of segments) {
|
|
117
|
+
const d = Math.hypot(s.x + s.width / 2 - cx, s.y + s.height / 2 - cy);
|
|
118
|
+
if (d < distance) {
|
|
119
|
+
distance = d;
|
|
120
|
+
nearest = s;
|
|
142
121
|
}
|
|
143
|
-
case "disable":
|
|
144
|
-
return {
|
|
145
|
-
state: event.disabled ? { press: PRESS_IDLE, hovered: false, focused: false, disabled: true } : { ...state, disabled: false },
|
|
146
|
-
activated: false
|
|
147
|
-
};
|
|
148
122
|
}
|
|
123
|
+
return nearest;
|
|
149
124
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
focus: state.focused ? 1 : 0,
|
|
155
|
-
disabled: state.disabled
|
|
156
|
-
};
|
|
125
|
+
function surfaceRegion(ctx, source) {
|
|
126
|
+
const segments = windowSegments(ctx);
|
|
127
|
+
if (source) return segmentOf(source, segments) ?? segments[0];
|
|
128
|
+
return segments.reduce((best, s) => area(s) > area(best) ? s : best, segments[0]);
|
|
157
129
|
}
|
|
158
130
|
function stepSpring(s, dt, response, damping, instant = false) {
|
|
159
131
|
if (instant) {
|
|
@@ -308,6 +280,200 @@ function stepGrowth(state, dt, open, reduceMotion = false, timing = DEFAULT_GROW
|
|
|
308
280
|
);
|
|
309
281
|
return movedShrink || movedGrow;
|
|
310
282
|
}
|
|
283
|
+
var CONTROL_SIZES = ["touch", "small", "medium", "large", "xlarge"];
|
|
284
|
+
var MIN_TOUCH_TARGET_DP = 44;
|
|
285
|
+
var MIN_POINTER_TARGET_DP = 24;
|
|
286
|
+
var capsule = (height, paddingX) => ({
|
|
287
|
+
height,
|
|
288
|
+
paddingX,
|
|
289
|
+
icon: Math.round(height / 2),
|
|
290
|
+
radius: height / 2,
|
|
291
|
+
shape: "capsule"
|
|
292
|
+
});
|
|
293
|
+
var rounded = (height, paddingX) => ({
|
|
294
|
+
height,
|
|
295
|
+
paddingX,
|
|
296
|
+
icon: Math.round(height / 2),
|
|
297
|
+
radius: height / 4,
|
|
298
|
+
shape: "rounded"
|
|
299
|
+
});
|
|
300
|
+
var BUTTON_SIZES = {
|
|
301
|
+
touch: capsule(44, 20),
|
|
302
|
+
small: rounded(24, 10),
|
|
303
|
+
medium: rounded(32, 14),
|
|
304
|
+
large: capsule(40, 18),
|
|
305
|
+
xlarge: capsule(48, 22)
|
|
306
|
+
};
|
|
307
|
+
function iconButtonSize(size) {
|
|
308
|
+
const spec = BUTTON_SIZES[size];
|
|
309
|
+
return { ...spec, width: spec.height, paddingX: 0 };
|
|
310
|
+
}
|
|
311
|
+
var MENU_PADDING = 0.3;
|
|
312
|
+
function menuSize(size) {
|
|
313
|
+
const spec = BUTTON_SIZES[size];
|
|
314
|
+
const padding = Math.round(spec.height * MENU_PADDING);
|
|
315
|
+
return {
|
|
316
|
+
row: spec.height,
|
|
317
|
+
padding,
|
|
318
|
+
itemRadius: spec.radius,
|
|
319
|
+
radius: spec.radius + padding,
|
|
320
|
+
icon: spec.icon,
|
|
321
|
+
labelPad: spec.paddingX
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function nestedRadius(outerRadius, inset, minimum = 0) {
|
|
325
|
+
return concentricRadius(outerRadius, inset, minimum);
|
|
326
|
+
}
|
|
327
|
+
function defaultSize(input) {
|
|
328
|
+
return input.touch ? "touch" : "medium";
|
|
329
|
+
}
|
|
330
|
+
function hitSlop(bodyDp, minTargetDp) {
|
|
331
|
+
return bodyDp >= minTargetDp ? 0 : (minTargetDp - bodyDp) / 2;
|
|
332
|
+
}
|
|
333
|
+
function minTarget(input) {
|
|
334
|
+
return input.touch ? MIN_TOUCH_TARGET_DP : MIN_POINTER_TARGET_DP;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/kit/adaptive/input.ts
|
|
338
|
+
function sizeTier(input) {
|
|
339
|
+
return defaultSize({ touch: input.primary === "coarse" });
|
|
340
|
+
}
|
|
341
|
+
function hitTarget(input) {
|
|
342
|
+
return minTarget({ touch: input.anyCoarse });
|
|
343
|
+
}
|
|
344
|
+
function hoversWith(pointerType) {
|
|
345
|
+
return pointerType !== "touch";
|
|
346
|
+
}
|
|
347
|
+
var ATOM_AT_REST = {
|
|
348
|
+
pressed: 0,
|
|
349
|
+
hovered: 0,
|
|
350
|
+
selected: 0,
|
|
351
|
+
disabled: false
|
|
352
|
+
};
|
|
353
|
+
function atomLift(kind) {
|
|
354
|
+
switch (kind) {
|
|
355
|
+
case "iconButton":
|
|
356
|
+
case "switch":
|
|
357
|
+
case "slider":
|
|
358
|
+
return 1;
|
|
359
|
+
default:
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
var DISABLED_PRESENCE = 0.25;
|
|
364
|
+
var HOVER_GAIN = 0.35;
|
|
365
|
+
function activation(state) {
|
|
366
|
+
if (state.disabled) return 0;
|
|
367
|
+
const clamp2 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
368
|
+
return Math.max(clamp2(state.selected), clamp2(state.pressed), clamp2(state.hovered) * HOVER_GAIN);
|
|
369
|
+
}
|
|
370
|
+
function atomMaterial(base, kind, state = ATOM_AT_REST) {
|
|
371
|
+
const out = activeMaterial(base, activation(state));
|
|
372
|
+
if (!state.disabled) return out;
|
|
373
|
+
return { ...out, presence: out.presence * DISABLED_PRESENCE };
|
|
374
|
+
}
|
|
375
|
+
function atomAtRest(state) {
|
|
376
|
+
return state.pressed === 0 && state.hovered === 0 && state.selected === 0 && state.disabled === false;
|
|
377
|
+
}
|
|
378
|
+
var REST_BODY = { alpha: 0.25, white: 0.15, lift: 0.1 };
|
|
379
|
+
var PRESSED_BODY = { alpha: 0.34, white: 0.35, lift: 0 };
|
|
380
|
+
var FROST_LIFT = 0.26;
|
|
381
|
+
function atomBody(base, state = ATOM_AT_REST, frost = false) {
|
|
382
|
+
const on = activation(state);
|
|
383
|
+
const mix2 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
|
|
384
|
+
let alpha = mix2("alpha");
|
|
385
|
+
let white = mix2("white");
|
|
386
|
+
let lift = mix2("lift");
|
|
387
|
+
if (state.disabled) alpha *= DISABLED_PRESENCE;
|
|
388
|
+
if (frost) {
|
|
389
|
+
const density = applyAccessibility(resolveOptics(base), {
|
|
390
|
+
reduceTransparency: true,
|
|
391
|
+
increaseContrast: false,
|
|
392
|
+
reduceMotion: false
|
|
393
|
+
}).bodyDensity;
|
|
394
|
+
alpha = Math.max(alpha, density);
|
|
395
|
+
white = 0;
|
|
396
|
+
lift = FROST_LIFT;
|
|
397
|
+
}
|
|
398
|
+
return { alpha, white, lift };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/kit/behavior/focus.ts
|
|
402
|
+
function stepFocus(event) {
|
|
403
|
+
return event.type === "focus" && event.visible;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/kit/behavior/hover.ts
|
|
407
|
+
function stepHover(event, canHover) {
|
|
408
|
+
return canHover && event.type === "enter";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/kit/behavior/press.ts
|
|
412
|
+
var PRESS_IDLE = { by: null };
|
|
413
|
+
function stepPress(state, event, enabled2) {
|
|
414
|
+
switch (event.type) {
|
|
415
|
+
case "down":
|
|
416
|
+
if (!enabled2 || state.by !== null) return { state, activated: false };
|
|
417
|
+
return { state: { by: event.source }, activated: false };
|
|
418
|
+
case "up":
|
|
419
|
+
if (state.by !== event.source) return { state, activated: false };
|
|
420
|
+
return { state: PRESS_IDLE, activated: enabled2 };
|
|
421
|
+
case "cancel":
|
|
422
|
+
return { state: PRESS_IDLE, activated: false };
|
|
423
|
+
case "leave":
|
|
424
|
+
return state.by === "pointer" ? { state: PRESS_IDLE, activated: false } : { state, activated: false };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/kit/behavior/control.ts
|
|
429
|
+
var TOUCH_INPUT = { hover: false };
|
|
430
|
+
var POINTER_INPUT = { hover: true };
|
|
431
|
+
var CONTROL_IDLE = {
|
|
432
|
+
press: PRESS_IDLE,
|
|
433
|
+
hovered: false,
|
|
434
|
+
focused: false,
|
|
435
|
+
disabled: false
|
|
436
|
+
};
|
|
437
|
+
function stepControl(state, event, caps) {
|
|
438
|
+
switch (event.type) {
|
|
439
|
+
case "down":
|
|
440
|
+
case "up":
|
|
441
|
+
case "cancel": {
|
|
442
|
+
const next = stepPress(state.press, event, !state.disabled);
|
|
443
|
+
return { state: { ...state, press: next.state }, activated: next.activated };
|
|
444
|
+
}
|
|
445
|
+
case "leave": {
|
|
446
|
+
const next = stepPress(state.press, event, !state.disabled);
|
|
447
|
+
return { state: { ...state, press: next.state, hovered: false }, activated: false };
|
|
448
|
+
}
|
|
449
|
+
case "enter":
|
|
450
|
+
return {
|
|
451
|
+
state: { ...state, hovered: !state.disabled && stepHover(event, caps.hover) },
|
|
452
|
+
activated: false
|
|
453
|
+
};
|
|
454
|
+
case "focus":
|
|
455
|
+
case "blur": {
|
|
456
|
+
const press = event.type === "blur" && state.press.by === "keyboard" ? PRESS_IDLE : state.press;
|
|
457
|
+
return {
|
|
458
|
+
state: { ...state, press, focused: !state.disabled && stepFocus(event) },
|
|
459
|
+
activated: false
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
case "disable":
|
|
463
|
+
return {
|
|
464
|
+
state: event.disabled ? { press: PRESS_IDLE, hovered: false, focused: false, disabled: true } : { ...state, disabled: false },
|
|
465
|
+
activated: false
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function controlTargets(state) {
|
|
470
|
+
return {
|
|
471
|
+
pressed: state.press.by !== null ? 1 : 0,
|
|
472
|
+
hovered: state.hovered ? 1 : 0,
|
|
473
|
+
focus: state.focused ? 1 : 0,
|
|
474
|
+
disabled: state.disabled
|
|
475
|
+
};
|
|
476
|
+
}
|
|
311
477
|
|
|
312
478
|
// src/kit/behavior/visual.ts
|
|
313
479
|
var createControlVisual = () => ({
|
|
@@ -401,40 +567,46 @@ var MENU_CORNERS = {
|
|
|
401
567
|
"bottom-left": { x: -1, y: 1 },
|
|
402
568
|
"bottom-right": { x: 1, y: 1 }
|
|
403
569
|
};
|
|
404
|
-
function menuCorner(trigger,
|
|
570
|
+
function menuCorner(trigger, region) {
|
|
571
|
+
const x0 = region.x ?? 0;
|
|
572
|
+
const y0 = region.y ?? 0;
|
|
405
573
|
return {
|
|
406
|
-
x: trigger.x + trigger.width / 2 <=
|
|
407
|
-
y: trigger.y + trigger.height / 2 <=
|
|
574
|
+
x: trigger.x + trigger.width / 2 <= x0 + region.width / 2 ? -1 : 1,
|
|
575
|
+
y: trigger.y + trigger.height / 2 <= y0 + region.height / 2 ? -1 : 1
|
|
408
576
|
};
|
|
409
577
|
}
|
|
410
578
|
function along(start, length, size, side) {
|
|
411
579
|
return side < 0 ? start : start + length - size;
|
|
412
580
|
}
|
|
413
|
-
function fits(at, size,
|
|
414
|
-
return at >= margin && at + size <=
|
|
581
|
+
function fits(at, size, lo, hi, margin) {
|
|
582
|
+
return at >= lo + margin && at + size <= hi - margin;
|
|
415
583
|
}
|
|
416
584
|
var clamp = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
|
|
417
|
-
function menuPlacement(trigger, menu,
|
|
585
|
+
function menuPlacement(trigger, menu, region, options = {}) {
|
|
418
586
|
const margin = options.margin ?? 8;
|
|
419
|
-
const
|
|
420
|
-
const
|
|
421
|
-
const
|
|
587
|
+
const x0 = region.x ?? 0;
|
|
588
|
+
const y0 = region.y ?? 0;
|
|
589
|
+
const x1 = x0 + region.width;
|
|
590
|
+
const y1 = y0 + region.height;
|
|
591
|
+
const width = Math.max(0, Math.min(menu.width, region.width - 2 * margin));
|
|
592
|
+
const height = Math.max(0, Math.min(menu.height, region.height - 2 * margin));
|
|
593
|
+
const corner = { ...options.corner ?? menuCorner(trigger, region) };
|
|
422
594
|
let x = along(trigger.x, trigger.width, width, corner.x);
|
|
423
595
|
let y = along(trigger.y, trigger.height, height, corner.y);
|
|
424
596
|
if (!options.corner) {
|
|
425
597
|
const fx = along(trigger.x, trigger.width, width, corner.x === -1 ? 1 : -1);
|
|
426
|
-
if (!fits(x, width,
|
|
598
|
+
if (!fits(x, width, x0, x1, margin) && fits(fx, width, x0, x1, margin)) {
|
|
427
599
|
corner.x = corner.x === -1 ? 1 : -1;
|
|
428
600
|
x = fx;
|
|
429
601
|
}
|
|
430
602
|
const fy = along(trigger.y, trigger.height, height, corner.y === -1 ? 1 : -1);
|
|
431
|
-
if (!fits(y, height,
|
|
603
|
+
if (!fits(y, height, y0, y1, margin) && fits(fy, height, y0, y1, margin)) {
|
|
432
604
|
corner.y = corner.y === -1 ? 1 : -1;
|
|
433
605
|
y = fy;
|
|
434
606
|
}
|
|
435
607
|
}
|
|
436
|
-
x = clamp(x, margin,
|
|
437
|
-
y = clamp(y, margin,
|
|
608
|
+
x = clamp(x, x0 + margin, x1 - margin - width);
|
|
609
|
+
y = clamp(y, y0 + margin, y1 - margin - height);
|
|
438
610
|
const rect = { x, y, width, height };
|
|
439
611
|
return {
|
|
440
612
|
rect,
|
|
@@ -448,7 +620,7 @@ function menuActivity(phase) {
|
|
|
448
620
|
const grow = Math.min(Math.max(phase.grow, 0), 1);
|
|
449
621
|
return shrink * (1 - grow);
|
|
450
622
|
}
|
|
451
|
-
function menuSurfaceFrame(trigger, menu, anchor, phase) {
|
|
623
|
+
function menuSurfaceFrame(trigger, menu, anchor, phase, bounds) {
|
|
452
624
|
const frame = growthFrame(
|
|
453
625
|
{ geometry: { width: trigger.width, height: trigger.height, cornerRadius: trigger.radius }, center: center(trigger) },
|
|
454
626
|
{ geometry: { width: menu.width, height: menu.height, cornerRadius: menu.radius }, center: center(menu) },
|
|
@@ -456,16 +628,24 @@ function menuSurfaceFrame(trigger, menu, anchor, phase) {
|
|
|
456
628
|
phase
|
|
457
629
|
);
|
|
458
630
|
const { geometry, center: c } = frame.body;
|
|
459
|
-
|
|
631
|
+
let box = {
|
|
460
632
|
x: c.x - geometry.width / 2,
|
|
461
633
|
y: c.y - geometry.height / 2,
|
|
462
634
|
width: geometry.width,
|
|
463
635
|
height: geometry.height,
|
|
464
636
|
radius: geometry.cornerRadius
|
|
465
637
|
};
|
|
638
|
+
let morph = frame.merge;
|
|
639
|
+
const held = bounds ? intersectRect(box, bounds) : null;
|
|
640
|
+
if (held && (held.width < box.width || held.height < box.height)) {
|
|
641
|
+
const was = center(box);
|
|
642
|
+
const now = center(held);
|
|
643
|
+
box = { ...held, radius: Math.min(box.radius, held.width / 2, held.height / 2) };
|
|
644
|
+
if (morph) morph = { ...morph, offsetX: morph.offsetX + was.x - now.x, offsetY: morph.offsetY + was.y - now.y };
|
|
645
|
+
}
|
|
466
646
|
return {
|
|
467
647
|
box,
|
|
468
|
-
morph
|
|
648
|
+
morph,
|
|
469
649
|
content: { x: menu.x - box.x, y: menu.y - box.y },
|
|
470
650
|
ink: frame.ink,
|
|
471
651
|
activity: menuActivity(phase)
|
|
@@ -498,60 +678,7 @@ var SINKING = { grow: 0.06, defocus: 1 };
|
|
|
498
678
|
function pressResponse(kind) {
|
|
499
679
|
return atomLift(kind) === 1 ? LIFTING : SINKING;
|
|
500
680
|
}
|
|
501
|
-
var CONTROL_SIZES = ["touch", "small", "medium", "large", "xlarge"];
|
|
502
|
-
var MIN_TOUCH_TARGET_DP = 44;
|
|
503
|
-
var MIN_POINTER_TARGET_DP = 24;
|
|
504
|
-
var capsule = (height, paddingX) => ({
|
|
505
|
-
height,
|
|
506
|
-
paddingX,
|
|
507
|
-
icon: Math.round(height / 2),
|
|
508
|
-
radius: height / 2,
|
|
509
|
-
shape: "capsule"
|
|
510
|
-
});
|
|
511
|
-
var rounded = (height, paddingX) => ({
|
|
512
|
-
height,
|
|
513
|
-
paddingX,
|
|
514
|
-
icon: Math.round(height / 2),
|
|
515
|
-
radius: height / 4,
|
|
516
|
-
shape: "rounded"
|
|
517
|
-
});
|
|
518
|
-
var BUTTON_SIZES = {
|
|
519
|
-
touch: capsule(44, 20),
|
|
520
|
-
small: rounded(24, 10),
|
|
521
|
-
medium: rounded(32, 14),
|
|
522
|
-
large: capsule(40, 18),
|
|
523
|
-
xlarge: capsule(48, 22)
|
|
524
|
-
};
|
|
525
|
-
function iconButtonSize(size) {
|
|
526
|
-
const spec = BUTTON_SIZES[size];
|
|
527
|
-
return { ...spec, width: spec.height, paddingX: 0 };
|
|
528
|
-
}
|
|
529
|
-
var MENU_PADDING = 0.3;
|
|
530
|
-
function menuSize(size) {
|
|
531
|
-
const spec = BUTTON_SIZES[size];
|
|
532
|
-
const padding = Math.round(spec.height * MENU_PADDING);
|
|
533
|
-
return {
|
|
534
|
-
row: spec.height,
|
|
535
|
-
padding,
|
|
536
|
-
itemRadius: spec.radius,
|
|
537
|
-
radius: spec.radius + padding,
|
|
538
|
-
icon: spec.icon,
|
|
539
|
-
labelPad: spec.paddingX
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
function nestedRadius(outerRadius, inset, minimum = 0) {
|
|
543
|
-
return concentricRadius(outerRadius, inset, minimum);
|
|
544
|
-
}
|
|
545
|
-
function defaultSize(input) {
|
|
546
|
-
return input.touch ? "touch" : "medium";
|
|
547
|
-
}
|
|
548
|
-
function hitSlop(bodyDp, minTargetDp) {
|
|
549
|
-
return bodyDp >= minTargetDp ? 0 : (minTargetDp - bodyDp) / 2;
|
|
550
|
-
}
|
|
551
|
-
function minTarget(input) {
|
|
552
|
-
return input.touch ? MIN_TOUCH_TARGET_DP : MIN_POINTER_TARGET_DP;
|
|
553
|
-
}
|
|
554
681
|
|
|
555
|
-
export { A11Y_NONE, ATOM_AT_REST, BUTTON_SIZES, CONTROL_IDLE, CONTROL_SIZES, DEFAULT_GROWTH_TIMING, MENU_CLOSED, MENU_CORNERS, MENU_DROP_BODY, MENU_ITEM_FILL, MIN_POINTER_TARGET_DP, MIN_TOUCH_TARGET_DP, POINTER_INPUT, PRESS_IDLE, TOUCH_INPUT, TYPEAHEAD_SECONDS, atomAtRest, atomBody, atomLift, atomMaterial, controlTargets, createControlVisual, createGrowthState, defaultSize, growthFrame, growthInk, growthPhase, hitSlop, iconButtonSize, menuActivity, menuBody, menuCorner, menuGlass, menuItemFill, menuMaterial, menuPlacement, menuSize, menuSurfaceFrame, minTarget, nestedRadius, presentationFor, pressResponse, resolveA11y, sameA11y, settleControlVisual, smoothstep, stepControl, stepControlVisual, stepFocus, stepGrowth, stepHover, stepMenu, stepPress, stepSpring, visualAtomState };
|
|
556
|
-
//# sourceMappingURL=chunk-
|
|
557
|
-
//# sourceMappingURL=chunk-
|
|
682
|
+
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 };
|
|
683
|
+
//# sourceMappingURL=chunk-VY5MD3CX.js.map
|
|
684
|
+
//# sourceMappingURL=chunk-VY5MD3CX.js.map
|