vireuikit 0.5.0 → 0.6.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 +56 -1
- package/dist/a11y-D3SS74I3.d.cts +25 -0
- package/dist/a11y-D3SS74I3.d.ts +25 -0
- package/dist/chunk-I3BD3XGC.js +392 -0
- package/dist/chunk-I3BD3XGC.js.map +1 -0
- package/dist/index.cjs +236 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -1
- package/dist/index.d.ts +119 -1
- package/dist/index.js +1 -186
- package/dist/index.js.map +1 -1
- package/dist/web.cjs +826 -0
- package/dist/web.cjs.map +1 -0
- package/dist/web.d.cts +27 -0
- package/dist/web.d.ts +27 -0
- package/dist/web.js +567 -0
- package/dist/web.js.map +1 -0
- package/package.json +18 -6
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ capsule-to-target growth morph.
|
|
|
12
12
|
npm install vireuikit vireglass
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
`vireglass` is a peer dependency — this package draws on its material model rather than
|
|
15
|
+
`vireglass` (3.x) is a peer dependency — this package draws on its material model rather than
|
|
16
16
|
shipping its own.
|
|
17
17
|
|
|
18
18
|
## Atom state and morph
|
|
@@ -43,6 +43,61 @@ const frame = growthFrame(sourceShape, targetShape, anchorPoint, growthPhase(sta
|
|
|
43
43
|
// frame.body: the shape to draw as glass this frame.
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
## Web elements
|
|
47
|
+
|
|
48
|
+
`vireuikit/web` upgrades native controls with custom elements. The control inside works before the
|
|
49
|
+
upgrade; afterwards the element drives its states from the model. Glass is opt-in: put `glass` on pieces
|
|
50
|
+
of the navigation layer, and leave it off for content.
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<vire-button><button>Play</button></vire-button>
|
|
54
|
+
<vire-icon-button glass><button aria-label="Share">…</button></vire-icon-button>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { defineVireElements, setA11yOverride } from 'vireuikit/web';
|
|
59
|
+
|
|
60
|
+
defineVireElements({ nonce: cspNonce });
|
|
61
|
+
setA11yOverride({ reduceTransparency: true });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- `<vire-button>` is an action: it sinks and grows slightly under press, and nothing opens out of it.
|
|
65
|
+
- `<vire-icon-button>` lifts into the glass under the finger; the lens response is visible while the
|
|
66
|
+
press is held.
|
|
67
|
+
- Attributes: `glass` (empty or `present`: a lens at rest; `touch`: a lens only under the finger; absent: no
|
|
68
|
+
glass), `size` (`touch`, `small`, `medium`, `large`, `xlarge`; by default `touch` on a coarse pointer and
|
|
69
|
+
`medium` otherwise), `disabled`.
|
|
70
|
+
- On a coarse pointer every size keeps a hit area of at least 44 dp without changing layout.
|
|
71
|
+
- The lens reads the backdrop from declared colours and cannot see an image, a video or a canvas. A host that knows
|
|
72
|
+
what lies under the control sets the element's `sample` (a `BackdropSample` from `vireglass`), at any time; `null`
|
|
73
|
+
hands it back to the probe.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { statsFromSamples } from 'vireglass/dom';
|
|
77
|
+
|
|
78
|
+
playButton.sample = statsFromSamples(coverColours); // [{ r, g, b }, …] in 0–255
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- The element dispatches `vire-glass-ready` once its lens is attached. If the lens cannot be loaded or attached,
|
|
82
|
+
the control stays a complete one on its plain (or, under reduced transparency, opaque frosted) surface, and the next
|
|
83
|
+
setting change tries again.
|
|
84
|
+
- Set `disabled` on the host element: it is mirrored onto the inner `<button>` and wins over it. Clearing it on
|
|
85
|
+
the host re-enables the button.
|
|
86
|
+
- Under `forced-colors` the elements keep the system colours and drop their own tints and blur.
|
|
87
|
+
- Accessibility follows the system `prefers-*` settings; `setA11yOverride` or the element's `a11y`
|
|
88
|
+
property overrides them. Reduced transparency keeps the lens and frosts it: a blurrier lens and a denser body, with the
|
|
89
|
+
ink following the body. Text takes the page's colour; the frosted fallback and the high-contrast bodies assume
|
|
90
|
+
the page's `color-scheme` matches it.
|
|
91
|
+
- The elements inject one stylesheet. Under a strict CSP pass `nonce` to `defineVireElements`, or ship the
|
|
92
|
+
exported `STYLES` string yourself.
|
|
93
|
+
- Importing the module touches no DOM, so it is safe on a server; `defineVireElements()` is the only call
|
|
94
|
+
that registers anything. An element defined before its markup is parsed waits for its button.
|
|
95
|
+
|
|
96
|
+
Sizes, behaviour state machines and accessibility modes are exported from `vireuikit` and shared by every
|
|
97
|
+
target. How an atom is accepted:
|
|
98
|
+
[design](https://github.com/pruffit/vireuikit/blob/main/docs/design.md),
|
|
99
|
+
[acceptance](https://github.com/pruffit/vireuikit/blob/main/docs/acceptance.md).
|
|
100
|
+
|
|
46
101
|
## License
|
|
47
102
|
|
|
48
103
|
Apache-2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type A11yMode = {
|
|
2
|
+
reduceTransparency: boolean;
|
|
3
|
+
increaseContrast: boolean;
|
|
4
|
+
reduceMotion: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const A11Y_NONE: A11yMode;
|
|
7
|
+
/** `undefined` follows the system; a boolean overrides it. */
|
|
8
|
+
type A11yOverride = {
|
|
9
|
+
[K in keyof A11yMode]?: boolean | undefined;
|
|
10
|
+
};
|
|
11
|
+
declare function resolveA11y(system: A11yMode, override?: A11yOverride): A11yMode;
|
|
12
|
+
/** `plain`: no glass was asked for. `frosted`: the stand-in when glass under reduced transparency cannot attach. */
|
|
13
|
+
type Surface = 'plain' | 'frosted' | 'glass';
|
|
14
|
+
type Presentation = {
|
|
15
|
+
glass: boolean;
|
|
16
|
+
/** Reduced transparency: the glass stays and frosts. */
|
|
17
|
+
frost: boolean;
|
|
18
|
+
surface: Surface;
|
|
19
|
+
contrastBorder: boolean;
|
|
20
|
+
instantSprings: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare function presentationFor(mode: A11yMode, wantsGlass: boolean): Presentation;
|
|
23
|
+
declare function sameA11y(a: A11yMode, b: A11yMode): boolean;
|
|
24
|
+
|
|
25
|
+
export { A11Y_NONE as A, type Presentation as P, type Surface as S, type A11yMode as a, type A11yOverride as b, presentationFor as p, resolveA11y as r, sameA11y as s };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type A11yMode = {
|
|
2
|
+
reduceTransparency: boolean;
|
|
3
|
+
increaseContrast: boolean;
|
|
4
|
+
reduceMotion: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const A11Y_NONE: A11yMode;
|
|
7
|
+
/** `undefined` follows the system; a boolean overrides it. */
|
|
8
|
+
type A11yOverride = {
|
|
9
|
+
[K in keyof A11yMode]?: boolean | undefined;
|
|
10
|
+
};
|
|
11
|
+
declare function resolveA11y(system: A11yMode, override?: A11yOverride): A11yMode;
|
|
12
|
+
/** `plain`: no glass was asked for. `frosted`: the stand-in when glass under reduced transparency cannot attach. */
|
|
13
|
+
type Surface = 'plain' | 'frosted' | 'glass';
|
|
14
|
+
type Presentation = {
|
|
15
|
+
glass: boolean;
|
|
16
|
+
/** Reduced transparency: the glass stays and frosts. */
|
|
17
|
+
frost: boolean;
|
|
18
|
+
surface: Surface;
|
|
19
|
+
contrastBorder: boolean;
|
|
20
|
+
instantSprings: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare function presentationFor(mode: A11yMode, wantsGlass: boolean): Presentation;
|
|
23
|
+
declare function sameA11y(a: A11yMode, b: A11yMode): boolean;
|
|
24
|
+
|
|
25
|
+
export { A11Y_NONE as A, type Presentation as P, type Surface as S, type A11yMode as a, type A11yOverride as b, presentationFor as p, resolveA11y as r, sameA11y as s };
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { activeMaterial, applyAccessibility, resolveOptics, halfMinDp, concentricRadius } from 'vireglass';
|
|
2
|
+
|
|
3
|
+
// src/kit/a11y.ts
|
|
4
|
+
var A11Y_NONE = {
|
|
5
|
+
reduceTransparency: false,
|
|
6
|
+
increaseContrast: false,
|
|
7
|
+
reduceMotion: false
|
|
8
|
+
};
|
|
9
|
+
function resolveA11y(system, override = {}) {
|
|
10
|
+
return {
|
|
11
|
+
reduceTransparency: override.reduceTransparency ?? system.reduceTransparency,
|
|
12
|
+
increaseContrast: override.increaseContrast ?? system.increaseContrast,
|
|
13
|
+
reduceMotion: override.reduceMotion ?? system.reduceMotion
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function presentationFor(mode, wantsGlass) {
|
|
17
|
+
return {
|
|
18
|
+
glass: wantsGlass,
|
|
19
|
+
frost: wantsGlass && mode.reduceTransparency,
|
|
20
|
+
surface: wantsGlass ? "glass" : "plain",
|
|
21
|
+
contrastBorder: mode.increaseContrast,
|
|
22
|
+
instantSprings: mode.reduceMotion
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function sameA11y(a, b) {
|
|
26
|
+
return a.reduceTransparency === b.reduceTransparency && a.increaseContrast === b.increaseContrast && a.reduceMotion === b.reduceMotion;
|
|
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 clamp = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
49
|
+
return Math.max(clamp(state.selected), clamp(state.pressed), clamp(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
|
+
|
|
82
|
+
// src/kit/behavior/focus.ts
|
|
83
|
+
function stepFocus(event) {
|
|
84
|
+
return event.type === "focus" && event.visible;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/kit/behavior/hover.ts
|
|
88
|
+
function stepHover(event, canHover) {
|
|
89
|
+
return canHover && event.type === "enter";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/kit/behavior/press.ts
|
|
93
|
+
var PRESS_IDLE = { by: null };
|
|
94
|
+
function stepPress(state, event, enabled) {
|
|
95
|
+
switch (event.type) {
|
|
96
|
+
case "down":
|
|
97
|
+
if (!enabled || state.by !== null) return { state, activated: false };
|
|
98
|
+
return { state: { by: event.source }, activated: false };
|
|
99
|
+
case "up":
|
|
100
|
+
if (state.by !== event.source) return { state, activated: false };
|
|
101
|
+
return { state: PRESS_IDLE, activated: enabled };
|
|
102
|
+
case "cancel":
|
|
103
|
+
return { state: PRESS_IDLE, activated: false };
|
|
104
|
+
case "leave":
|
|
105
|
+
return state.by === "pointer" ? { state: PRESS_IDLE, activated: false } : { state, activated: false };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/kit/behavior/control.ts
|
|
110
|
+
var TOUCH_INPUT = { hover: false };
|
|
111
|
+
var POINTER_INPUT = { hover: true };
|
|
112
|
+
var CONTROL_IDLE = {
|
|
113
|
+
press: PRESS_IDLE,
|
|
114
|
+
hovered: false,
|
|
115
|
+
focused: false,
|
|
116
|
+
disabled: false
|
|
117
|
+
};
|
|
118
|
+
function stepControl(state, event, caps) {
|
|
119
|
+
switch (event.type) {
|
|
120
|
+
case "down":
|
|
121
|
+
case "up":
|
|
122
|
+
case "cancel": {
|
|
123
|
+
const next = stepPress(state.press, event, !state.disabled);
|
|
124
|
+
return { state: { ...state, press: next.state }, activated: next.activated };
|
|
125
|
+
}
|
|
126
|
+
case "leave": {
|
|
127
|
+
const next = stepPress(state.press, event, !state.disabled);
|
|
128
|
+
return { state: { ...state, press: next.state, hovered: false }, activated: false };
|
|
129
|
+
}
|
|
130
|
+
case "enter":
|
|
131
|
+
return {
|
|
132
|
+
state: { ...state, hovered: !state.disabled && stepHover(event, caps.hover) },
|
|
133
|
+
activated: false
|
|
134
|
+
};
|
|
135
|
+
case "focus":
|
|
136
|
+
case "blur": {
|
|
137
|
+
const press = event.type === "blur" && state.press.by === "keyboard" ? PRESS_IDLE : state.press;
|
|
138
|
+
return {
|
|
139
|
+
state: { ...state, press, focused: !state.disabled && stepFocus(event) },
|
|
140
|
+
activated: false
|
|
141
|
+
};
|
|
142
|
+
}
|
|
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
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function controlTargets(state) {
|
|
151
|
+
return {
|
|
152
|
+
pressed: state.press.by !== null ? 1 : 0,
|
|
153
|
+
hovered: state.hovered ? 1 : 0,
|
|
154
|
+
focus: state.focused ? 1 : 0,
|
|
155
|
+
disabled: state.disabled
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function stepSpring(s, dt, response, damping, instant = false) {
|
|
159
|
+
if (instant) {
|
|
160
|
+
const moved = s.x !== s.target || s.v !== 0;
|
|
161
|
+
s.x = s.target;
|
|
162
|
+
s.v = 0;
|
|
163
|
+
return moved;
|
|
164
|
+
}
|
|
165
|
+
const stiffness = (2 * Math.PI / response) ** 2;
|
|
166
|
+
const friction = 4 * Math.PI * damping / response;
|
|
167
|
+
const h = 1 / 240;
|
|
168
|
+
for (let left = Math.min(dt, 0.05); left > 0; left -= h) {
|
|
169
|
+
const step = Math.min(left, h);
|
|
170
|
+
s.v += (stiffness * (s.target - s.x) - friction * s.v) * step;
|
|
171
|
+
s.x += s.v * step;
|
|
172
|
+
}
|
|
173
|
+
if (Math.abs(s.target - s.x) < 1e-3 && Math.abs(s.v) < 0.01) {
|
|
174
|
+
s.x = s.target;
|
|
175
|
+
s.v = 0;
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
var smoothstep = (e0, e1, x) => {
|
|
181
|
+
const t = Math.min(Math.max((x - e0) / (e1 - e0), 0), 1);
|
|
182
|
+
return t * t * (3 - 2 * t);
|
|
183
|
+
};
|
|
184
|
+
var mix = (a, b, t) => a + (b - a) * t;
|
|
185
|
+
var DROP_DIAMETER = 1.1;
|
|
186
|
+
var DROP_LIFT = 0.5;
|
|
187
|
+
var NECK_GAIN = 0.3;
|
|
188
|
+
var NECK_RAMP = 4;
|
|
189
|
+
var CORNER_EASE_START = 0.55;
|
|
190
|
+
var INK_OUT_END = 0.4;
|
|
191
|
+
var INK_IN_START = 0.45;
|
|
192
|
+
var INK_IN_END = 0.95;
|
|
193
|
+
function growthInk(phase) {
|
|
194
|
+
const a = Math.min(Math.max(phase.shrink, 0), 1);
|
|
195
|
+
const b = Math.max(phase.grow, 0);
|
|
196
|
+
return {
|
|
197
|
+
outgoing: 1 - smoothstep(0, INK_OUT_END, a),
|
|
198
|
+
incoming: smoothstep(INK_IN_START, INK_IN_END, b)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function largerOf(a, b) {
|
|
202
|
+
return halfMinDp(a.geometry) >= halfMinDp(b.geometry) ? { main: a, other: b } : { main: b, other: a };
|
|
203
|
+
}
|
|
204
|
+
function growthFrame(source, target, anchor, phase) {
|
|
205
|
+
const dx = target.center.x - source.center.x;
|
|
206
|
+
const dy = target.center.y - source.center.y;
|
|
207
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
208
|
+
const sourceSize = halfMinDp(source.geometry) * 2;
|
|
209
|
+
const lift = DROP_LIFT * sourceSize;
|
|
210
|
+
const drop = {
|
|
211
|
+
x: source.center.x + dx / len * lift,
|
|
212
|
+
y: source.center.y + dy / len * lift
|
|
213
|
+
};
|
|
214
|
+
const a = Math.min(Math.max(phase.shrink, 0), 1);
|
|
215
|
+
const b = Math.max(phase.grow, 0);
|
|
216
|
+
const keep = Math.max(1 - b, 0);
|
|
217
|
+
const d = DROP_DIAMETER * sourceSize;
|
|
218
|
+
const shrunkGeometry = {
|
|
219
|
+
width: mix(source.geometry.width, 0.6 * d, a) * keep,
|
|
220
|
+
height: mix(source.geometry.height, 0.6 * d, a) * keep,
|
|
221
|
+
cornerRadius: 0
|
|
222
|
+
};
|
|
223
|
+
shrunkGeometry.cornerRadius = Math.min(
|
|
224
|
+
mix(source.geometry.cornerRadius, halfMinDp(shrunkGeometry), a),
|
|
225
|
+
halfMinDp(shrunkGeometry)
|
|
226
|
+
);
|
|
227
|
+
const shrunk = {
|
|
228
|
+
geometry: shrunkGeometry,
|
|
229
|
+
center: { x: mix(source.center.x, drop.x, a), y: mix(source.center.y, drop.y, a) }
|
|
230
|
+
};
|
|
231
|
+
const seed = d * Math.sqrt(a);
|
|
232
|
+
const cornerX = Math.sign(anchor.x - source.center.x);
|
|
233
|
+
const cornerY = Math.sign(anchor.y - source.center.y);
|
|
234
|
+
const edgeX = mix(drop.x + cornerX * (seed / 2), anchor.x, Math.min(b, 1));
|
|
235
|
+
const edgeY = mix(drop.y + cornerY * (seed / 2), anchor.y, Math.min(b, 1));
|
|
236
|
+
const width = mix(seed, target.geometry.width, b);
|
|
237
|
+
const height = mix(seed, target.geometry.height, b);
|
|
238
|
+
const grownGeometry = { width, height, cornerRadius: 0 };
|
|
239
|
+
grownGeometry.cornerRadius = Math.min(
|
|
240
|
+
mix(halfMinDp(grownGeometry), target.geometry.cornerRadius, smoothstep(CORNER_EASE_START, 1, b)),
|
|
241
|
+
halfMinDp(grownGeometry)
|
|
242
|
+
);
|
|
243
|
+
const grown = {
|
|
244
|
+
geometry: grownGeometry,
|
|
245
|
+
center: { x: edgeX - cornerX * (width / 2), y: edgeY - cornerY * (height / 2) }
|
|
246
|
+
};
|
|
247
|
+
const { main, other } = largerOf(grown, shrunk);
|
|
248
|
+
const neck = NECK_GAIN * sourceSize * Math.min(a * NECK_RAMP, 1) * keep;
|
|
249
|
+
const united = neck > 0.01 && other.geometry.width > 0.5 && other.geometry.height > 0.5;
|
|
250
|
+
return {
|
|
251
|
+
body: main,
|
|
252
|
+
grown,
|
|
253
|
+
merge: united ? {
|
|
254
|
+
offsetX: other.center.x - main.center.x,
|
|
255
|
+
offsetY: other.center.y - main.center.y,
|
|
256
|
+
width: other.geometry.width,
|
|
257
|
+
height: other.geometry.height,
|
|
258
|
+
cornerRadius: other.geometry.cornerRadius,
|
|
259
|
+
smoothing: neck
|
|
260
|
+
} : void 0,
|
|
261
|
+
ink: growthInk(phase)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
var createGrowthState = () => ({
|
|
265
|
+
shrink: { x: 0, v: 0, target: 0 },
|
|
266
|
+
grow: { x: 0, v: 0, target: 0 }
|
|
267
|
+
});
|
|
268
|
+
var growthPhase = (state) => ({
|
|
269
|
+
shrink: state.shrink.x,
|
|
270
|
+
grow: state.grow.x
|
|
271
|
+
});
|
|
272
|
+
var DEFAULT_GROWTH_TIMING = {
|
|
273
|
+
shrinkOpen: 0.38,
|
|
274
|
+
shrinkClose: 0.3,
|
|
275
|
+
growOpen: 0.46,
|
|
276
|
+
growClose: 0.4,
|
|
277
|
+
growDampingOpen: 0.72
|
|
278
|
+
};
|
|
279
|
+
var GROW_DAMPING_CLOSE = 1;
|
|
280
|
+
var SHRINK_DAMPING = 1;
|
|
281
|
+
var GROW_STARTS_AT = 0.8;
|
|
282
|
+
var SHRINK_RETURNS_AT = 0.12;
|
|
283
|
+
function stepGrowth(state, dt, open, reduceMotion = false, timing = DEFAULT_GROWTH_TIMING) {
|
|
284
|
+
const { shrink, grow } = state;
|
|
285
|
+
if (reduceMotion) {
|
|
286
|
+
shrink.target = open ? 1 : 0;
|
|
287
|
+
grow.target = open ? 1 : 0;
|
|
288
|
+
} else if (open) {
|
|
289
|
+
shrink.target = 1;
|
|
290
|
+
if (shrink.x > GROW_STARTS_AT) grow.target = 1;
|
|
291
|
+
} else {
|
|
292
|
+
grow.target = 0;
|
|
293
|
+
if (grow.x < SHRINK_RETURNS_AT) shrink.target = 0;
|
|
294
|
+
}
|
|
295
|
+
const movedShrink = stepSpring(
|
|
296
|
+
shrink,
|
|
297
|
+
dt,
|
|
298
|
+
open ? timing.shrinkOpen : timing.shrinkClose,
|
|
299
|
+
SHRINK_DAMPING,
|
|
300
|
+
reduceMotion
|
|
301
|
+
);
|
|
302
|
+
const movedGrow = stepSpring(
|
|
303
|
+
grow,
|
|
304
|
+
dt,
|
|
305
|
+
open ? timing.growOpen : timing.growClose,
|
|
306
|
+
open ? timing.growDampingOpen : GROW_DAMPING_CLOSE,
|
|
307
|
+
reduceMotion
|
|
308
|
+
);
|
|
309
|
+
return movedShrink || movedGrow;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/kit/behavior/visual.ts
|
|
313
|
+
var createControlVisual = () => ({
|
|
314
|
+
pressed: { x: 0, v: 0, target: 0 },
|
|
315
|
+
hovered: { x: 0, v: 0, target: 0 },
|
|
316
|
+
focus: { x: 0, v: 0, target: 0 }
|
|
317
|
+
});
|
|
318
|
+
var PRESS = { response: 0.18, damping: 0.78 };
|
|
319
|
+
var HOVER = { response: 0.22, damping: 1 };
|
|
320
|
+
var FOCUS = { response: 0.16, damping: 1 };
|
|
321
|
+
function stepControlVisual(visual, dt, state, instant = false) {
|
|
322
|
+
const t = controlTargets(state);
|
|
323
|
+
visual.pressed.target = t.pressed;
|
|
324
|
+
visual.hovered.target = t.hovered;
|
|
325
|
+
visual.focus.target = t.focus;
|
|
326
|
+
const a = stepSpring(visual.pressed, dt, PRESS.response, PRESS.damping, instant);
|
|
327
|
+
const b = stepSpring(visual.hovered, dt, HOVER.response, HOVER.damping, instant);
|
|
328
|
+
const c = stepSpring(visual.focus, dt, FOCUS.response, FOCUS.damping, instant);
|
|
329
|
+
return a || b || c;
|
|
330
|
+
}
|
|
331
|
+
function visualAtomState(visual, state) {
|
|
332
|
+
return {
|
|
333
|
+
pressed: visual.pressed.x,
|
|
334
|
+
hovered: visual.hovered.x,
|
|
335
|
+
selected: 0,
|
|
336
|
+
disabled: state.disabled
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function settleControlVisual(visual, state) {
|
|
340
|
+
stepControlVisual(visual, 0, state, true);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/kit/response.ts
|
|
344
|
+
var LIFTING = { grow: 0.18, defocus: 1.6 };
|
|
345
|
+
var SINKING = { grow: 0.06, defocus: 1 };
|
|
346
|
+
function pressResponse(kind) {
|
|
347
|
+
return atomLift(kind) === 1 ? LIFTING : SINKING;
|
|
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
|
+
function nestedRadius(outerRadius, inset, minimum = 0) {
|
|
378
|
+
return concentricRadius(outerRadius, inset, minimum);
|
|
379
|
+
}
|
|
380
|
+
function defaultSize(input) {
|
|
381
|
+
return input.touch ? "touch" : "medium";
|
|
382
|
+
}
|
|
383
|
+
function hitSlop(bodyDp, minTargetDp) {
|
|
384
|
+
return bodyDp >= minTargetDp ? 0 : (minTargetDp - bodyDp) / 2;
|
|
385
|
+
}
|
|
386
|
+
function minTarget(input) {
|
|
387
|
+
return input.touch ? MIN_TOUCH_TARGET_DP : MIN_POINTER_TARGET_DP;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export { A11Y_NONE, ATOM_AT_REST, BUTTON_SIZES, CONTROL_IDLE, CONTROL_SIZES, DEFAULT_GROWTH_TIMING, MIN_POINTER_TARGET_DP, MIN_TOUCH_TARGET_DP, POINTER_INPUT, PRESS_IDLE, TOUCH_INPUT, atomAtRest, atomBody, atomLift, atomMaterial, controlTargets, createControlVisual, createGrowthState, defaultSize, growthFrame, growthInk, growthPhase, hitSlop, iconButtonSize, minTarget, nestedRadius, presentationFor, pressResponse, resolveA11y, sameA11y, settleControlVisual, smoothstep, stepControl, stepControlVisual, stepFocus, stepGrowth, stepHover, stepPress, stepSpring, visualAtomState };
|
|
391
|
+
//# sourceMappingURL=chunk-I3BD3XGC.js.map
|
|
392
|
+
//# sourceMappingURL=chunk-I3BD3XGC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/kit/a11y.ts","../src/kit/atom.ts","../src/kit/behavior/focus.ts","../src/kit/behavior/hover.ts","../src/kit/behavior/press.ts","../src/kit/behavior/control.ts","../src/kit/morph.ts","../src/kit/behavior/visual.ts","../src/kit/response.ts","../src/kit/sizes.ts"],"names":["mix"],"mappings":"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","file":"chunk-I3BD3XGC.js","sourcesContent":["export type A11yMode = {\n reduceTransparency: boolean;\n increaseContrast: boolean;\n reduceMotion: boolean;\n};\n\nexport const A11Y_NONE: A11yMode = {\n reduceTransparency: false,\n increaseContrast: false,\n reduceMotion: false,\n};\n\n/** `undefined` follows the system; a boolean overrides it. */\nexport type A11yOverride = { [K in keyof A11yMode]?: boolean | undefined };\n\nexport function resolveA11y(system: A11yMode, override: A11yOverride = {}): A11yMode {\n return {\n reduceTransparency: override.reduceTransparency ?? system.reduceTransparency,\n increaseContrast: override.increaseContrast ?? system.increaseContrast,\n reduceMotion: override.reduceMotion ?? system.reduceMotion,\n };\n}\n\n/** `plain`: no glass was asked for. `frosted`: the stand-in when glass under reduced transparency cannot attach. */\nexport type Surface = 'plain' | 'frosted' | 'glass';\n\nexport type Presentation = {\n glass: boolean;\n /** Reduced transparency: the glass stays and frosts. */\n frost: boolean;\n surface: Surface;\n contrastBorder: boolean;\n instantSprings: boolean;\n};\n\n// Reduced motion stops the springs, not the press response itself.\nexport function presentationFor(mode: A11yMode, wantsGlass: boolean): Presentation {\n return {\n glass: wantsGlass,\n frost: wantsGlass && mode.reduceTransparency,\n surface: wantsGlass ? 'glass' : 'plain',\n contrastBorder: mode.increaseContrast,\n instantSprings: mode.reduceMotion,\n };\n}\n\nexport function sameA11y(a: A11yMode, b: A11yMode): boolean {\n return (\n a.reduceTransparency === b.reduceTransparency &&\n a.increaseContrast === b.increaseContrast &&\n a.reduceMotion === b.reduceMotion\n );\n}\n","// Atom states — shared across every client: the set of states, what they mean, and what they do\n// to the material. Sizes, layout and per-input idioms are client-specific and live elsewhere.\nimport { activeMaterial, applyAccessibility, resolveOptics, type VireGlassMaterial } from 'vireglass';\n\nexport type VireUIKitAtomKind =\n | 'button'\n | 'iconButton'\n | 'switch'\n | 'slider'\n | 'segment'\n | 'chip'\n | 'field'\n | 'menu';\n\n/** Hover and focus don't exist everywhere: touch has neither. The atom accepts them, but has to\n * look complete at zero too — otherwise a touch client gets an unfinished look. */\nexport type VireUIKitAtomState = {\n pressed: number;\n hovered: number;\n selected: number;\n disabled: boolean;\n};\n\nexport const ATOM_AT_REST: VireUIKitAtomState = {\n pressed: 0,\n hovered: 0,\n selected: 0,\n disabled: false,\n};\n\n/**\n * Whether the piece lifts INTO the glass under press (1) or sinks into the backing (0).\n *\n * Lift isn't decoration: a small target has the finger covering it entirely, and an enlarged\n * transparent lens underneath is the only sign of a hit. A large target stays visible past the\n * finger, so it reads more naturally sinking toward the backing instead.\n */\nexport function atomLift(kind: VireUIKitAtomKind): number {\n switch (kind) {\n case 'iconButton':\n case 'switch':\n case 'slider':\n return 1;\n default:\n return 0;\n }\n}\n\n/** A disabled piece stays glass but stops asserting itself: the legibility requirement is lifted\n * and tint turns off. Hiding it via transparency won't do — the material is already transparent. */\nconst DISABLED_PRESENCE = 0.25;\n/** Hover is halfway to a press: the cursor is already on the target, but nothing is chosen yet. */\nconst HOVER_GAIN = 0.35;\n\n/** How far a state is from rest toward the pressed end, 0..1. */\nfunction activation(state: VireUIKitAtomState): number {\n if (state.disabled) return 0;\n const clamp = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n return Math.max(clamp(state.selected), clamp(state.pressed), clamp(state.hovered) * HOVER_GAIN);\n}\n\n/**\n * The atom's material for a given state. Press and selection go through `activeMaterial` — for\n * glass, state is a change in the MEDIUM itself (denser, thicker, clearer), not a highlight\n * drawn over it.\n */\nexport function atomMaterial(\n base: VireGlassMaterial,\n kind: VireUIKitAtomKind,\n state: VireUIKitAtomState = ATOM_AT_REST,\n): VireGlassMaterial {\n const out = activeMaterial(base, activation(state));\n if (!state.disabled) return out;\n return { ...out, presence: out.presence * DISABLED_PRESENCE };\n}\n\n/** Whether every field on the atom matches rest — lets a client skip redraws for states its own\n * input can never produce (hover on touch, focus on Android). */\nexport function atomAtRest(state: VireUIKitAtomState): boolean {\n return (\n state.pressed === 0 && state.hovered === 0 && state.selected === 0 && state.disabled === false\n );\n}\n\n/** The veil over the backdrop: the surroundings' colour, `white` of it swapped for white, lifted by `lift` in OKLab lightness. */\nexport type AtomBody = { alpha: number; white: number; lift: number };\n\nconst REST_BODY: AtomBody = { alpha: 0.25, white: 0.15, lift: 0.1 };\nconst PRESSED_BODY: AtomBody = { alpha: 0.34, white: 0.35, lift: 0 };\nconst FROST_LIFT = 0.26;\n\n/** The body for a state; reduced transparency raises it to the density `vireglass` asks of the glass in that mode. */\nexport function atomBody(\n base: VireGlassMaterial,\n state: VireUIKitAtomState = ATOM_AT_REST,\n frost = false,\n): AtomBody {\n const on = activation(state);\n const mix = (key: keyof AtomBody) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;\n let alpha = mix('alpha');\n let white = mix('white');\n let lift = mix('lift');\n if (state.disabled) alpha *= DISABLED_PRESENCE;\n if (frost) {\n const density = applyAccessibility(resolveOptics(base), {\n reduceTransparency: true,\n increaseContrast: false,\n reduceMotion: false,\n }).bodyDensity;\n alpha = Math.max(alpha, density);\n white = 0;\n lift = FROST_LIFT;\n }\n return { alpha, white, lift };\n}\n","export type FocusChange = { type: 'focus'; visible: boolean } | { type: 'blur' };\n\n// `visible` is the browser's keyboard-modality verdict, so a ring follows a key on any device, and a\n// click, which also focuses a button, shows none.\nexport function stepFocus(event: FocusChange): boolean {\n return event.type === 'focus' && event.visible;\n}\n","export type HoverChange = { type: 'enter' } | { type: 'leave' };\n\nexport function stepHover(event: HoverChange, canHover: boolean): boolean {\n return canHover && event.type === 'enter';\n}\n","export type PressSource = 'pointer' | 'keyboard';\n\nexport type PressState = { by: PressSource | null };\n\nexport type PressEvent =\n | { type: 'down'; source: PressSource }\n | { type: 'up'; source: PressSource }\n | { type: 'cancel' }\n | { type: 'leave' };\n\nexport const PRESS_IDLE: PressState = { by: null };\n\nexport type PressStep = {\n state: PressState;\n activated: boolean;\n};\n\nexport function stepPress(state: PressState, event: PressEvent, enabled: boolean): PressStep {\n switch (event.type) {\n case 'down':\n if (!enabled || state.by !== null) return { state, activated: false };\n return { state: { by: event.source }, activated: false };\n case 'up':\n // A key lifted mid-touch is not a tap.\n if (state.by !== event.source) return { state, activated: false };\n return { state: PRESS_IDLE, activated: enabled };\n case 'cancel':\n return { state: PRESS_IDLE, activated: false };\n case 'leave':\n // A key press has no position, so the pointer leaving cannot cancel it.\n return state.by === 'pointer' ? { state: PRESS_IDLE, activated: false } : { state, activated: false };\n }\n}\n","import { stepFocus, type FocusChange } from './focus';\nimport { stepHover, type HoverChange } from './hover';\nimport { PRESS_IDLE, stepPress, type PressEvent, type PressState } from './press';\n\nexport type InputCaps = { hover: boolean };\n\nexport const TOUCH_INPUT: InputCaps = { hover: false };\nexport const POINTER_INPUT: InputCaps = { hover: true };\n\nexport type ControlState = {\n press: PressState;\n hovered: boolean;\n focused: boolean;\n disabled: boolean;\n};\n\nexport const CONTROL_IDLE: ControlState = {\n press: PRESS_IDLE,\n hovered: false,\n focused: false,\n disabled: false,\n};\n\nexport type ControlEvent = PressEvent | HoverChange | FocusChange | { type: 'disable'; disabled: boolean };\n\nexport type ControlStep = { state: ControlState; activated: boolean };\n\nexport function stepControl(state: ControlState, event: ControlEvent, caps: InputCaps): ControlStep {\n switch (event.type) {\n case 'down':\n case 'up':\n case 'cancel': {\n const next = stepPress(state.press, event, !state.disabled);\n return { state: { ...state, press: next.state }, activated: next.activated };\n }\n case 'leave': {\n const next = stepPress(state.press, event, !state.disabled);\n return { state: { ...state, press: next.state, hovered: false }, activated: false };\n }\n case 'enter':\n return {\n state: { ...state, hovered: !state.disabled && stepHover(event, caps.hover) },\n activated: false,\n };\n case 'focus':\n case 'blur': {\n // Focus moving away takes the key-up with it, so a held key press must not outlive it.\n const press = event.type === 'blur' && state.press.by === 'keyboard' ? PRESS_IDLE : state.press;\n return {\n state: { ...state, press, focused: !state.disabled && stepFocus(event) },\n activated: false,\n };\n }\n case 'disable':\n return {\n state: event.disabled\n ? { press: PRESS_IDLE, hovered: false, focused: false, disabled: true }\n : { ...state, disabled: false },\n activated: false,\n };\n }\n}\n\nexport type ControlTargets = {\n pressed: number;\n hovered: number;\n focus: number;\n disabled: boolean;\n};\n\nexport function controlTargets(state: ControlState): ControlTargets {\n return {\n pressed: state.press.by !== null ? 1 : 0,\n hovered: state.hovered ? 1 : 0,\n focus: state.focused ? 1 : 0,\n disabled: state.disabled,\n };\n}\n","// Two-phase \"capsule → drop → target\" growth: atom behavior, not layout. The body contracts into\n// a drop, a neck grows from its side toward the target, the drop grows into the target with a\n// slight overshoot; closing retraces the same path in reverse, because the model holds no\n// direction, only the current phase. Source and target sizes are layout, supplied as parameters.\nimport { halfMinDp, type VireGlassGeometry, type VireGlassMorph } from 'vireglass';\n\n/** A spring with `response`-second settle time; `damping` < 1 overshoots. `instant` skips\n * integration entirely, for reduced motion. */\nexport type Spring = { x: number; v: number; target: number };\n\nexport function stepSpring(\n s: Spring,\n dt: number,\n response: number,\n damping: number,\n instant = false,\n): boolean {\n if (instant) {\n const moved = s.x !== s.target || s.v !== 0;\n s.x = s.target;\n s.v = 0;\n return moved;\n }\n const stiffness = ((2 * Math.PI) / response) ** 2;\n const friction = (4 * Math.PI * damping) / response;\n const h = 1 / 240;\n // Fixed step, but not a fixed frame budget: without this, a rare slow frame hands the spring a\n // huge dt and it diverges.\n for (let left = Math.min(dt, 0.05); left > 0; left -= h) {\n const step = Math.min(left, h);\n s.v += (stiffness * (s.target - s.x) - friction * s.v) * step;\n s.x += s.v * step;\n }\n if (Math.abs(s.target - s.x) < 1e-3 && Math.abs(s.v) < 1e-2) {\n s.x = s.target;\n s.v = 0;\n return false;\n }\n return true;\n}\n\nexport const smoothstep = (e0: number, e1: number, x: number): number => {\n const t = Math.min(Math.max((x - e0) / (e1 - e0), 0), 1);\n return t * t * (3 - 2 * t);\n};\n\nconst mix = (a: number, b: number, t: number) => a + (b - a) * t;\n\nexport type GrowthPoint = { x: number; y: number };\n\n/** A shape and its position — it has no role of its own; `growthFrame` assigns the role (body, or\n * what the body is merging into). */\nexport type GrowthShape = { geometry: VireGlassGeometry; center: GrowthPoint };\n\n/** `shrink` — capsule → drop, no overshoot. `grow` — drop → target; may exceed 1 for the\n * overshoot. Both are read as-is; `stepGrowth` owns the bounds and the handoff between them. */\nexport type GrowthPhase = { shrink: number; grow: number };\n\nexport type GrowthInk = {\n /** 0…1: how much of the source's ink is still visible (1) vs. already defocused into the drop (0). */\n outgoing: number;\n /** 0…1: how much of the target's ink has come into focus (0 = not yet, 1 = fully). */\n incoming: number;\n};\n\nexport type GrowthFrame = {\n /** The shape worth drawing as glass right now: the larger of the two, which supplies the\n * thickness and the bevel. */\n body: GrowthShape;\n /** The target shape as-is, regardless of role — it drives the response to finger/light while\n * the glass hasn't become it yet (while `body` is still the drop). */\n grown: GrowthShape;\n /** The second merge shape, ready for `toLensProps`/`toSurfaceUniforms`; `undefined` while the\n * neck hasn't grown yet or has already retracted. */\n merge: VireGlassMorph | undefined;\n ink: GrowthInk;\n};\n\n/** The drop's size — a fraction of the source's diameter, with headroom for the neck. */\nconst DROP_DIAMETER = 1.1;\n/** How far the drop travels from the source toward the target, as a fraction of the source's diameter. */\nconst DROP_LIFT = 0.5;\nconst NECK_GAIN = 0.3;\nconst NECK_RAMP = 4;\nconst CORNER_EASE_START = 0.55;\nconst INK_OUT_END = 0.4;\nconst INK_IN_START = 0.45;\nconst INK_IN_END = 0.95;\n\nexport function growthInk(phase: GrowthPhase): GrowthInk {\n const a = Math.min(Math.max(phase.shrink, 0), 1);\n const b = Math.max(phase.grow, 0);\n // Ink leaves and arrives by going out of focus, not by plain transparency — the blur amount is\n // the drawer's job, this only reports the fraction of focus.\n return {\n outgoing: 1 - smoothstep(0, INK_OUT_END, a),\n incoming: smoothstep(INK_IN_START, INK_IN_END, b),\n };\n}\n\n/** The main shape is the larger one by half-size: it supplies the thickness and the bevel, so the\n * roles swap exactly at equal half-sizes, which is invisible on screen. */\nfunction largerOf(a: GrowthShape, b: GrowthShape): { main: GrowthShape; other: GrowthShape } {\n return halfMinDp(a.geometry) >= halfMinDp(b.geometry) ? { main: a, other: b } : { main: b, other: a };\n}\n\n/** `anchor` is the corner shared by source and target: the body pins to it, otherwise the target\n * would grow out of the drop's center instead of the button's corner. */\nexport function growthFrame(\n source: GrowthShape,\n target: GrowthShape,\n anchor: GrowthPoint,\n phase: GrowthPhase,\n): GrowthFrame {\n const dx = target.center.x - source.center.x;\n const dy = target.center.y - source.center.y;\n const len = Math.hypot(dx, dy) || 1;\n const sourceSize = halfMinDp(source.geometry) * 2;\n const lift = DROP_LIFT * sourceSize;\n const drop: GrowthPoint = {\n x: source.center.x + (dx / len) * lift,\n y: source.center.y + (dy / len) * lift,\n };\n\n const a = Math.min(Math.max(phase.shrink, 0), 1);\n const b = Math.max(phase.grow, 0);\n const keep = Math.max(1 - b, 0);\n const d = DROP_DIAMETER * sourceSize;\n\n const shrunkGeometry: VireGlassGeometry = {\n width: mix(source.geometry.width, 0.6 * d, a) * keep,\n height: mix(source.geometry.height, 0.6 * d, a) * keep,\n cornerRadius: 0,\n };\n // The source's radius EASES into the drop's rather than being replaced by it outright —\n // otherwise a field with a partial corner radius at rest would render as a full pill.\n shrunkGeometry.cornerRadius = Math.min(\n mix(source.geometry.cornerRadius, halfMinDp(shrunkGeometry), a),\n halfMinDp(shrunkGeometry),\n );\n const shrunk: GrowthShape = {\n geometry: shrunkGeometry,\n center: { x: mix(source.center.x, drop.x, a), y: mix(source.center.y, drop.y, a) },\n };\n\n const seed = d * Math.sqrt(a);\n // The sign of the corner the body pins to. Zero means the anchor sits on the same axis as the\n // source's center, so growth along that axis centers instead of hugging an edge.\n const cornerX = Math.sign(anchor.x - source.center.x);\n const cornerY = Math.sign(anchor.y - source.center.y);\n const edgeX = mix(drop.x + cornerX * (seed / 2), anchor.x, Math.min(b, 1));\n const edgeY = mix(drop.y + cornerY * (seed / 2), anchor.y, Math.min(b, 1));\n const width = mix(seed, target.geometry.width, b);\n const height = mix(seed, target.geometry.height, b);\n const grownGeometry: VireGlassGeometry = { width, height, cornerRadius: 0 };\n grownGeometry.cornerRadius = Math.min(\n mix(halfMinDp(grownGeometry), target.geometry.cornerRadius, smoothstep(CORNER_EASE_START, 1, b)),\n halfMinDp(grownGeometry),\n );\n const grown: GrowthShape = {\n geometry: grownGeometry,\n center: { x: edgeX - cornerX * (width / 2), y: edgeY - cornerY * (height / 2) },\n };\n\n const { main, other } = largerOf(grown, shrunk);\n const neck = NECK_GAIN * sourceSize * Math.min(a * NECK_RAMP, 1) * keep;\n const united = neck > 0.01 && other.geometry.width > 0.5 && other.geometry.height > 0.5;\n\n return {\n body: main,\n grown,\n merge: united\n ? {\n offsetX: other.center.x - main.center.x,\n offsetY: other.center.y - main.center.y,\n width: other.geometry.width,\n height: other.geometry.height,\n cornerRadius: other.geometry.cornerRadius,\n smoothing: neck,\n }\n : undefined,\n ink: growthInk(phase),\n };\n}\n\nexport type GrowthState = { shrink: Spring; grow: Spring };\n\nexport const createGrowthState = (): GrowthState => ({\n shrink: { x: 0, v: 0, target: 0 },\n grow: { x: 0, v: 0, target: 0 },\n});\n\nexport const growthPhase = (state: GrowthState): GrowthPhase => ({\n shrink: state.shrink.x,\n grow: state.grow.x,\n});\n\n/** Durations are client-specific: this set is tuned for the capsule-to-menu transition, and an\n * atom at a different scale (an icon opening into a sheet) isn't obligated to match it. The\n * shape of the transition is shared regardless. */\nexport type GrowthTiming = {\n shrinkOpen: number;\n shrinkClose: number;\n growOpen: number;\n growClose: number;\n /** < 1 gives the target a slight overshoot on open; closing never overshoots. */\n growDampingOpen: number;\n};\n\nexport const DEFAULT_GROWTH_TIMING: GrowthTiming = {\n shrinkOpen: 0.38,\n shrinkClose: 0.3,\n growOpen: 0.46,\n growClose: 0.4,\n growDampingOpen: 0.72,\n};\n\nconst GROW_DAMPING_CLOSE = 1;\nconst SHRINK_DAMPING = 1;\nconst GROW_STARTS_AT = 0.8;\nconst SHRINK_RETURNS_AT = 0.12;\n\n/** Growth starts once the drop has gathered, and the reverse on close. `reduceMotion` turns off\n * the spring entirely: both phases jump straight to their target in one step, with no overshoot\n * and no defocus phase. */\nexport function stepGrowth(\n state: GrowthState,\n dt: number,\n open: boolean,\n reduceMotion = false,\n timing: GrowthTiming = DEFAULT_GROWTH_TIMING,\n): boolean {\n const { shrink, grow } = state;\n if (reduceMotion) {\n shrink.target = open ? 1 : 0;\n grow.target = open ? 1 : 0;\n } else if (open) {\n shrink.target = 1;\n if (shrink.x > GROW_STARTS_AT) grow.target = 1;\n } else {\n grow.target = 0;\n if (grow.x < SHRINK_RETURNS_AT) shrink.target = 0;\n }\n const movedShrink = stepSpring(\n shrink,\n dt,\n open ? timing.shrinkOpen : timing.shrinkClose,\n SHRINK_DAMPING,\n reduceMotion,\n );\n const movedGrow = stepSpring(\n grow,\n dt,\n open ? timing.growOpen : timing.growClose,\n open ? timing.growDampingOpen : GROW_DAMPING_CLOSE,\n reduceMotion,\n );\n return movedShrink || movedGrow;\n}\n","import type { VireUIKitAtomState } from '../atom';\nimport { stepSpring, type Spring } from '../morph';\nimport { controlTargets, type ControlState } from './control';\n\nexport type ControlVisual = {\n pressed: Spring;\n hovered: Spring;\n focus: Spring;\n};\n\nexport const createControlVisual = (): ControlVisual => ({\n pressed: { x: 0, v: 0, target: 0 },\n hovered: { x: 0, v: 0, target: 0 },\n focus: { x: 0, v: 0, target: 0 },\n});\n\n// Press attacks fast and settles with a little give; hover and focus just ease.\nconst PRESS = { response: 0.18, damping: 0.78 };\nconst HOVER = { response: 0.22, damping: 1 };\nconst FOCUS = { response: 0.16, damping: 1 };\n\nexport function stepControlVisual(\n visual: ControlVisual,\n dt: number,\n state: ControlState,\n instant = false,\n): boolean {\n const t = controlTargets(state);\n visual.pressed.target = t.pressed;\n visual.hovered.target = t.hovered;\n visual.focus.target = t.focus;\n const a = stepSpring(visual.pressed, dt, PRESS.response, PRESS.damping, instant);\n const b = stepSpring(visual.hovered, dt, HOVER.response, HOVER.damping, instant);\n const c = stepSpring(visual.focus, dt, FOCUS.response, FOCUS.damping, instant);\n return a || b || c;\n}\n\n// The press may overshoot 1; `atomMaterial` clamps, so the raw value is passed through.\nexport function visualAtomState(visual: ControlVisual, state: ControlState): VireUIKitAtomState {\n return {\n pressed: visual.pressed.x,\n hovered: visual.hovered.x,\n selected: 0,\n disabled: state.disabled,\n };\n}\n\nexport function settleControlVisual(visual: ControlVisual, state: ControlState): void {\n stepControlVisual(visual, 0, state, true);\n}\n","import { atomLift, type VireUIKitAtomKind } from './atom';\n\nexport type PressResponse = {\n /** Fraction of its own size the body adds at full press. */\n grow: number;\n /** Glyph blur at full press, px. */\n defocus: number;\n};\n\n// A lifted piece grows more: the finger covers it, so the enlarged lens is the only sign of a hit.\nconst LIFTING: PressResponse = { grow: 0.18, defocus: 1.6 };\nconst SINKING: PressResponse = { grow: 0.06, defocus: 1 };\n\nexport function pressResponse(kind: VireUIKitAtomKind): PressResponse {\n return atomLift(kind) === 1 ? LIFTING : SINKING;\n}\n","import { concentricRadius } from 'vireglass';\n\nexport type ControlSize = 'touch' | 'small' | 'medium' | 'large' | 'xlarge';\n\nexport const CONTROL_SIZES: readonly ControlSize[] = ['touch', 'small', 'medium', 'large', 'xlarge'];\n\nexport type SizeSpec = {\n /** Body height; an icon button is a square of this side. */\n height: number;\n paddingX: number;\n icon: number;\n radius: number;\n shape: 'capsule' | 'rounded';\n};\n\nexport const MIN_TOUCH_TARGET_DP = 44;\nexport const MIN_POINTER_TARGET_DP = 24;\n\nconst capsule = (height: number, paddingX: number): SizeSpec => ({\n height,\n paddingX,\n icon: Math.round(height / 2),\n radius: height / 2,\n shape: 'capsule',\n});\n\nconst rounded = (height: number, paddingX: number): SizeSpec => ({\n height,\n paddingX,\n icon: Math.round(height / 2),\n radius: height / 4,\n shape: 'rounded',\n});\n\n// Where each number comes from: docs/acceptance.md.\nexport const BUTTON_SIZES: Readonly<Record<ControlSize, SizeSpec>> = {\n touch: capsule(44, 20),\n small: rounded(24, 10),\n medium: rounded(32, 14),\n large: capsule(40, 18),\n xlarge: capsule(48, 22),\n};\n\nexport function iconButtonSize(size: ControlSize): SizeSpec & { width: number } {\n const spec = BUTTON_SIZES[size];\n return { ...spec, width: spec.height, paddingX: 0 };\n}\n\nexport function nestedRadius(outerRadius: number, inset: number, minimum = 0): number {\n return concentricRadius(outerRadius, inset, minimum);\n}\n\nexport function defaultSize(input: { touch: boolean }): ControlSize {\n return input.touch ? 'touch' : 'medium';\n}\n\nexport function hitSlop(bodyDp: number, minTargetDp: number): number {\n return bodyDp >= minTargetDp ? 0 : (minTargetDp - bodyDp) / 2;\n}\n\nexport function minTarget(input: { touch: boolean }): number {\n return input.touch ? MIN_TOUCH_TARGET_DP : MIN_POINTER_TARGET_DP;\n}\n"]}
|