@ambientcss/components 2.0.1 → 3.0.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 +35 -0
- package/dist/index.cjs +1563 -384
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +763 -48
- package/dist/index.d.ts +763 -48
- package/dist/index.js +1524 -385
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1102 -256
- package/package.json +2 -2
- package/src/components/AmbientButton.tsx +25 -25
- package/src/components/AmbientFader.tsx +33 -115
- package/src/components/AmbientKnob.tsx +64 -222
- package/src/components/AmbientPanel.tsx +8 -2
- package/src/components/AmbientProvider.tsx +3 -0
- package/src/components/AmbientRack.tsx +32 -0
- package/src/components/AmbientSelect.tsx +40 -0
- package/src/components/AmbientSlider.tsx +32 -113
- package/src/components/AmbientSwitch.tsx +58 -58
- package/src/controls/AmbientBank.tsx +138 -0
- package/src/controls/AmbientLatch.tsx +78 -0
- package/src/controls/AmbientPress.tsx +74 -0
- package/src/controls/AmbientRotary.tsx +94 -0
- package/src/controls/AmbientTravel.tsx +83 -0
- package/src/core/context.tsx +46 -0
- package/src/core/controllable.ts +33 -0
- package/src/core/dev.ts +15 -0
- package/src/core/frames.tsx +63 -0
- package/src/core/kit.tsx +107 -0
- package/src/core/material.ts +27 -0
- package/src/core/numeric.ts +88 -0
- package/src/core/types.ts +116 -0
- package/src/core/useBank.ts +163 -0
- package/src/core/useLatch.ts +54 -0
- package/src/core/usePress.ts +120 -0
- package/src/core/useRotary.ts +253 -0
- package/src/core/useTravel.ts +141 -0
- package/src/index.ts +127 -4
- package/src/kits/console.tsx +80 -0
- package/src/kits/grounded.tsx +113 -0
- package/src/parts/bank.tsx +32 -0
- package/src/parts/console.tsx +99 -0
- package/src/parts/knob.tsx +203 -0
- package/src/parts/latch.tsx +33 -0
- package/src/parts/press.tsx +56 -0
- package/src/parts/travel.tsx +70 -0
- package/src/styles.css +1102 -256
package/dist/index.cjs
CHANGED
|
@@ -20,13 +20,53 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AmbientBank: () => AmbientBank,
|
|
23
24
|
AmbientButton: () => AmbientButton,
|
|
24
25
|
AmbientFader: () => AmbientFader,
|
|
26
|
+
AmbientKitProvider: () => AmbientKitProvider,
|
|
25
27
|
AmbientKnob: () => AmbientKnob,
|
|
28
|
+
AmbientLatch: () => AmbientLatch,
|
|
26
29
|
AmbientPanel: () => AmbientPanel,
|
|
30
|
+
AmbientPress: () => AmbientPress,
|
|
27
31
|
AmbientProvider: () => AmbientProvider,
|
|
32
|
+
AmbientRack: () => AmbientRack,
|
|
33
|
+
AmbientRotary: () => AmbientRotary,
|
|
34
|
+
AmbientSelect: () => AmbientSelect,
|
|
28
35
|
AmbientSlider: () => AmbientSlider,
|
|
29
|
-
AmbientSwitch: () => AmbientSwitch
|
|
36
|
+
AmbientSwitch: () => AmbientSwitch,
|
|
37
|
+
AmbientTravel: () => AmbientTravel,
|
|
38
|
+
ButtonCap: () => ButtonCap,
|
|
39
|
+
ConsoleBar: () => ConsoleBar,
|
|
40
|
+
ConsoleKnob: () => ConsoleKnob,
|
|
41
|
+
ConsoleMarks: () => ConsoleMarks,
|
|
42
|
+
ConsoleToggle: () => ConsoleToggle,
|
|
43
|
+
ConsoleWell: () => ConsoleWell,
|
|
44
|
+
FaderCap: () => FaderCap,
|
|
45
|
+
IndicatorBar: () => IndicatorBar,
|
|
46
|
+
IndicatorDot: () => IndicatorDot,
|
|
47
|
+
KeyCap: () => KeyCap,
|
|
48
|
+
KeyLens: () => KeyLens,
|
|
49
|
+
KnobBody: () => KnobBody,
|
|
50
|
+
KnurledFace: () => KnurledFace,
|
|
51
|
+
Led: () => Led,
|
|
52
|
+
ScaleRing: () => ScaleRing,
|
|
53
|
+
SliderThumb: () => SliderThumb,
|
|
54
|
+
SwitchPill: () => SwitchPill,
|
|
55
|
+
SwitchTrack: () => SwitchTrack,
|
|
56
|
+
ToggleThumb: () => ToggleThumb,
|
|
57
|
+
ToggleTrack: () => ToggleTrack,
|
|
58
|
+
TravelTrack: () => TravelTrack,
|
|
59
|
+
consoleKit: () => consoleKit,
|
|
60
|
+
groundedKit: () => groundedKit,
|
|
61
|
+
useBank: () => useBank,
|
|
62
|
+
useBankKey: () => useBankKey,
|
|
63
|
+
useControlState: () => useControlState,
|
|
64
|
+
useDress: () => useDress,
|
|
65
|
+
useKit: () => useKit,
|
|
66
|
+
useLatch: () => useLatch,
|
|
67
|
+
usePress: () => usePress,
|
|
68
|
+
useRotary: () => useRotary,
|
|
69
|
+
useTravel: () => useTravel
|
|
30
70
|
});
|
|
31
71
|
module.exports = __toCommonJS(index_exports);
|
|
32
72
|
|
|
@@ -74,7 +114,7 @@ function AmbientPanel({ className, material = "matte", ...props }) {
|
|
|
74
114
|
{
|
|
75
115
|
className: cn(
|
|
76
116
|
"ambient amb-surface amb-chamfer amb-elevation-2 ambx-panel",
|
|
77
|
-
|
|
117
|
+
`amb-mat-${material}`,
|
|
78
118
|
className
|
|
79
119
|
),
|
|
80
120
|
...props
|
|
@@ -82,492 +122,1631 @@ function AmbientPanel({ className, material = "matte", ...props }) {
|
|
|
82
122
|
);
|
|
83
123
|
}
|
|
84
124
|
|
|
85
|
-
// src/components/
|
|
125
|
+
// src/components/AmbientRack.tsx
|
|
86
126
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
87
|
-
function
|
|
127
|
+
function AmbientRack({
|
|
88
128
|
className,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
shape = "pill",
|
|
129
|
+
gap = "normal",
|
|
130
|
+
direction = "row",
|
|
92
131
|
...props
|
|
93
132
|
}) {
|
|
94
133
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
95
|
-
"
|
|
134
|
+
"div",
|
|
96
135
|
{
|
|
97
|
-
type: "button",
|
|
98
136
|
className: cn(
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
137
|
+
"ambx-rack",
|
|
138
|
+
gap === "tight" && "ambx-rack-tight",
|
|
139
|
+
gap === "loose" && "ambx-rack-loose",
|
|
140
|
+
direction === "column" && "ambx-rack-column",
|
|
102
141
|
className
|
|
103
142
|
),
|
|
104
|
-
...props
|
|
105
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
106
|
-
"span",
|
|
107
|
-
{
|
|
108
|
-
className: cn(
|
|
109
|
-
"amb-button-cap ambient amb-chamfer amb-surface amb-heading-3",
|
|
110
|
-
material === "matte" ? "amb-mat-matte" : material === "shiny" ? "amb-mat-shiny" : "amb-mat-glass"
|
|
111
|
-
),
|
|
112
|
-
children
|
|
113
|
-
}
|
|
114
|
-
)
|
|
143
|
+
...props
|
|
115
144
|
}
|
|
116
145
|
);
|
|
117
146
|
}
|
|
118
147
|
|
|
119
|
-
// src/
|
|
148
|
+
// src/controls/AmbientPress.tsx
|
|
149
|
+
var import_react5 = require("react");
|
|
150
|
+
|
|
151
|
+
// src/core/context.tsx
|
|
120
152
|
var import_react = require("react");
|
|
153
|
+
var ControlStateContext = (0, import_react.createContext)(null);
|
|
154
|
+
var ControlStateProvider = ControlStateContext.Provider;
|
|
155
|
+
function useControlState() {
|
|
156
|
+
const state = (0, import_react.useContext)(ControlStateContext);
|
|
157
|
+
if (!state) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
"useControlState() must be called from inside a control's parts. Pass the component through `parts` on AmbientRotary, AmbientTravel, AmbientPress, AmbientLatch or AmbientBank."
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
var BankKeyContext = (0, import_react.createContext)(null);
|
|
165
|
+
var BankKeyProvider = BankKeyContext.Provider;
|
|
166
|
+
function useBankKey() {
|
|
167
|
+
const key = (0, import_react.useContext)(BankKeyContext);
|
|
168
|
+
if (!key) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
"useBankKey() must be called from inside a bank key's parts. Pass the component through `keyParts` on AmbientBank."
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return key;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/core/frames.tsx
|
|
177
|
+
var import_react2 = require("react");
|
|
178
|
+
|
|
179
|
+
// src/core/dev.ts
|
|
180
|
+
var isDev = (() => {
|
|
181
|
+
try {
|
|
182
|
+
return process.env.NODE_ENV !== "production";
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
})();
|
|
187
|
+
|
|
188
|
+
// src/core/types.ts
|
|
189
|
+
var FRAME_ORDER = ["panel", "base", "actuator", "fixture"];
|
|
190
|
+
function stateStyle(state) {
|
|
191
|
+
return {
|
|
192
|
+
"--ambx-value": state.value,
|
|
193
|
+
"--ambx-percent": state.percent,
|
|
194
|
+
"--ambx-angle": `${state.angle}deg`,
|
|
195
|
+
"--ambx-travel-start": `${state.travelStart}deg`,
|
|
196
|
+
"--ambx-travel-sweep": `${state.travelSweep}deg`,
|
|
197
|
+
"--ambx-detents": state.detents
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function stateData(state) {
|
|
201
|
+
return {
|
|
202
|
+
"data-dragging": state.dragging ? "" : void 0,
|
|
203
|
+
"data-disabled": state.disabled ? "" : void 0,
|
|
204
|
+
"data-at-min": state.atMin ? "" : void 0,
|
|
205
|
+
"data-at-max": state.atMax ? "" : void 0
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function sizeProps(family, size) {
|
|
209
|
+
if (size === void 0) return {};
|
|
210
|
+
if (size === "sm" || size === "md" || size === "lg") {
|
|
211
|
+
return { className: `ambx-${family}-${size}` };
|
|
212
|
+
}
|
|
213
|
+
return { style: { "--ambx-size": size } };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/core/frames.tsx
|
|
121
217
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
122
|
-
function
|
|
218
|
+
function Frames({ parts }) {
|
|
219
|
+
if (!parts) return null;
|
|
220
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: FRAME_ORDER.map(
|
|
221
|
+
(name) => parts[name] == null ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-frame": name, className: `ambx-frame ambx-frame-${name}`, children: parts[name] }, name)
|
|
222
|
+
) });
|
|
223
|
+
}
|
|
224
|
+
var FOCUSABLE = "a[href], button, input, select, textarea, [tabindex], [contenteditable=true],[role=button], [role=checkbox], [role=radio], [role=slider], [role=switch], [role=link]";
|
|
225
|
+
function useDevPartCheck(ref, control) {
|
|
226
|
+
const warned2 = (0, import_react2.useRef)(false);
|
|
227
|
+
(0, import_react2.useEffect)(() => {
|
|
228
|
+
if (!isDev || warned2.current) return;
|
|
229
|
+
const root = ref.current;
|
|
230
|
+
if (!root) return;
|
|
231
|
+
const offenders = root.querySelectorAll(`[data-frame] :is(${FOCUSABLE})`);
|
|
232
|
+
if (offenders.length === 0) return;
|
|
233
|
+
warned2.current = true;
|
|
234
|
+
const tags = Array.from(offenders, (node) => `<${node.tagName.toLowerCase()}>`).join(", ");
|
|
235
|
+
console.warn(
|
|
236
|
+
`[@ambientcss/components] ${control}: a part contains a focusable element (${tags}). Parts are presentational \u2014 the control root already owns the role, the tab stop and the keyboard handler, so this creates a second tab stop and usually a conflicting role.`
|
|
237
|
+
);
|
|
238
|
+
}, [ref, control]);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/core/usePress.ts
|
|
242
|
+
var import_react4 = require("react");
|
|
243
|
+
|
|
244
|
+
// src/core/controllable.ts
|
|
245
|
+
var import_react3 = require("react");
|
|
246
|
+
function useControllableValue(controlled, defaultValue, onChange) {
|
|
247
|
+
const isControlled = controlled !== void 0;
|
|
248
|
+
const [internal, setInternal] = (0, import_react3.useState)(defaultValue);
|
|
249
|
+
const onChangeRef = (0, import_react3.useRef)(onChange);
|
|
250
|
+
onChangeRef.current = onChange;
|
|
251
|
+
const controlledRef = (0, import_react3.useRef)(isControlled);
|
|
252
|
+
controlledRef.current = isControlled;
|
|
253
|
+
const set = (0, import_react3.useCallback)((next) => {
|
|
254
|
+
if (!controlledRef.current) setInternal(next);
|
|
255
|
+
onChangeRef.current?.(next);
|
|
256
|
+
}, []);
|
|
257
|
+
return [isControlled ? controlled : internal, set];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/core/usePress.ts
|
|
261
|
+
function usePress(options) {
|
|
262
|
+
const {
|
|
263
|
+
mode = "momentary",
|
|
264
|
+
repeatDelay = 400,
|
|
265
|
+
repeatInterval = 60,
|
|
266
|
+
disabled = false
|
|
267
|
+
} = options;
|
|
268
|
+
const [on, setOn] = useControllableValue(
|
|
269
|
+
options.value,
|
|
270
|
+
options.defaultValue ?? false,
|
|
271
|
+
options.onChange
|
|
272
|
+
);
|
|
273
|
+
const [held, setHeld] = (0, import_react4.useState)(false);
|
|
274
|
+
const onPressRef = (0, import_react4.useRef)(options.onPress);
|
|
275
|
+
onPressRef.current = options.onPress;
|
|
276
|
+
const timers = (0, import_react4.useRef)(
|
|
277
|
+
{}
|
|
278
|
+
);
|
|
279
|
+
const stopRepeat = (0, import_react4.useCallback)(() => {
|
|
280
|
+
if (timers.current.delay) clearTimeout(timers.current.delay);
|
|
281
|
+
if (timers.current.tick) clearInterval(timers.current.tick);
|
|
282
|
+
timers.current = {};
|
|
283
|
+
}, []);
|
|
284
|
+
(0, import_react4.useEffect)(() => stopRepeat, [stopRepeat]);
|
|
285
|
+
const activate = (0, import_react4.useCallback)(() => {
|
|
286
|
+
if (mode === "toggle") setOn(!on);
|
|
287
|
+
onPressRef.current?.();
|
|
288
|
+
}, [mode, on, setOn]);
|
|
289
|
+
const pressed = mode === "toggle" ? on : held;
|
|
290
|
+
const state = (0, import_react4.useMemo)(
|
|
291
|
+
() => ({
|
|
292
|
+
value: pressed ? 1 : 0,
|
|
293
|
+
min: 0,
|
|
294
|
+
max: 1,
|
|
295
|
+
percent: pressed ? 1 : 0,
|
|
296
|
+
angle: 0,
|
|
297
|
+
travelStart: 0,
|
|
298
|
+
travelSweep: 0,
|
|
299
|
+
detents: 2,
|
|
300
|
+
dragging: held,
|
|
301
|
+
disabled,
|
|
302
|
+
atMin: !pressed,
|
|
303
|
+
atMax: pressed
|
|
304
|
+
}),
|
|
305
|
+
[pressed, held, disabled]
|
|
306
|
+
);
|
|
307
|
+
const rootProps = {
|
|
308
|
+
type: "button",
|
|
309
|
+
disabled,
|
|
310
|
+
"aria-pressed": mode === "toggle" ? on : void 0,
|
|
311
|
+
style: stateStyle(state),
|
|
312
|
+
"data-pressed": pressed ? "" : void 0,
|
|
313
|
+
"data-mode": mode,
|
|
314
|
+
...stateData(state),
|
|
315
|
+
onPointerDown: (event) => {
|
|
316
|
+
if (disabled || event.button !== 0) return;
|
|
317
|
+
setHeld(true);
|
|
318
|
+
if (mode !== "repeat") return;
|
|
319
|
+
onPressRef.current?.();
|
|
320
|
+
timers.current.delay = setTimeout(() => {
|
|
321
|
+
timers.current.tick = setInterval(() => onPressRef.current?.(), repeatInterval);
|
|
322
|
+
}, repeatDelay);
|
|
323
|
+
},
|
|
324
|
+
onPointerUp: () => {
|
|
325
|
+
setHeld(false);
|
|
326
|
+
stopRepeat();
|
|
327
|
+
},
|
|
328
|
+
onPointerCancel: () => {
|
|
329
|
+
setHeld(false);
|
|
330
|
+
stopRepeat();
|
|
331
|
+
},
|
|
332
|
+
onPointerLeave: () => {
|
|
333
|
+
setHeld(false);
|
|
334
|
+
stopRepeat();
|
|
335
|
+
},
|
|
336
|
+
onClick: () => {
|
|
337
|
+
if (mode !== "repeat") activate();
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
return { state, rootProps, pressed, on, setOn };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/controls/AmbientPress.tsx
|
|
344
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
345
|
+
function AmbientPress({
|
|
346
|
+
parts,
|
|
347
|
+
size,
|
|
123
348
|
className,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
349
|
+
mode,
|
|
350
|
+
value,
|
|
351
|
+
defaultValue,
|
|
352
|
+
onChange,
|
|
353
|
+
onPress,
|
|
354
|
+
repeatDelay,
|
|
355
|
+
repeatInterval,
|
|
356
|
+
disabled,
|
|
131
357
|
children,
|
|
132
|
-
...
|
|
358
|
+
...rest
|
|
133
359
|
}) {
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
360
|
+
const ref = (0, import_react5.useRef)(null);
|
|
361
|
+
const { state, rootProps } = usePress({
|
|
362
|
+
mode,
|
|
363
|
+
value,
|
|
364
|
+
defaultValue,
|
|
365
|
+
onChange,
|
|
366
|
+
onPress,
|
|
367
|
+
repeatDelay,
|
|
368
|
+
repeatInterval,
|
|
369
|
+
disabled
|
|
370
|
+
});
|
|
371
|
+
useDevPartCheck(ref, "AmbientPress");
|
|
372
|
+
const sized = sizeProps("press", size);
|
|
373
|
+
const { style: restStyle, onClick, ...restProps } = rest;
|
|
374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
139
375
|
"button",
|
|
140
376
|
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
377
|
+
...restProps,
|
|
378
|
+
...rootProps,
|
|
379
|
+
ref,
|
|
380
|
+
className: cn("ambx-control ambx-press", sized.className, className),
|
|
381
|
+
style: { ...rootProps.style, ...sized.style, ...restStyle },
|
|
382
|
+
onClick: (event) => {
|
|
383
|
+
rootProps.onClick();
|
|
384
|
+
onClick?.(event);
|
|
385
|
+
},
|
|
386
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ControlStateProvider, { value: state, children: [
|
|
387
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Frames, { parts }),
|
|
388
|
+
children
|
|
389
|
+
] })
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/core/kit.tsx
|
|
395
|
+
var import_react6 = require("react");
|
|
396
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
397
|
+
var KitContext = (0, import_react6.createContext)(null);
|
|
398
|
+
function AmbientKitProvider({ kit, children }) {
|
|
399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KitContext.Provider, { value: kit, children });
|
|
400
|
+
}
|
|
401
|
+
function useKit() {
|
|
402
|
+
return (0, import_react6.useContext)(KitContext);
|
|
403
|
+
}
|
|
404
|
+
var warned = /* @__PURE__ */ new Set();
|
|
405
|
+
function useDress(family, look, fallback) {
|
|
406
|
+
const kit = useKit();
|
|
407
|
+
const dressed = kit?.[family];
|
|
408
|
+
if (isDev && kit && dressed) {
|
|
409
|
+
const honoured = kit.looks?.[family];
|
|
410
|
+
if (honoured) {
|
|
411
|
+
for (const key of Object.keys(look)) {
|
|
412
|
+
if (look[key] === void 0 || honoured.includes(key)) continue;
|
|
413
|
+
const id = `${kit.name}:${family}:${key}`;
|
|
414
|
+
if (warned.has(id)) continue;
|
|
415
|
+
warned.add(id);
|
|
416
|
+
console.warn(
|
|
417
|
+
`[@ambientcss/components] the "${kit.name}" kit's ${family} does not use \`${key}\` \u2014 it is a look prop from another kit's vocabulary, so it will have no effect here.`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
dress: (dressed ?? fallback)(look),
|
|
424
|
+
/* Defaults come from the kit that actually dressed the control: a kit
|
|
425
|
+
that falls through to grounded for a family has no say in how that
|
|
426
|
+
family behaves either. */
|
|
427
|
+
defaults: dressed ? kit?.defaults?.[family] : void 0
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/parts/knob.tsx
|
|
432
|
+
var import_react7 = require("react");
|
|
433
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
434
|
+
var KNURLS = {
|
|
435
|
+
/* 48 ribs — the referent lineup's fine knurl (referents.py knob_cap /
|
|
436
|
+
knob_wheel) rather than the 36 of its coarse one, because a band a tenth
|
|
437
|
+
of the radius wide reads as a machined grip only if the ribs are finer
|
|
438
|
+
than it is; at 36 the same band came out a bottle cap. */
|
|
439
|
+
standard: { teeth: 48, depth: 9e-3, sharpness: 1.6, band: 0.05 }
|
|
440
|
+
};
|
|
441
|
+
var KNURL_BAND = KNURLS.standard.band;
|
|
442
|
+
var SEAM = 5e-3;
|
|
443
|
+
var STEPS = 6;
|
|
444
|
+
function knurlPath({ teeth, depth, sharpness, band }) {
|
|
445
|
+
const outer = 0.5;
|
|
446
|
+
const inner = outer - band - SEAM;
|
|
447
|
+
const segs = teeth * STEPS;
|
|
448
|
+
const pts = [];
|
|
449
|
+
for (let i = 0; i < segs; i++) {
|
|
450
|
+
const t = i / segs * Math.PI * 2;
|
|
451
|
+
const r2 = outer - depth * (0.5 + 0.5 * Math.cos(teeth * t)) ** sharpness;
|
|
452
|
+
pts.push(
|
|
453
|
+
`${(0.5 + r2 * Math.cos(t)).toFixed(4)} ${(0.5 + r2 * Math.sin(t)).toFixed(4)}`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
const l = (0.5 - inner).toFixed(4);
|
|
457
|
+
const r = (0.5 + inner).toFixed(4);
|
|
458
|
+
const hole = `M${l} 0.5 A${inner} ${inner} 0 0 0 ${r} 0.5 A${inner} ${inner} 0 0 0 ${l} 0.5 Z`;
|
|
459
|
+
return `M${pts.join(" L")} Z ${hole}`;
|
|
460
|
+
}
|
|
461
|
+
var KNURL_PATH = knurlPath(KNURLS.standard);
|
|
462
|
+
function KnobBody({
|
|
463
|
+
material,
|
|
464
|
+
flush = false,
|
|
465
|
+
className
|
|
466
|
+
}) {
|
|
467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
468
|
+
"span",
|
|
469
|
+
{
|
|
145
470
|
className: cn(
|
|
146
|
-
"amb-
|
|
147
|
-
|
|
148
|
-
"ambx-switch",
|
|
149
|
-
`ambx-switch-${size}`,
|
|
471
|
+
"amb-knob-body ambient amb-thickness-2 amb-surface",
|
|
472
|
+
material && `amb-mat-${material}`,
|
|
150
473
|
className
|
|
151
474
|
),
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
475
|
+
style: flush ? void 0 : { inset: `${KNURL_BAND * 100}%` }
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
function KnurledFace({
|
|
480
|
+
material,
|
|
481
|
+
color,
|
|
482
|
+
className
|
|
483
|
+
}) {
|
|
484
|
+
const id = `amb-knurl-${(0, import_react7.useId)().replace(/:/g, "")}`;
|
|
485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: 0, height: 0, style: { position: "absolute" }, "aria-hidden": true, focusable: false, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("clipPath", { id, clipPathUnits: "objectBoundingBox", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: KNURL_PATH, clipRule: "evenodd" }) }) }) }),
|
|
487
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
488
|
+
"span",
|
|
489
|
+
{
|
|
490
|
+
className: cn("amb-knob-face", material && `amb-mat-${material}`, className),
|
|
491
|
+
style: {
|
|
492
|
+
clipPath: `url(#${id})`,
|
|
493
|
+
/* Not a paint colour: --amb-albedo is the ribs' REFLECTANCE, so a
|
|
494
|
+
dark knurl still takes the scene's exposure, the lamp's cast and
|
|
495
|
+
the rim's own --amb-shade step, and still goes dark when the
|
|
496
|
+
lights do. Inline, so it beats the albedo a micro-relief material
|
|
497
|
+
would otherwise set on this element — an explicit colour wins
|
|
498
|
+
over the finish's own, and the finish keeps its grain. */
|
|
499
|
+
...color ? { "--amb-albedo": color } : null
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
)
|
|
503
|
+
] });
|
|
504
|
+
}
|
|
505
|
+
function IndicatorDot({ className }) {
|
|
506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn("amb-knob-indicator-circle", className) });
|
|
507
|
+
}
|
|
508
|
+
function IndicatorBar({ className }) {
|
|
509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn("amb-knob-indicator-rectangle", className) });
|
|
510
|
+
}
|
|
511
|
+
function ScaleRing({ count = 13, className, children }) {
|
|
512
|
+
const { travelStart, travelSweep } = useControlState();
|
|
513
|
+
const divisions = Math.max(1, count - 1);
|
|
514
|
+
const angles = count <= 1 ? [travelStart] : Array.from({ length: count }, (_, i) => travelStart + i / divisions * travelSweep);
|
|
515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: cn("amb-knob-marker-ring", className), "aria-hidden": true, children: [
|
|
516
|
+
angles.map((angle) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
517
|
+
"span",
|
|
518
|
+
{
|
|
519
|
+
className: "amb-knob-marker",
|
|
520
|
+
style: { "--amb-marker-angle": `${angle}deg` }
|
|
157
521
|
},
|
|
158
|
-
|
|
522
|
+
angle
|
|
523
|
+
)),
|
|
524
|
+
children
|
|
525
|
+
] });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/parts/travel.tsx
|
|
529
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
530
|
+
function TravelTrack({
|
|
531
|
+
depth = "slot",
|
|
532
|
+
className
|
|
533
|
+
}) {
|
|
534
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
535
|
+
"span",
|
|
536
|
+
{
|
|
537
|
+
className: cn(
|
|
538
|
+
"amb-travel-track amb-groove",
|
|
539
|
+
depth === "channel" && "amb-travel-track-channel",
|
|
540
|
+
className
|
|
541
|
+
)
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
function FaderCap({
|
|
546
|
+
material,
|
|
547
|
+
className
|
|
548
|
+
}) {
|
|
549
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
550
|
+
"span",
|
|
551
|
+
{
|
|
552
|
+
className: cn(
|
|
553
|
+
"amb-fader-thumb ambient amb-fillet",
|
|
554
|
+
material !== "glass" && "amb-surface-concave",
|
|
555
|
+
material && `amb-mat-${material}`,
|
|
556
|
+
className
|
|
557
|
+
),
|
|
558
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "amb-fader-gripline" })
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
function SliderThumb({
|
|
563
|
+
material,
|
|
564
|
+
className
|
|
565
|
+
}) {
|
|
566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
567
|
+
"span",
|
|
568
|
+
{
|
|
569
|
+
className: cn(
|
|
570
|
+
"amb-slider-thumb ambient amb-fillet",
|
|
571
|
+
material !== "glass" && "amb-surface-convex",
|
|
572
|
+
material && `amb-mat-${material}`,
|
|
573
|
+
className
|
|
574
|
+
)
|
|
575
|
+
}
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// src/core/material.ts
|
|
580
|
+
function isRelief(material) {
|
|
581
|
+
return material === "brushed" || material === "brushed-round" || material === "blasted";
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/parts/press.tsx
|
|
585
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
586
|
+
function ButtonCap({
|
|
587
|
+
material = "matte",
|
|
588
|
+
className,
|
|
589
|
+
children
|
|
590
|
+
}) {
|
|
591
|
+
const relief = isRelief(material);
|
|
592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
593
|
+
"span",
|
|
594
|
+
{
|
|
595
|
+
className: cn(
|
|
596
|
+
"amb-button-cap ambient amb-chamfer amb-surface amb-heading-3",
|
|
597
|
+
relief ? void 0 : `amb-mat-${material}`,
|
|
598
|
+
className
|
|
599
|
+
),
|
|
159
600
|
children: [
|
|
160
|
-
|
|
601
|
+
relief ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
161
602
|
"span",
|
|
162
603
|
{
|
|
163
|
-
className: cn("amb-
|
|
164
|
-
|
|
604
|
+
className: cn("ambx-cap-face ambient amb-chamfer amb-surface", `amb-mat-${material}`),
|
|
605
|
+
"aria-hidden": true
|
|
165
606
|
}
|
|
166
607
|
) : null,
|
|
167
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "amb-switch-track amb-groove", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "amb-switch-pill ambient amb-fillet amb-surface-convex" }) }),
|
|
168
608
|
children
|
|
169
609
|
]
|
|
170
610
|
}
|
|
171
611
|
);
|
|
172
|
-
if (!label) return button;
|
|
173
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ambx-stack", children: [
|
|
174
|
-
button,
|
|
175
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: labelId, className: "ambx-label", children: label })
|
|
176
|
-
] });
|
|
177
612
|
}
|
|
178
613
|
|
|
179
|
-
// src/
|
|
180
|
-
var
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
/*
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
[0, root],
|
|
198
|
-
[rise, outer],
|
|
199
|
-
[fall[0], outer],
|
|
200
|
-
[fall[1], root]
|
|
201
|
-
];
|
|
202
|
-
for (const [frac, radius] of tooth) {
|
|
203
|
-
const t = a + frac * pitch;
|
|
204
|
-
pts.push(
|
|
205
|
-
`${(0.5 + radius * Math.cos(t)).toFixed(4)} ${(0.5 + radius * Math.sin(t)).toFixed(4)}`
|
|
206
|
-
);
|
|
614
|
+
// src/parts/latch.tsx
|
|
615
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
616
|
+
function SwitchTrack({ className }) {
|
|
617
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn("amb-switch-track amb-groove", className) });
|
|
618
|
+
}
|
|
619
|
+
function SwitchPill({ className }) {
|
|
620
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn("amb-switch-pill ambient amb-fillet amb-surface-convex", className) });
|
|
621
|
+
}
|
|
622
|
+
function Led({
|
|
623
|
+
on = true,
|
|
624
|
+
color,
|
|
625
|
+
className
|
|
626
|
+
}) {
|
|
627
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
628
|
+
"span",
|
|
629
|
+
{
|
|
630
|
+
className: cn("amb-led", !on && "amb-led-off", className),
|
|
631
|
+
style: color ? { "--amb-led-color": color } : void 0
|
|
207
632
|
}
|
|
208
|
-
|
|
209
|
-
return `M${pts.join(" L")} Z`;
|
|
633
|
+
);
|
|
210
634
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
635
|
+
|
|
636
|
+
// src/parts/bank.tsx
|
|
637
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
638
|
+
function KeyLens({ className }) {
|
|
639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: cn("amb-select-lens", className) });
|
|
640
|
+
}
|
|
641
|
+
function KeyCap({
|
|
642
|
+
className,
|
|
643
|
+
children
|
|
644
|
+
}) {
|
|
645
|
+
const { option } = useBankKey();
|
|
646
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: cn("amb-select-cap ambient amb-chamfer amb-mat-glass", className), children: children ?? option.label ?? option.value });
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// src/kits/grounded.tsx
|
|
650
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
651
|
+
var FULL_MARKERS = 13;
|
|
652
|
+
function rotary(look) {
|
|
653
|
+
const material = look.material;
|
|
654
|
+
const knurling = look.knurling !== false;
|
|
655
|
+
const knurlColor = look.knurlColor;
|
|
656
|
+
const markers = look.markers ?? "none";
|
|
657
|
+
const indicator = look.indicator ?? "circle";
|
|
658
|
+
return {
|
|
659
|
+
/* The full ring reaches past the knob's own box, so its layout clearance
|
|
660
|
+
has to be reserved — and only the kit knows it put a ring there. */
|
|
661
|
+
className: cn("amb-knob", markers === "full" && "amb-knob-markers-full"),
|
|
662
|
+
parts: {
|
|
663
|
+
panel: markers === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScaleRing, { count: markers === "ends" ? 2 : FULL_MARKERS }),
|
|
664
|
+
/* `material` goes on every element that paints. The cap always does —
|
|
665
|
+
it is the knob's top face either way — and the knurl ring does too
|
|
666
|
+
when there is one, so a knurled knob's rim and cap are the same
|
|
667
|
+
material rather than the rim being the only thing wearing it.
|
|
668
|
+
|
|
669
|
+
`knurlColor` is the one thing the ring may hold on its own: a
|
|
670
|
+
two-tone knob — dark grip round a pale cap — is a real piece of
|
|
671
|
+
hardware, and the cap has no matching prop because the cap's colour
|
|
672
|
+
is the control's colour, set the ordinary way with --amb-albedo. */
|
|
673
|
+
base: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(KnobBody, { flush: !knurling, material }),
|
|
674
|
+
actuator: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
675
|
+
knurling ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(KnurledFace, { material, color: knurlColor }) : null,
|
|
676
|
+
indicator === "circle" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IndicatorDot, {}) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IndicatorBar, {})
|
|
677
|
+
] })
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function travel(look) {
|
|
682
|
+
const material = look.material;
|
|
683
|
+
const upright = look.orientation === "vertical";
|
|
684
|
+
return {
|
|
685
|
+
className: upright ? "amb-fader" : "amb-slider",
|
|
686
|
+
parts: {
|
|
687
|
+
base: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TravelTrack, { depth: upright ? "slot" : "channel" }),
|
|
688
|
+
actuator: upright ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FaderCap, { material }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SliderThumb, { material })
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function press(look) {
|
|
693
|
+
const shape = look.shape ?? "pill";
|
|
694
|
+
return {
|
|
695
|
+
className: cn(
|
|
696
|
+
"amb-button amb-groove",
|
|
697
|
+
shape === "round" && "amb-button-round",
|
|
698
|
+
shape === "square" && "amb-button-square"
|
|
699
|
+
),
|
|
700
|
+
parts: {
|
|
701
|
+
actuator: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ButtonCap, { material: look.material ?? "matte", children: look.children })
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function latch() {
|
|
706
|
+
return {
|
|
707
|
+
className: "amb-switch",
|
|
708
|
+
parts: { base: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SwitchTrack, {}), actuator: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SwitchPill, {}) }
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function bank() {
|
|
712
|
+
return {
|
|
713
|
+
className: "amb-select amb-groove",
|
|
714
|
+
parts: { base: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(KeyLens, {}), actuator: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(KeyCap, {}) }
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
var groundedKit = {
|
|
718
|
+
name: "grounded",
|
|
719
|
+
rotary,
|
|
720
|
+
travel,
|
|
721
|
+
press,
|
|
722
|
+
latch,
|
|
723
|
+
bank,
|
|
724
|
+
looks: {
|
|
725
|
+
rotary: ["material", "knurling", "knurlColor", "markers", "indicator"],
|
|
726
|
+
travel: ["material", "orientation"],
|
|
727
|
+
press: ["material", "shape", "children"],
|
|
728
|
+
latch: [],
|
|
729
|
+
bank: []
|
|
730
|
+
}
|
|
222
731
|
};
|
|
223
|
-
|
|
224
|
-
|
|
732
|
+
|
|
733
|
+
// src/components/AmbientButton.tsx
|
|
734
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
735
|
+
function AmbientButton({
|
|
736
|
+
className,
|
|
737
|
+
children,
|
|
738
|
+
look,
|
|
739
|
+
material = "matte",
|
|
740
|
+
shape = "pill",
|
|
741
|
+
size = "md",
|
|
742
|
+
...rest
|
|
743
|
+
}) {
|
|
744
|
+
const { dress } = useDress("press", { material, shape, children, ...look }, groundedKit.press);
|
|
745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
746
|
+
AmbientPress,
|
|
747
|
+
{
|
|
748
|
+
...rest,
|
|
749
|
+
size,
|
|
750
|
+
className: cn(dress.className, className),
|
|
751
|
+
parts: dress.parts
|
|
752
|
+
}
|
|
753
|
+
);
|
|
225
754
|
}
|
|
226
|
-
|
|
755
|
+
|
|
756
|
+
// src/components/AmbientSwitch.tsx
|
|
757
|
+
var import_react10 = require("react");
|
|
758
|
+
|
|
759
|
+
// src/controls/AmbientLatch.tsx
|
|
760
|
+
var import_react9 = require("react");
|
|
761
|
+
|
|
762
|
+
// src/core/useLatch.ts
|
|
763
|
+
var import_react8 = require("react");
|
|
764
|
+
function useLatch(options) {
|
|
765
|
+
const { disabled = false } = options;
|
|
766
|
+
const [on, setOn] = useControllableValue(
|
|
767
|
+
options.value,
|
|
768
|
+
options.defaultValue ?? false,
|
|
769
|
+
options.onChange
|
|
770
|
+
);
|
|
771
|
+
const state = (0, import_react8.useMemo)(
|
|
772
|
+
() => ({
|
|
773
|
+
value: on ? 1 : 0,
|
|
774
|
+
min: 0,
|
|
775
|
+
max: 1,
|
|
776
|
+
percent: on ? 1 : 0,
|
|
777
|
+
angle: 0,
|
|
778
|
+
travelStart: 0,
|
|
779
|
+
travelSweep: 0,
|
|
780
|
+
detents: 2,
|
|
781
|
+
dragging: false,
|
|
782
|
+
disabled,
|
|
783
|
+
atMin: !on,
|
|
784
|
+
atMax: on
|
|
785
|
+
}),
|
|
786
|
+
[on, disabled]
|
|
787
|
+
);
|
|
788
|
+
const rootProps = {
|
|
789
|
+
type: "button",
|
|
790
|
+
role: "switch",
|
|
791
|
+
"aria-checked": on,
|
|
792
|
+
disabled,
|
|
793
|
+
style: stateStyle(state),
|
|
794
|
+
...stateData(state),
|
|
795
|
+
onClick: () => setOn(!on)
|
|
796
|
+
};
|
|
797
|
+
return { state, rootProps, on, setOn };
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// src/controls/AmbientLatch.tsx
|
|
801
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
802
|
+
function AmbientLatch({
|
|
803
|
+
parts,
|
|
804
|
+
size,
|
|
805
|
+
animate = "auto",
|
|
806
|
+
label,
|
|
807
|
+
className,
|
|
227
808
|
value,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
809
|
+
defaultValue,
|
|
810
|
+
onChange,
|
|
811
|
+
disabled,
|
|
812
|
+
children,
|
|
813
|
+
...rest
|
|
814
|
+
}) {
|
|
815
|
+
const labelId = (0, import_react9.useId)();
|
|
816
|
+
const ref = (0, import_react9.useRef)(null);
|
|
817
|
+
const { state, rootProps } = useLatch({ value, defaultValue, onChange, disabled });
|
|
818
|
+
useDevPartCheck(ref, "AmbientLatch");
|
|
819
|
+
const sized = sizeProps("latch", size);
|
|
820
|
+
const { style: restStyle, onClick, ...restProps } = rest;
|
|
821
|
+
const control = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
822
|
+
"button",
|
|
823
|
+
{
|
|
824
|
+
...restProps,
|
|
825
|
+
...rootProps,
|
|
826
|
+
ref,
|
|
827
|
+
"aria-labelledby": label ? labelId : rest["aria-labelledby"],
|
|
828
|
+
"data-animate": animate,
|
|
829
|
+
className: cn("ambx-control ambx-latch", sized.className, className),
|
|
830
|
+
style: { ...rootProps.style, ...sized.style, ...restStyle },
|
|
831
|
+
onClick: (event) => {
|
|
832
|
+
rootProps.onClick();
|
|
833
|
+
onClick?.(event);
|
|
834
|
+
},
|
|
835
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(ControlStateProvider, { value: state, children: [
|
|
836
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Frames, { parts }),
|
|
837
|
+
children
|
|
838
|
+
] })
|
|
839
|
+
}
|
|
840
|
+
);
|
|
841
|
+
if (!label) return control;
|
|
842
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ambx-stack", children: [
|
|
843
|
+
control,
|
|
844
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { id: labelId, className: "ambx-label", children: label })
|
|
845
|
+
] });
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/components/AmbientSwitch.tsx
|
|
849
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
850
|
+
function AmbientSwitch({
|
|
851
|
+
size = "md",
|
|
852
|
+
led,
|
|
231
853
|
label,
|
|
232
|
-
|
|
233
|
-
|
|
854
|
+
look,
|
|
855
|
+
value,
|
|
856
|
+
defaultValue,
|
|
234
857
|
onChange,
|
|
858
|
+
animate,
|
|
235
859
|
className,
|
|
236
|
-
...
|
|
860
|
+
...rest
|
|
237
861
|
}) {
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
862
|
+
const labelId = (0, import_react10.useId)();
|
|
863
|
+
const [on, setOn] = useControllableValue(value, defaultValue ?? false, onChange);
|
|
864
|
+
const { dress, defaults } = useDress("latch", { ...look }, groundedKit.latch);
|
|
865
|
+
const control = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
866
|
+
AmbientLatch,
|
|
867
|
+
{
|
|
868
|
+
...rest,
|
|
869
|
+
value: on,
|
|
870
|
+
onChange: setOn,
|
|
871
|
+
size,
|
|
872
|
+
animate: animate ?? defaults?.animate,
|
|
873
|
+
"aria-labelledby": label ? labelId : rest["aria-labelledby"],
|
|
874
|
+
className: cn(dress.className, className),
|
|
875
|
+
parts: dress.parts
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
if (!led && !label) return control;
|
|
879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ambx-stack", children: [
|
|
880
|
+
led ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "ambx-switch-mount", children: [
|
|
881
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Led, { on, ...typeof led === "string" ? { color: led } : null }),
|
|
882
|
+
control
|
|
883
|
+
] }) : control,
|
|
884
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { id: labelId, className: "ambx-label", children: label }) : null
|
|
885
|
+
] });
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// src/controls/AmbientBank.tsx
|
|
889
|
+
var import_react12 = require("react");
|
|
890
|
+
|
|
891
|
+
// src/core/useBank.ts
|
|
892
|
+
var import_react11 = require("react");
|
|
893
|
+
function toArray(value) {
|
|
894
|
+
if (value === void 0) return [];
|
|
895
|
+
return Array.isArray(value) ? value : [value];
|
|
896
|
+
}
|
|
897
|
+
function useBank(options) {
|
|
898
|
+
const { options: items, multiple = false, orientation = "vertical", disabled = false } = options;
|
|
899
|
+
const [raw, setRaw] = useControllableValue(
|
|
900
|
+
options.value,
|
|
901
|
+
options.defaultValue ?? (multiple ? [] : ""),
|
|
902
|
+
options.onChange
|
|
903
|
+
);
|
|
904
|
+
const selected = toArray(raw);
|
|
905
|
+
const keyRefs = (0, import_react11.useRef)([]);
|
|
906
|
+
const commit2 = (0, import_react11.useCallback)(
|
|
907
|
+
(next) => setRaw(multiple ? next : next[0] ?? ""),
|
|
908
|
+
[setRaw, multiple]
|
|
909
|
+
);
|
|
910
|
+
const select = (0, import_react11.useCallback)(
|
|
911
|
+
(option) => {
|
|
912
|
+
if (!option || option.disabled || disabled) return;
|
|
913
|
+
if (!multiple) return commit2([option.value]);
|
|
914
|
+
commit2(
|
|
915
|
+
selected.includes(option.value) ? selected.filter((v) => v !== option.value) : [...selected, option.value]
|
|
916
|
+
);
|
|
917
|
+
},
|
|
918
|
+
[commit2, multiple, selected, disabled]
|
|
919
|
+
);
|
|
920
|
+
const enabled = items.filter((o) => !o.disabled);
|
|
921
|
+
const litIndex = items.findIndex((o) => selected.includes(o.value) && !o.disabled);
|
|
922
|
+
const firstEnabled = items.findIndex((o) => !o.disabled);
|
|
923
|
+
const tabStop = multiple ? -1 : litIndex >= 0 ? litIndex : firstEnabled;
|
|
924
|
+
const focusAt = (index) => keyRefs.current[index]?.focus();
|
|
925
|
+
const step = (from, delta) => {
|
|
926
|
+
if (enabled.length === 0) return;
|
|
927
|
+
let i = from;
|
|
928
|
+
for (let guard = 0; guard < items.length; guard += 1) {
|
|
929
|
+
i = (i + delta + items.length) % items.length;
|
|
930
|
+
if (!items[i]?.disabled) break;
|
|
931
|
+
}
|
|
932
|
+
focusAt(i);
|
|
933
|
+
if (!multiple) select(items[i]);
|
|
260
934
|
};
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
|
|
935
|
+
const edge = (which) => {
|
|
936
|
+
const i = which === "first" ? firstEnabled : items.map((o) => !o.disabled).lastIndexOf(true);
|
|
937
|
+
if (i < 0) return;
|
|
938
|
+
focusAt(i);
|
|
939
|
+
if (!multiple) select(items[i]);
|
|
264
940
|
};
|
|
265
|
-
const onKeyDown = (event) => {
|
|
266
|
-
const
|
|
941
|
+
const onKeyDown = (event, index) => {
|
|
942
|
+
const back = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
|
|
943
|
+
const forward = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
|
|
267
944
|
switch (event.key) {
|
|
268
|
-
case
|
|
269
|
-
case "ArrowRight":
|
|
270
|
-
event.preventDefault();
|
|
271
|
-
setValue(value + safeStep);
|
|
272
|
-
break;
|
|
273
|
-
case "ArrowDown":
|
|
274
|
-
case "ArrowLeft":
|
|
945
|
+
case back:
|
|
275
946
|
event.preventDefault();
|
|
276
|
-
|
|
947
|
+
step(index, -1);
|
|
277
948
|
break;
|
|
278
|
-
case
|
|
949
|
+
case forward:
|
|
279
950
|
event.preventDefault();
|
|
280
|
-
|
|
281
|
-
break;
|
|
282
|
-
case "PageDown":
|
|
283
|
-
event.preventDefault();
|
|
284
|
-
setValue(value - pageStep);
|
|
951
|
+
step(index, 1);
|
|
285
952
|
break;
|
|
286
953
|
case "Home":
|
|
287
954
|
event.preventDefault();
|
|
288
|
-
|
|
955
|
+
edge("first");
|
|
289
956
|
break;
|
|
290
957
|
case "End":
|
|
291
958
|
event.preventDefault();
|
|
292
|
-
|
|
959
|
+
edge("last");
|
|
960
|
+
break;
|
|
961
|
+
case " ":
|
|
962
|
+
case "Enter":
|
|
963
|
+
event.preventDefault();
|
|
964
|
+
select(items[index]);
|
|
293
965
|
break;
|
|
294
966
|
default:
|
|
295
967
|
break;
|
|
296
968
|
}
|
|
297
969
|
};
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
970
|
+
const rootProps = {
|
|
971
|
+
role: multiple ? "group" : "radiogroup",
|
|
972
|
+
"aria-orientation": multiple ? void 0 : orientation,
|
|
973
|
+
"aria-disabled": disabled || void 0,
|
|
974
|
+
"data-orientation": orientation,
|
|
975
|
+
"data-disabled": disabled ? "" : void 0
|
|
976
|
+
};
|
|
977
|
+
const keyProps = (option, index) => {
|
|
978
|
+
const on = selected.includes(option.value);
|
|
979
|
+
return {
|
|
980
|
+
key: option.value,
|
|
981
|
+
type: "button",
|
|
982
|
+
ref: (node) => {
|
|
983
|
+
keyRefs.current[index] = node;
|
|
984
|
+
},
|
|
985
|
+
role: multiple ? "checkbox" : "radio",
|
|
986
|
+
"aria-checked": on,
|
|
987
|
+
"aria-label": option.ariaLabel,
|
|
988
|
+
title: option.ariaLabel,
|
|
989
|
+
disabled: option.disabled || disabled,
|
|
990
|
+
tabIndex: multiple ? 0 : index === tabStop ? 0 : -1,
|
|
991
|
+
"data-on": on ? "" : void 0,
|
|
992
|
+
onClick: () => select(option),
|
|
993
|
+
onKeyDown: (event) => onKeyDown(event, index)
|
|
994
|
+
};
|
|
995
|
+
};
|
|
996
|
+
return { selected, select, rootProps, keyProps };
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// src/controls/AmbientBank.tsx
|
|
1000
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1001
|
+
function keyState(on, disabled) {
|
|
1002
|
+
return {
|
|
1003
|
+
value: on ? 1 : 0,
|
|
1004
|
+
min: 0,
|
|
1005
|
+
max: 1,
|
|
1006
|
+
percent: on ? 1 : 0,
|
|
1007
|
+
angle: 0,
|
|
1008
|
+
travelStart: 0,
|
|
1009
|
+
travelSweep: 0,
|
|
1010
|
+
detents: 2,
|
|
1011
|
+
dragging: false,
|
|
1012
|
+
disabled,
|
|
1013
|
+
atMin: !on,
|
|
1014
|
+
atMax: on
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function AmbientBank({
|
|
1018
|
+
options,
|
|
1019
|
+
keyParts,
|
|
1020
|
+
parts,
|
|
1021
|
+
size,
|
|
1022
|
+
color,
|
|
1023
|
+
label,
|
|
1024
|
+
renderKey,
|
|
1025
|
+
className,
|
|
1026
|
+
style,
|
|
1027
|
+
value,
|
|
1028
|
+
defaultValue,
|
|
1029
|
+
onChange,
|
|
1030
|
+
multiple,
|
|
1031
|
+
orientation = "vertical",
|
|
1032
|
+
disabled = false,
|
|
1033
|
+
...rest
|
|
1034
|
+
}) {
|
|
1035
|
+
const labelId = (0, import_react12.useId)();
|
|
1036
|
+
const { selected, rootProps, keyProps } = useBank({
|
|
1037
|
+
options,
|
|
1038
|
+
value,
|
|
1039
|
+
defaultValue,
|
|
1040
|
+
onChange,
|
|
1041
|
+
multiple,
|
|
1042
|
+
orientation,
|
|
1043
|
+
disabled
|
|
1044
|
+
});
|
|
1045
|
+
const sized = sizeProps("bank", size);
|
|
1046
|
+
const bank2 = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
1047
|
+
"div",
|
|
1048
|
+
{
|
|
1049
|
+
...rest,
|
|
1050
|
+
...rootProps,
|
|
1051
|
+
"aria-labelledby": label ? labelId : rest["aria-labelledby"],
|
|
1052
|
+
className: cn(
|
|
1053
|
+
"ambx-control ambx-bank",
|
|
1054
|
+
`ambx-bank-${orientation}`,
|
|
1055
|
+
sized.className,
|
|
1056
|
+
className
|
|
1057
|
+
),
|
|
1058
|
+
style: {
|
|
1059
|
+
...color ? { "--amb-led-color": color } : null,
|
|
1060
|
+
...sized.style,
|
|
1061
|
+
...style
|
|
1062
|
+
},
|
|
1063
|
+
children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Frames, { parts }),
|
|
1065
|
+
options.map((option, index) => {
|
|
1066
|
+
const on = selected.includes(option.value);
|
|
1067
|
+
const ks = keyState(on, option.disabled || disabled);
|
|
1068
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1069
|
+
"button",
|
|
332
1070
|
{
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
children: [
|
|
342
|
-
variant === "dot" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "amb-knob-indicator-dot" }) : null,
|
|
343
|
-
variant === "line" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "amb-knob-indicator-line" }) : null,
|
|
344
|
-
variant === "flute" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "amb-knob-indicator-dot amb-knob-indicator-dot-center" }) : null
|
|
345
|
-
]
|
|
1071
|
+
...keyProps(option, index),
|
|
1072
|
+
...stateData(ks),
|
|
1073
|
+
className: "ambx-key",
|
|
1074
|
+
style: {
|
|
1075
|
+
...stateStyle(ks),
|
|
1076
|
+
...option.color ? { "--amb-led-color": option.color } : null
|
|
1077
|
+
},
|
|
1078
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ControlStateProvider, { value: ks, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BankKeyProvider, { value: { option, on, index }, children: renderKey ? renderKey(option, on) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Frames, { parts: keyParts }) }) })
|
|
346
1079
|
}
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
1080
|
+
);
|
|
1081
|
+
})
|
|
1082
|
+
]
|
|
1083
|
+
}
|
|
1084
|
+
);
|
|
1085
|
+
if (!label) return bank2;
|
|
1086
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ambx-stack", children: [
|
|
1087
|
+
bank2,
|
|
1088
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { id: labelId, className: "ambx-label", children: label })
|
|
352
1089
|
] });
|
|
353
1090
|
}
|
|
354
1091
|
|
|
355
|
-
// src/components/
|
|
356
|
-
var
|
|
357
|
-
|
|
358
|
-
|
|
1092
|
+
// src/components/AmbientSelect.tsx
|
|
1093
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1094
|
+
function AmbientSelect({ size = "md", look, className, ...rest }) {
|
|
1095
|
+
const { dress } = useDress("bank", { ...look }, groundedKit.bank);
|
|
1096
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1097
|
+
AmbientBank,
|
|
1098
|
+
{
|
|
1099
|
+
...rest,
|
|
1100
|
+
size,
|
|
1101
|
+
className: cn(dress.className, className),
|
|
1102
|
+
keyParts: dress.parts
|
|
1103
|
+
}
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/controls/AmbientRotary.tsx
|
|
1108
|
+
var import_react14 = require("react");
|
|
1109
|
+
|
|
1110
|
+
// src/core/useRotary.ts
|
|
1111
|
+
var import_react13 = require("react");
|
|
1112
|
+
|
|
1113
|
+
// src/core/numeric.ts
|
|
1114
|
+
function clamp(value, min, max) {
|
|
359
1115
|
return Math.min(max, Math.max(min, value));
|
|
360
1116
|
}
|
|
361
|
-
function
|
|
362
|
-
value
|
|
363
|
-
min
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
onChange?.(clamp2(snapped, min, max));
|
|
384
|
-
};
|
|
385
|
-
const setValue = (nextValue) => {
|
|
386
|
-
const snapped = Math.round(nextValue / safeStep) * safeStep;
|
|
387
|
-
onChange?.(clamp2(snapped, min, max));
|
|
388
|
-
};
|
|
389
|
-
const onKeyDown = (event) => {
|
|
390
|
-
const pageStep = safeStep * 10;
|
|
1117
|
+
function snap(value, min, step) {
|
|
1118
|
+
if (!(step > 0)) return value;
|
|
1119
|
+
return min + Math.round((value - min) / step) * step;
|
|
1120
|
+
}
|
|
1121
|
+
function normalise(value, min, max) {
|
|
1122
|
+
return (value - min) / (max - min || 1);
|
|
1123
|
+
}
|
|
1124
|
+
function denormalise(t, min, max) {
|
|
1125
|
+
return min + t * (max - min);
|
|
1126
|
+
}
|
|
1127
|
+
function commit(value, min, max, step) {
|
|
1128
|
+
return clamp(snap(value, min, max === min ? 1 : step), min, max);
|
|
1129
|
+
}
|
|
1130
|
+
function valueKeyHandler(options) {
|
|
1131
|
+
return (event) => {
|
|
1132
|
+
const { value, min, max, step, invert, onChange, disabled } = options;
|
|
1133
|
+
if (disabled) return false;
|
|
1134
|
+
const dir = invert ? -1 : 1;
|
|
1135
|
+
const set = (next) => {
|
|
1136
|
+
event.preventDefault();
|
|
1137
|
+
onChange(commit(next, min, max, step));
|
|
1138
|
+
};
|
|
391
1139
|
switch (event.key) {
|
|
392
1140
|
case "ArrowUp":
|
|
393
1141
|
case "ArrowRight":
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
break;
|
|
1142
|
+
set(value + step * dir);
|
|
1143
|
+
return true;
|
|
397
1144
|
case "ArrowDown":
|
|
398
1145
|
case "ArrowLeft":
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
break;
|
|
1146
|
+
set(value - step * dir);
|
|
1147
|
+
return true;
|
|
402
1148
|
case "PageUp":
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
break;
|
|
1149
|
+
set(value + step * 10 * dir);
|
|
1150
|
+
return true;
|
|
406
1151
|
case "PageDown":
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
break;
|
|
1152
|
+
set(value - step * 10 * dir);
|
|
1153
|
+
return true;
|
|
410
1154
|
case "Home":
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
break;
|
|
1155
|
+
set(min);
|
|
1156
|
+
return true;
|
|
414
1157
|
case "End":
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
break;
|
|
1158
|
+
set(max);
|
|
1159
|
+
return true;
|
|
418
1160
|
default:
|
|
419
|
-
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// src/core/useRotary.ts
|
|
1167
|
+
var DEFAULT_TRAVEL = { start: -135, sweep: 270 };
|
|
1168
|
+
function wrapDeg(deg) {
|
|
1169
|
+
return ((deg + 180) % 360 + 360) % 360 - 180;
|
|
1170
|
+
}
|
|
1171
|
+
function resolveTravel(travel2) {
|
|
1172
|
+
if (travel2 === void 0) return DEFAULT_TRAVEL;
|
|
1173
|
+
if (typeof travel2 === "number") return { start: -travel2 / 2, sweep: travel2 };
|
|
1174
|
+
return travel2;
|
|
1175
|
+
}
|
|
1176
|
+
var warnedFullTurn = false;
|
|
1177
|
+
function capturePointer(target, pointerId) {
|
|
1178
|
+
try {
|
|
1179
|
+
target.setPointerCapture?.(pointerId);
|
|
1180
|
+
} catch {
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
function useRotary(options) {
|
|
1184
|
+
const {
|
|
1185
|
+
min = 0,
|
|
1186
|
+
max = 100,
|
|
1187
|
+
step = 1,
|
|
1188
|
+
dragDistance = 200,
|
|
1189
|
+
wrap = false,
|
|
1190
|
+
disabled = false
|
|
1191
|
+
} = options;
|
|
1192
|
+
const { start, sweep } = resolveTravel(options.travel);
|
|
1193
|
+
let input = options.input ?? "drag";
|
|
1194
|
+
if (input === "angle" && Math.abs(sweep) >= 360) {
|
|
1195
|
+
if (isDev && !warnedFullTurn) {
|
|
1196
|
+
warnedFullTurn = true;
|
|
1197
|
+
console.warn(
|
|
1198
|
+
`[@ambientcss/components] input="angle" needs a sweep under a full turn (got ${sweep}deg): an absolute angle is ambiguous at 360deg, where the first and last values share a screen position. Falling back to "drag".`
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
input = "drag";
|
|
1202
|
+
}
|
|
1203
|
+
const [value, setValue] = useControllableValue(
|
|
1204
|
+
options.value,
|
|
1205
|
+
options.defaultValue ?? min,
|
|
1206
|
+
options.onChange
|
|
1207
|
+
);
|
|
1208
|
+
const [dragging, setDragging] = (0, import_react13.useState)(false);
|
|
1209
|
+
const draggingRef = (0, import_react13.useRef)(false);
|
|
1210
|
+
const rootRef = (0, import_react13.useRef)(null);
|
|
1211
|
+
const drag = (0, import_react13.useRef)({ value: 0, clientY: 0, angle: 0, accum: 0 });
|
|
1212
|
+
const range = max - min;
|
|
1213
|
+
const keyStep = step > 0 ? step : range / 100 || 1;
|
|
1214
|
+
const percent = clamp(normalise(value, min, max), 0, 1);
|
|
1215
|
+
const angle = start + percent * sweep;
|
|
1216
|
+
const set = (0, import_react13.useCallback)(
|
|
1217
|
+
(next) => setValue(commit(next, min, max, step)),
|
|
1218
|
+
[setValue, min, max, step]
|
|
1219
|
+
);
|
|
1220
|
+
const pointerAngle = (event) => {
|
|
1221
|
+
const rect = rootRef.current?.getBoundingClientRect();
|
|
1222
|
+
if (!rect) return null;
|
|
1223
|
+
const dx = event.clientX - (rect.left + rect.width / 2);
|
|
1224
|
+
const dy = event.clientY - (rect.top + rect.height / 2);
|
|
1225
|
+
return Math.atan2(dy, dx) * (180 / Math.PI) + 90;
|
|
1226
|
+
};
|
|
1227
|
+
const fromAngle = (event) => {
|
|
1228
|
+
const raw = pointerAngle(event);
|
|
1229
|
+
if (raw === null) return;
|
|
1230
|
+
const mid = start + sweep / 2;
|
|
1231
|
+
let a = mid + wrapDeg(raw - mid);
|
|
1232
|
+
const end = start + sweep;
|
|
1233
|
+
if (a < Math.min(start, end) || a > Math.max(start, end)) {
|
|
1234
|
+
const toStart = Math.abs(wrapDeg(a - start));
|
|
1235
|
+
const toEnd = Math.abs(wrapDeg(a - end));
|
|
1236
|
+
a = toStart <= toEnd ? start : end;
|
|
1237
|
+
}
|
|
1238
|
+
set(denormalise((a - start) / (sweep || 1), min, max));
|
|
1239
|
+
};
|
|
1240
|
+
const fromDrag = (event) => {
|
|
1241
|
+
const dy = drag.current.clientY - event.clientY;
|
|
1242
|
+
drag.current.clientY = event.clientY;
|
|
1243
|
+
drag.current.accum += dy * (event.shiftKey ? 0.25 : 1);
|
|
1244
|
+
set(drag.current.value + drag.current.accum / dragDistance * range);
|
|
1245
|
+
};
|
|
1246
|
+
const fromDelta = (event) => {
|
|
1247
|
+
const raw = pointerAngle(event);
|
|
1248
|
+
if (raw === null) return;
|
|
1249
|
+
drag.current.accum += wrapDeg(raw - drag.current.angle);
|
|
1250
|
+
drag.current.angle = raw;
|
|
1251
|
+
const next = drag.current.value + drag.current.accum / (sweep || 360) * range;
|
|
1252
|
+
set(wrap && range > 0 ? min + ((next - min) % range + range) % range : next);
|
|
1253
|
+
};
|
|
1254
|
+
const track = (event) => {
|
|
1255
|
+
if (input === "angle") fromAngle(event);
|
|
1256
|
+
else if (input === "delta") fromDelta(event);
|
|
1257
|
+
else fromDrag(event);
|
|
1258
|
+
};
|
|
1259
|
+
const state = (0, import_react13.useMemo)(
|
|
1260
|
+
() => ({
|
|
1261
|
+
value,
|
|
1262
|
+
min,
|
|
1263
|
+
max,
|
|
1264
|
+
percent,
|
|
1265
|
+
angle,
|
|
1266
|
+
travelStart: start,
|
|
1267
|
+
travelSweep: sweep,
|
|
1268
|
+
detents: options.detents ?? (step > 0 && range > 0 ? Math.round(range / step) : 0),
|
|
1269
|
+
dragging,
|
|
1270
|
+
disabled,
|
|
1271
|
+
atMin: value <= min,
|
|
1272
|
+
atMax: value >= max
|
|
1273
|
+
}),
|
|
1274
|
+
[value, min, max, percent, angle, start, sweep, options.detents, step, range, dragging, disabled]
|
|
1275
|
+
);
|
|
1276
|
+
const onKeyDown = valueKeyHandler({ value, min, max, step: keyStep, disabled, onChange: setValue });
|
|
1277
|
+
const rootProps = {
|
|
1278
|
+
ref: rootRef,
|
|
1279
|
+
role: "slider",
|
|
1280
|
+
"aria-valuemin": min,
|
|
1281
|
+
"aria-valuemax": max,
|
|
1282
|
+
"aria-valuenow": value,
|
|
1283
|
+
"aria-orientation": "vertical",
|
|
1284
|
+
"aria-disabled": disabled || void 0,
|
|
1285
|
+
tabIndex: disabled ? -1 : 0,
|
|
1286
|
+
style: stateStyle(state),
|
|
1287
|
+
...stateData(state),
|
|
1288
|
+
onPointerDown: (event) => {
|
|
1289
|
+
if (disabled || event.button !== 0) return;
|
|
1290
|
+
capturePointer(event.currentTarget, event.pointerId);
|
|
1291
|
+
draggingRef.current = true;
|
|
1292
|
+
setDragging(true);
|
|
1293
|
+
drag.current = {
|
|
1294
|
+
value,
|
|
1295
|
+
clientY: event.clientY,
|
|
1296
|
+
angle: pointerAngle(event) ?? 0,
|
|
1297
|
+
accum: 0
|
|
1298
|
+
};
|
|
1299
|
+
if (input === "angle") fromAngle(event);
|
|
1300
|
+
},
|
|
1301
|
+
onPointerMove: (event) => {
|
|
1302
|
+
if (!draggingRef.current || disabled) return;
|
|
1303
|
+
track(event);
|
|
1304
|
+
},
|
|
1305
|
+
onPointerUp: () => {
|
|
1306
|
+
draggingRef.current = false;
|
|
1307
|
+
setDragging(false);
|
|
1308
|
+
},
|
|
1309
|
+
onPointerCancel: () => {
|
|
1310
|
+
draggingRef.current = false;
|
|
1311
|
+
setDragging(false);
|
|
1312
|
+
},
|
|
1313
|
+
onKeyDown: (event) => {
|
|
1314
|
+
onKeyDown(event);
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
return { state, rootProps, setValue: set };
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// src/controls/AmbientRotary.tsx
|
|
1321
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1322
|
+
function AmbientRotary({
|
|
1323
|
+
parts,
|
|
1324
|
+
size,
|
|
1325
|
+
animate = "auto",
|
|
1326
|
+
label,
|
|
1327
|
+
className,
|
|
1328
|
+
value,
|
|
1329
|
+
defaultValue,
|
|
1330
|
+
min,
|
|
1331
|
+
max,
|
|
1332
|
+
step,
|
|
1333
|
+
detents,
|
|
1334
|
+
travel: travel2,
|
|
1335
|
+
input,
|
|
1336
|
+
dragDistance,
|
|
1337
|
+
wrap,
|
|
1338
|
+
disabled,
|
|
1339
|
+
onChange,
|
|
1340
|
+
...rest
|
|
1341
|
+
}) {
|
|
1342
|
+
const labelId = (0, import_react14.useId)();
|
|
1343
|
+
const stackRef = (0, import_react14.useRef)(null);
|
|
1344
|
+
const { state, rootProps } = useRotary({
|
|
1345
|
+
value,
|
|
1346
|
+
defaultValue,
|
|
1347
|
+
min,
|
|
1348
|
+
max,
|
|
1349
|
+
step,
|
|
1350
|
+
detents,
|
|
1351
|
+
travel: travel2,
|
|
1352
|
+
input,
|
|
1353
|
+
dragDistance,
|
|
1354
|
+
wrap,
|
|
1355
|
+
disabled,
|
|
1356
|
+
onChange
|
|
1357
|
+
});
|
|
1358
|
+
useDevPartCheck(stackRef, "AmbientRotary");
|
|
1359
|
+
const sized = sizeProps("rotary", size);
|
|
1360
|
+
const { style: restStyle, ...restProps } = rest;
|
|
1361
|
+
const control = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1362
|
+
"div",
|
|
1363
|
+
{
|
|
1364
|
+
...restProps,
|
|
1365
|
+
...rootProps,
|
|
1366
|
+
"aria-labelledby": label ? labelId : rest["aria-labelledby"],
|
|
1367
|
+
"data-animate": animate,
|
|
1368
|
+
className: cn("ambx-control ambx-rotary", sized.className, className),
|
|
1369
|
+
style: { ...rootProps.style, ...sized.style, ...restStyle },
|
|
1370
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ControlStateProvider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Frames, { parts }) })
|
|
1371
|
+
}
|
|
1372
|
+
);
|
|
1373
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ambx-stack", ref: stackRef, children: [
|
|
1374
|
+
control,
|
|
1375
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { id: labelId, className: "ambx-label", children: label }) : null
|
|
1376
|
+
] });
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/components/AmbientKnob.tsx
|
|
1380
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1381
|
+
function AmbientKnob({
|
|
1382
|
+
material,
|
|
1383
|
+
knurling,
|
|
1384
|
+
knurlColor,
|
|
1385
|
+
markers,
|
|
1386
|
+
indicator,
|
|
1387
|
+
look,
|
|
1388
|
+
size = "md",
|
|
1389
|
+
className,
|
|
1390
|
+
travel: travel2,
|
|
1391
|
+
input,
|
|
1392
|
+
animate,
|
|
1393
|
+
...rest
|
|
1394
|
+
}) {
|
|
1395
|
+
const { dress, defaults } = useDress(
|
|
1396
|
+
"rotary",
|
|
1397
|
+
{ material, knurling, knurlColor, markers, indicator, ...look },
|
|
1398
|
+
groundedKit.rotary
|
|
1399
|
+
);
|
|
1400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1401
|
+
AmbientRotary,
|
|
1402
|
+
{
|
|
1403
|
+
...rest,
|
|
1404
|
+
travel: travel2 ?? defaults?.travel,
|
|
1405
|
+
input: input ?? defaults?.input,
|
|
1406
|
+
animate: animate ?? defaults?.animate,
|
|
1407
|
+
size,
|
|
1408
|
+
className: cn(dress.className, className),
|
|
1409
|
+
parts: dress.parts
|
|
420
1410
|
}
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// src/controls/AmbientTravel.tsx
|
|
1415
|
+
var import_react16 = require("react");
|
|
1416
|
+
|
|
1417
|
+
// src/core/useTravel.ts
|
|
1418
|
+
var import_react15 = require("react");
|
|
1419
|
+
function useTravel(options) {
|
|
1420
|
+
const {
|
|
1421
|
+
min = 0,
|
|
1422
|
+
max = 100,
|
|
1423
|
+
step = 1,
|
|
1424
|
+
orientation = "horizontal",
|
|
1425
|
+
invert = false,
|
|
1426
|
+
disabled = false
|
|
1427
|
+
} = options;
|
|
1428
|
+
const [value, setValue] = useControllableValue(
|
|
1429
|
+
options.value,
|
|
1430
|
+
options.defaultValue ?? min,
|
|
1431
|
+
options.onChange
|
|
1432
|
+
);
|
|
1433
|
+
const [dragging, setDragging] = (0, import_react15.useState)(false);
|
|
1434
|
+
const draggingRef = (0, import_react15.useRef)(false);
|
|
1435
|
+
const rootRef = (0, import_react15.useRef)(null);
|
|
1436
|
+
const vertical = orientation === "vertical";
|
|
1437
|
+
const range = max - min;
|
|
1438
|
+
const keyStep = step > 0 ? step : range / 100 || 1;
|
|
1439
|
+
const percent = clamp(normalise(value, min, max), 0, 1);
|
|
1440
|
+
const set = (0, import_react15.useCallback)(
|
|
1441
|
+
(next) => setValue(commit(next, min, max, step)),
|
|
1442
|
+
[setValue, min, max, step]
|
|
1443
|
+
);
|
|
1444
|
+
const track = (event) => {
|
|
1445
|
+
const rect = rootRef.current?.getBoundingClientRect();
|
|
1446
|
+
if (!rect) return;
|
|
1447
|
+
const raw = vertical ? 1 - (event.clientY - rect.top) / (rect.height || 1) : (event.clientX - rect.left) / (rect.width || 1);
|
|
1448
|
+
set(denormalise(clamp(invert ? 1 - raw : raw, 0, 1), min, max));
|
|
421
1449
|
};
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
1450
|
+
const state = (0, import_react15.useMemo)(
|
|
1451
|
+
() => ({
|
|
1452
|
+
value,
|
|
1453
|
+
min,
|
|
1454
|
+
max,
|
|
1455
|
+
percent,
|
|
1456
|
+
angle: 0,
|
|
1457
|
+
travelStart: 0,
|
|
1458
|
+
travelSweep: 0,
|
|
1459
|
+
detents: options.detents ?? (step > 0 && range > 0 ? Math.round(range / step) : 0),
|
|
1460
|
+
dragging,
|
|
1461
|
+
disabled,
|
|
1462
|
+
atMin: value <= min,
|
|
1463
|
+
atMax: value >= max
|
|
1464
|
+
}),
|
|
1465
|
+
[value, min, max, percent, options.detents, step, range, dragging, disabled]
|
|
1466
|
+
);
|
|
1467
|
+
const onKeyDown = valueKeyHandler({
|
|
1468
|
+
value,
|
|
1469
|
+
min,
|
|
1470
|
+
max,
|
|
1471
|
+
step: keyStep,
|
|
1472
|
+
disabled,
|
|
1473
|
+
invert,
|
|
1474
|
+
onChange: setValue
|
|
1475
|
+
});
|
|
1476
|
+
const rootProps = {
|
|
1477
|
+
ref: rootRef,
|
|
1478
|
+
role: "slider",
|
|
1479
|
+
"aria-valuemin": min,
|
|
1480
|
+
"aria-valuemax": max,
|
|
1481
|
+
"aria-valuenow": value,
|
|
1482
|
+
"aria-orientation": orientation,
|
|
1483
|
+
"aria-disabled": disabled || void 0,
|
|
1484
|
+
tabIndex: disabled ? -1 : 0,
|
|
1485
|
+
style: stateStyle(state),
|
|
1486
|
+
"data-orientation": orientation,
|
|
1487
|
+
...stateData(state),
|
|
1488
|
+
onPointerDown: (event) => {
|
|
1489
|
+
if (disabled || event.button !== 0) return;
|
|
1490
|
+
capturePointer(event.currentTarget, event.pointerId);
|
|
1491
|
+
draggingRef.current = true;
|
|
1492
|
+
setDragging(true);
|
|
1493
|
+
track(event);
|
|
1494
|
+
},
|
|
1495
|
+
onPointerMove: (event) => {
|
|
1496
|
+
if (!draggingRef.current || disabled) return;
|
|
1497
|
+
track(event);
|
|
1498
|
+
},
|
|
1499
|
+
onPointerUp: () => {
|
|
1500
|
+
draggingRef.current = false;
|
|
1501
|
+
setDragging(false);
|
|
1502
|
+
},
|
|
1503
|
+
onPointerCancel: () => {
|
|
1504
|
+
draggingRef.current = false;
|
|
1505
|
+
setDragging(false);
|
|
1506
|
+
},
|
|
1507
|
+
onKeyDown: (event) => {
|
|
1508
|
+
onKeyDown(event);
|
|
1509
|
+
}
|
|
425
1510
|
};
|
|
426
|
-
return
|
|
427
|
-
|
|
1511
|
+
return { state, rootProps, setValue: set };
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// src/controls/AmbientTravel.tsx
|
|
1515
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1516
|
+
function AmbientTravel({
|
|
1517
|
+
parts,
|
|
1518
|
+
size,
|
|
1519
|
+
animate = "auto",
|
|
1520
|
+
label,
|
|
1521
|
+
className,
|
|
1522
|
+
value,
|
|
1523
|
+
defaultValue,
|
|
1524
|
+
min,
|
|
1525
|
+
max,
|
|
1526
|
+
step,
|
|
1527
|
+
detents,
|
|
1528
|
+
orientation = "horizontal",
|
|
1529
|
+
invert,
|
|
1530
|
+
disabled,
|
|
1531
|
+
onChange,
|
|
1532
|
+
...rest
|
|
1533
|
+
}) {
|
|
1534
|
+
const labelId = (0, import_react16.useId)();
|
|
1535
|
+
const stackRef = (0, import_react16.useRef)(null);
|
|
1536
|
+
const { state, rootProps } = useTravel({
|
|
1537
|
+
value,
|
|
1538
|
+
defaultValue,
|
|
1539
|
+
min,
|
|
1540
|
+
max,
|
|
1541
|
+
step,
|
|
1542
|
+
detents,
|
|
1543
|
+
orientation,
|
|
1544
|
+
invert,
|
|
1545
|
+
disabled,
|
|
1546
|
+
onChange
|
|
1547
|
+
});
|
|
1548
|
+
useDevPartCheck(stackRef, "AmbientTravel");
|
|
1549
|
+
const sized = sizeProps("travel", size);
|
|
1550
|
+
const { style: restStyle, ...restProps } = rest;
|
|
1551
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "ambx-stack", ref: stackRef, children: [
|
|
1552
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
428
1553
|
"div",
|
|
429
1554
|
{
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
"
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
updateFromClientY(event.clientY);
|
|
443
|
-
},
|
|
444
|
-
onPointerMove,
|
|
445
|
-
onKeyDown,
|
|
446
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
447
|
-
"div",
|
|
448
|
-
{
|
|
449
|
-
className: cn("amb-fader-thumb ambient amb-fillet ambx-fader-thumb", material !== "glass" && "amb-surface-concave", material && `amb-mat-${material}`),
|
|
450
|
-
style: { top: `${100 - percent}%` },
|
|
451
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "amb-fader-gripline" })
|
|
452
|
-
}
|
|
453
|
-
)
|
|
1555
|
+
...restProps,
|
|
1556
|
+
...rootProps,
|
|
1557
|
+
"aria-labelledby": label ? labelId : rest["aria-labelledby"],
|
|
1558
|
+
"data-animate": animate,
|
|
1559
|
+
className: cn(
|
|
1560
|
+
"ambx-control ambx-travel",
|
|
1561
|
+
`ambx-travel-${orientation}`,
|
|
1562
|
+
sized.className,
|
|
1563
|
+
className
|
|
1564
|
+
),
|
|
1565
|
+
style: { ...rootProps.style, ...sized.style, ...restStyle },
|
|
1566
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ControlStateProvider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Frames, { parts }) })
|
|
454
1567
|
}
|
|
455
1568
|
),
|
|
456
|
-
label ? /* @__PURE__ */ (0,
|
|
1569
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { id: labelId, className: "ambx-label", children: label }) : null
|
|
457
1570
|
] });
|
|
458
1571
|
}
|
|
459
1572
|
|
|
460
|
-
// src/components/
|
|
461
|
-
var
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
1573
|
+
// src/components/AmbientFader.tsx
|
|
1574
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1575
|
+
function AmbientFader({
|
|
1576
|
+
material,
|
|
1577
|
+
look,
|
|
1578
|
+
size = "md",
|
|
1579
|
+
animate,
|
|
1580
|
+
className,
|
|
1581
|
+
...rest
|
|
1582
|
+
}) {
|
|
1583
|
+
const { dress, defaults } = useDress(
|
|
1584
|
+
"travel",
|
|
1585
|
+
{ material, orientation: "vertical", ...look },
|
|
1586
|
+
groundedKit.travel
|
|
1587
|
+
);
|
|
1588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1589
|
+
AmbientTravel,
|
|
1590
|
+
{
|
|
1591
|
+
...rest,
|
|
1592
|
+
orientation: "vertical",
|
|
1593
|
+
size,
|
|
1594
|
+
animate: animate ?? defaults?.animate,
|
|
1595
|
+
className: cn(dress.className, className),
|
|
1596
|
+
parts: dress.parts
|
|
1597
|
+
}
|
|
1598
|
+
);
|
|
465
1599
|
}
|
|
1600
|
+
|
|
1601
|
+
// src/components/AmbientSlider.tsx
|
|
1602
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
466
1603
|
function AmbientSlider({
|
|
467
|
-
value,
|
|
468
|
-
min = 0,
|
|
469
|
-
max = 100,
|
|
470
|
-
step = 1,
|
|
471
|
-
label,
|
|
472
1604
|
material,
|
|
473
|
-
|
|
1605
|
+
look,
|
|
1606
|
+
size = "md",
|
|
1607
|
+
animate,
|
|
474
1608
|
className,
|
|
475
|
-
...
|
|
1609
|
+
...rest
|
|
476
1610
|
}) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
1611
|
+
const { dress, defaults } = useDress(
|
|
1612
|
+
"travel",
|
|
1613
|
+
{ material, orientation: "horizontal", ...look },
|
|
1614
|
+
groundedKit.travel
|
|
1615
|
+
);
|
|
1616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1617
|
+
AmbientTravel,
|
|
1618
|
+
{
|
|
1619
|
+
...rest,
|
|
1620
|
+
orientation: "horizontal",
|
|
1621
|
+
size,
|
|
1622
|
+
animate: animate ?? defaults?.animate,
|
|
1623
|
+
className: cn(dress.className, className),
|
|
1624
|
+
parts: dress.parts
|
|
1625
|
+
}
|
|
1626
|
+
);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// src/parts/console.tsx
|
|
1630
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1631
|
+
function ConsoleWell({ className }) {
|
|
1632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: cn("amb-console-housing amb-groove", className), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "amb-console-face amb-surface" }) });
|
|
1633
|
+
}
|
|
1634
|
+
function ConsoleBar({ className }) {
|
|
1635
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: cn("amb-console-bar", className), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "amb-console-bar-body ambient amb-surface amb-chamfer amb-thickness-2", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "amb-console-indicator" }) }) });
|
|
1636
|
+
}
|
|
1637
|
+
function ConsoleMarks({
|
|
1638
|
+
mark = true,
|
|
1639
|
+
legend = true,
|
|
1640
|
+
className
|
|
1641
|
+
}) {
|
|
1642
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: cn("amb-console-marks", className), "aria-hidden": true, children: [
|
|
1643
|
+
mark ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "amb-console-mark" }) : null,
|
|
1644
|
+
legend ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1645
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "amb-console-legend amb-console-legend-min", children: "\u2212" }),
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "amb-console-legend amb-console-legend-max", children: "+" })
|
|
1647
|
+
] }) : null
|
|
1648
|
+
] });
|
|
1649
|
+
}
|
|
1650
|
+
function ToggleTrack({ className }) {
|
|
1651
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: cn("amb-console-track amb-groove", className) });
|
|
1652
|
+
}
|
|
1653
|
+
function ToggleThumb({ className }) {
|
|
1654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: cn("amb-console-thumb ambient amb-thickness-2", className) });
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
// src/kits/console.tsx
|
|
1658
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1659
|
+
function rotary2(look) {
|
|
1660
|
+
const mark = look.mark !== false;
|
|
1661
|
+
const legend = look.legend !== false;
|
|
1662
|
+
return {
|
|
1663
|
+
className: cn(
|
|
1664
|
+
"amb-console-knob",
|
|
1665
|
+
mark && "amb-console-knob-marked",
|
|
1666
|
+
legend && "amb-console-knob-legended"
|
|
1667
|
+
),
|
|
1668
|
+
parts: {
|
|
1669
|
+
panel: mark || legend ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ConsoleMarks, { mark, legend }) : null,
|
|
1670
|
+
base: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ConsoleWell, {}),
|
|
1671
|
+
actuator: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ConsoleBar, {})
|
|
525
1672
|
}
|
|
526
1673
|
};
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
1674
|
+
}
|
|
1675
|
+
function latch2() {
|
|
1676
|
+
return {
|
|
1677
|
+
className: "amb-console-toggle",
|
|
1678
|
+
parts: { base: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleTrack, {}), actuator: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleThumb, {}) }
|
|
530
1679
|
};
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
"div",
|
|
553
|
-
{
|
|
554
|
-
className: cn("amb-slider-thumb ambient amb-fillet ambx-slider-thumb", material !== "glass" && "amb-surface-convex", material && `amb-mat-${material}`),
|
|
555
|
-
style: { left: `${percent}%` }
|
|
556
|
-
}
|
|
557
|
-
)
|
|
558
|
-
}
|
|
559
|
-
),
|
|
560
|
-
label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id, className: "ambx-label", children: label }) : null
|
|
561
|
-
] });
|
|
1680
|
+
}
|
|
1681
|
+
var consoleKit = {
|
|
1682
|
+
name: "console",
|
|
1683
|
+
rotary: rotary2,
|
|
1684
|
+
latch: latch2,
|
|
1685
|
+
/* A visual identity may say how its controls feel to turn. The desk knob
|
|
1686
|
+
this is drawn from is an absolute-position pot with a centre detent, so
|
|
1687
|
+
it grabs where you press rather than tracking a drag. */
|
|
1688
|
+
defaults: {
|
|
1689
|
+
rotary: { input: "angle", travel: 280 }
|
|
1690
|
+
},
|
|
1691
|
+
looks: {
|
|
1692
|
+
rotary: ["mark", "legend"],
|
|
1693
|
+
latch: []
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
function ConsoleKnob({ mark, legend, ...rest }) {
|
|
1697
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AmbientKnob, { ...rest, look: { mark, legend } });
|
|
1698
|
+
}
|
|
1699
|
+
function ConsoleToggle(props) {
|
|
1700
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AmbientSwitch, { ...props });
|
|
562
1701
|
}
|
|
563
1702
|
// Annotate the CommonJS export names for ESM import in node:
|
|
564
1703
|
0 && (module.exports = {
|
|
1704
|
+
AmbientBank,
|
|
565
1705
|
AmbientButton,
|
|
566
1706
|
AmbientFader,
|
|
1707
|
+
AmbientKitProvider,
|
|
567
1708
|
AmbientKnob,
|
|
1709
|
+
AmbientLatch,
|
|
568
1710
|
AmbientPanel,
|
|
1711
|
+
AmbientPress,
|
|
569
1712
|
AmbientProvider,
|
|
1713
|
+
AmbientRack,
|
|
1714
|
+
AmbientRotary,
|
|
1715
|
+
AmbientSelect,
|
|
570
1716
|
AmbientSlider,
|
|
571
|
-
AmbientSwitch
|
|
1717
|
+
AmbientSwitch,
|
|
1718
|
+
AmbientTravel,
|
|
1719
|
+
ButtonCap,
|
|
1720
|
+
ConsoleBar,
|
|
1721
|
+
ConsoleKnob,
|
|
1722
|
+
ConsoleMarks,
|
|
1723
|
+
ConsoleToggle,
|
|
1724
|
+
ConsoleWell,
|
|
1725
|
+
FaderCap,
|
|
1726
|
+
IndicatorBar,
|
|
1727
|
+
IndicatorDot,
|
|
1728
|
+
KeyCap,
|
|
1729
|
+
KeyLens,
|
|
1730
|
+
KnobBody,
|
|
1731
|
+
KnurledFace,
|
|
1732
|
+
Led,
|
|
1733
|
+
ScaleRing,
|
|
1734
|
+
SliderThumb,
|
|
1735
|
+
SwitchPill,
|
|
1736
|
+
SwitchTrack,
|
|
1737
|
+
ToggleThumb,
|
|
1738
|
+
ToggleTrack,
|
|
1739
|
+
TravelTrack,
|
|
1740
|
+
consoleKit,
|
|
1741
|
+
groundedKit,
|
|
1742
|
+
useBank,
|
|
1743
|
+
useBankKey,
|
|
1744
|
+
useControlState,
|
|
1745
|
+
useDress,
|
|
1746
|
+
useKit,
|
|
1747
|
+
useLatch,
|
|
1748
|
+
usePress,
|
|
1749
|
+
useRotary,
|
|
1750
|
+
useTravel
|
|
572
1751
|
});
|
|
573
1752
|
//# sourceMappingURL=index.cjs.map
|