@5voltfx/design-system 0.2.0 → 0.3.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/dist/chunk-NJ2SBQYA.js +1208 -0
- package/dist/chunk-NJ2SBQYA.js.map +1 -0
- package/dist/chunk-UZW4E2UH.js +206 -0
- package/dist/chunk-UZW4E2UH.js.map +1 -0
- package/dist/{chunk-SUYKOWXJ.js → chunk-YLUQANAI.js} +33 -229
- package/dist/chunk-YLUQANAI.js.map +1 -0
- package/dist/components/DialNav/DialNav.d.ts +48 -0
- package/dist/components/DialNav/DialNav.d.ts.map +1 -0
- package/dist/components/DialNav/geometry.d.ts +159 -0
- package/dist/components/DialNav/geometry.d.ts.map +1 -0
- package/dist/components/DialNav/index.css +321 -0
- package/dist/components/DialNav/index.css.map +1 -0
- package/dist/components/DialNav/index.d.ts +2 -0
- package/dist/components/DialNav/index.d.ts.map +1 -0
- package/dist/components/DialNav/index.js +8 -0
- package/dist/components/DialNav/index.js.map +1 -0
- package/dist/components/DialNav/variants/Concentric.d.ts +10 -0
- package/dist/components/DialNav/variants/Concentric.d.ts.map +1 -0
- package/dist/components/DialNav/variants/RatchetFan.d.ts +12 -0
- package/dist/components/DialNav/variants/RatchetFan.d.ts.map +1 -0
- package/dist/components/DialNav/variants/Volvelle.d.ts +12 -0
- package/dist/components/DialNav/variants/Volvelle.d.ts.map +1 -0
- package/dist/components/SlideRuleNav/index.js +2 -1
- package/dist/index.css +321 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-SUYKOWXJ.js.map +0 -1
|
@@ -1,172 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useDetentTravel,
|
|
3
|
+
usePointerDrag,
|
|
4
|
+
useReducedMotion,
|
|
5
|
+
validateStationCount
|
|
6
|
+
} from "./chunk-UZW4E2UH.js";
|
|
7
|
+
|
|
1
8
|
// src/components/SlideRuleNav/SlideRuleNav.jsx
|
|
2
|
-
import { useCallback
|
|
9
|
+
import { useCallback, useEffect, useMemo as useMemo4, useRef as useRef4, useState } from "react";
|
|
3
10
|
import { NavLink } from "react-router-dom";
|
|
4
11
|
|
|
5
|
-
// src/components/_shared/useDetentTravel.js
|
|
6
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
7
|
-
|
|
8
|
-
// src/components/_shared/useReducedMotion.js
|
|
9
|
-
import { useSyncExternalStore } from "react";
|
|
10
|
-
var QUERY = "(prefers-reduced-motion: reduce)";
|
|
11
|
-
function subscribe(onChange) {
|
|
12
|
-
const mq = window.matchMedia(QUERY);
|
|
13
|
-
mq.addEventListener("change", onChange);
|
|
14
|
-
return () => mq.removeEventListener("change", onChange);
|
|
15
|
-
}
|
|
16
|
-
var getSnapshot = () => window.matchMedia(QUERY).matches;
|
|
17
|
-
var getServerSnapshot = () => false;
|
|
18
|
-
function useReducedMotion() {
|
|
19
|
-
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// src/components/_shared/useDetentTravel.js
|
|
23
|
-
function easeBack(x) {
|
|
24
|
-
const c1 = 1.02;
|
|
25
|
-
const c3 = c1 + 1;
|
|
26
|
-
return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
|
|
27
|
-
}
|
|
28
|
-
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
29
|
-
function useDetentTravel({
|
|
30
|
-
count,
|
|
31
|
-
value,
|
|
32
|
-
defaultValue = 0,
|
|
33
|
-
onChange,
|
|
34
|
-
subdivisions = 4,
|
|
35
|
-
settleMs = 420,
|
|
36
|
-
onDetentCross
|
|
37
|
-
} = {}) {
|
|
38
|
-
const last = Math.max(0, count - 1);
|
|
39
|
-
const isControlled = value != null;
|
|
40
|
-
const [internalStation, setInternalStation] = useState(
|
|
41
|
-
() => clamp(Math.round(defaultValue), 0, last)
|
|
42
|
-
);
|
|
43
|
-
const station = clamp(
|
|
44
|
-
Math.round(isControlled ? value : internalStation),
|
|
45
|
-
0,
|
|
46
|
-
last
|
|
47
|
-
);
|
|
48
|
-
const [pos, setPos] = useState(station);
|
|
49
|
-
const [dragging, setDragging] = useState(false);
|
|
50
|
-
const [settling, setSettling] = useState(false);
|
|
51
|
-
const reduced = useReducedMotion();
|
|
52
|
-
const posRef = useRef(pos);
|
|
53
|
-
const rafRef = useRef(0);
|
|
54
|
-
const targetRef = useRef(station);
|
|
55
|
-
const stationRef = useRef(station);
|
|
56
|
-
const detentRef = useRef(Math.round(station * subdivisions));
|
|
57
|
-
const onChangeRef = useRef(onChange);
|
|
58
|
-
const onDetentCrossRef = useRef(onDetentCross);
|
|
59
|
-
posRef.current = pos;
|
|
60
|
-
stationRef.current = station;
|
|
61
|
-
onChangeRef.current = onChange;
|
|
62
|
-
onDetentCrossRef.current = onDetentCross;
|
|
63
|
-
const cancel = useCallback(() => {
|
|
64
|
-
if (rafRef.current) {
|
|
65
|
-
cancelAnimationFrame(rafRef.current);
|
|
66
|
-
rafRef.current = 0;
|
|
67
|
-
}
|
|
68
|
-
}, []);
|
|
69
|
-
useEffect(() => cancel, [cancel]);
|
|
70
|
-
const writePos = useCallback((v) => {
|
|
71
|
-
posRef.current = v;
|
|
72
|
-
setPos(v);
|
|
73
|
-
}, []);
|
|
74
|
-
const animateTo = useCallback(
|
|
75
|
-
(target, ms, done) => {
|
|
76
|
-
cancel();
|
|
77
|
-
const from = posRef.current;
|
|
78
|
-
if (reduced || ms <= 0 || from === target) {
|
|
79
|
-
writePos(target);
|
|
80
|
-
setSettling(false);
|
|
81
|
-
done?.();
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
setSettling(true);
|
|
85
|
-
const t0 = performance.now();
|
|
86
|
-
const frame = (now) => {
|
|
87
|
-
const k = Math.min(1, (now - t0) / ms);
|
|
88
|
-
if (k < 1) {
|
|
89
|
-
writePos(from + (target - from) * easeBack(k));
|
|
90
|
-
rafRef.current = requestAnimationFrame(frame);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
rafRef.current = 0;
|
|
94
|
-
writePos(target);
|
|
95
|
-
setSettling(false);
|
|
96
|
-
done?.();
|
|
97
|
-
};
|
|
98
|
-
rafRef.current = requestAnimationFrame(frame);
|
|
99
|
-
},
|
|
100
|
-
[cancel, reduced, writePos]
|
|
101
|
-
);
|
|
102
|
-
const goTo = useCallback(
|
|
103
|
-
(i, ms = settleMs) => {
|
|
104
|
-
const next = clamp(Math.round(i), 0, last);
|
|
105
|
-
targetRef.current = next;
|
|
106
|
-
detentRef.current = Math.round(next * subdivisions);
|
|
107
|
-
animateTo(next, ms, () => {
|
|
108
|
-
if (next === stationRef.current) return;
|
|
109
|
-
if (!isControlled) setInternalStation(next);
|
|
110
|
-
onChangeRef.current?.(next);
|
|
111
|
-
});
|
|
112
|
-
},
|
|
113
|
-
[animateTo, isControlled, last, settleMs, subdivisions]
|
|
114
|
-
);
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
if (targetRef.current === station) return;
|
|
117
|
-
targetRef.current = station;
|
|
118
|
-
detentRef.current = Math.round(station * subdivisions);
|
|
119
|
-
animateTo(station, settleMs);
|
|
120
|
-
}, [station, animateTo, settleMs, subdivisions]);
|
|
121
|
-
const begin = useCallback(() => {
|
|
122
|
-
cancel();
|
|
123
|
-
setSettling(false);
|
|
124
|
-
setDragging(true);
|
|
125
|
-
}, [cancel]);
|
|
126
|
-
const track = useCallback(
|
|
127
|
-
(raw) => {
|
|
128
|
-
cancel();
|
|
129
|
-
const detent = Math.round(clamp(raw, 0, last) * subdivisions);
|
|
130
|
-
if (detent !== detentRef.current) {
|
|
131
|
-
detentRef.current = detent;
|
|
132
|
-
onDetentCrossRef.current?.();
|
|
133
|
-
}
|
|
134
|
-
writePos(detent / subdivisions);
|
|
135
|
-
},
|
|
136
|
-
[cancel, last, subdivisions, writePos]
|
|
137
|
-
);
|
|
138
|
-
const release = useCallback(() => {
|
|
139
|
-
setDragging(false);
|
|
140
|
-
goTo(Math.round(posRef.current));
|
|
141
|
-
}, [goTo]);
|
|
142
|
-
const step = useCallback(
|
|
143
|
-
(delta) => goTo(stationRef.current + delta),
|
|
144
|
-
[goTo]
|
|
145
|
-
);
|
|
146
|
-
return { pos, station, dragging, settling, goTo, step, track, begin, release };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// src/components/_shared/validateStationCount.js
|
|
150
|
-
var MIN_STATIONS = 2;
|
|
151
|
-
var MAX_STATIONS = 8;
|
|
152
|
-
var isDev = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
153
|
-
function validateStationCount(n, component) {
|
|
154
|
-
if (n > MAX_STATIONS) {
|
|
155
|
-
if (isDev) {
|
|
156
|
-
console.warn(
|
|
157
|
-
`[${component}] ${n} stations exceeds the supported maximum of ${MAX_STATIONS}; extra items are not rendered. Slide-rule navigations stay legible up to ${MAX_STATIONS} stations.`
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
return MAX_STATIONS;
|
|
161
|
-
}
|
|
162
|
-
if (n < MIN_STATIONS && isDev) {
|
|
163
|
-
console.warn(
|
|
164
|
-
`[${component}] needs at least ${MIN_STATIONS} stations; got ${n}.`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return n;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
12
|
// src/components/SlideRuleNav/geometry.js
|
|
171
13
|
var MINOR_TARGET = 38;
|
|
172
14
|
var LINEAR = Object.freeze({
|
|
@@ -244,7 +86,7 @@ var SLIDE_VALUES = Object.freeze({
|
|
|
244
86
|
8: Object.freeze([1, 1.5, 2, 2.5, 3, 4, 6, 8])
|
|
245
87
|
});
|
|
246
88
|
var pct = (v, extent) => `${v / extent * 100}%`;
|
|
247
|
-
var
|
|
89
|
+
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
248
90
|
function pitchFor(spec, count) {
|
|
249
91
|
return (spec.x1 - spec.x0) / Math.max(1, count - 1);
|
|
250
92
|
}
|
|
@@ -281,8 +123,8 @@ var logStops = (spec, count) => slideValues(count).map((v) => logX(spec, v));
|
|
|
281
123
|
var statorX = (spec, v) => spec.statorL0 + Math.log10(v) * spec.statorLw;
|
|
282
124
|
function localAt(stops, pos) {
|
|
283
125
|
const last = stops.length - 1;
|
|
284
|
-
const i =
|
|
285
|
-
const f =
|
|
126
|
+
const i = clamp(Math.floor(pos), 0, last - 1);
|
|
127
|
+
const f = clamp(pos - i, 0, 1);
|
|
286
128
|
return stops[i] + (stops[i + 1] - stops[i]) * f;
|
|
287
129
|
}
|
|
288
130
|
function posFromLocal(stops, want) {
|
|
@@ -298,52 +140,14 @@ function posFromLocal(stops, want) {
|
|
|
298
140
|
}
|
|
299
141
|
|
|
300
142
|
// src/components/SlideRuleNav/variants/CursorRule.jsx
|
|
301
|
-
import { forwardRef, useMemo, useRef
|
|
302
|
-
|
|
303
|
-
// src/components/_shared/usePointerDrag.js
|
|
304
|
-
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
305
|
-
function usePointerDrag(ref, { onBegin, onMove, onEnd, threshold = 3 } = {}) {
|
|
306
|
-
const latest = useRef2({ onBegin, onMove, onEnd, threshold });
|
|
307
|
-
latest.current = { onBegin, onMove, onEnd, threshold };
|
|
308
|
-
useEffect2(() => {
|
|
309
|
-
const node = ref.current;
|
|
310
|
-
if (!node) return void 0;
|
|
311
|
-
function onPointerDown(event) {
|
|
312
|
-
if (event.button) return;
|
|
313
|
-
const startX = event.clientX;
|
|
314
|
-
const startY = event.clientY;
|
|
315
|
-
let moved = false;
|
|
316
|
-
latest.current.onBegin?.(event);
|
|
317
|
-
function onPointerMove(moveEvent) {
|
|
318
|
-
if (!moved && Math.hypot(moveEvent.clientX - startX, moveEvent.clientY - startY) < latest.current.threshold) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
moved = true;
|
|
322
|
-
latest.current.onMove?.(moveEvent);
|
|
323
|
-
}
|
|
324
|
-
function onPointerUp(upEvent) {
|
|
325
|
-
window.removeEventListener("pointermove", onPointerMove);
|
|
326
|
-
window.removeEventListener("pointerup", onPointerUp);
|
|
327
|
-
window.removeEventListener("pointercancel", onPointerUp);
|
|
328
|
-
latest.current.onEnd?.(upEvent, !moved);
|
|
329
|
-
}
|
|
330
|
-
window.addEventListener("pointermove", onPointerMove);
|
|
331
|
-
window.addEventListener("pointerup", onPointerUp);
|
|
332
|
-
window.addEventListener("pointercancel", onPointerUp);
|
|
333
|
-
}
|
|
334
|
-
node.addEventListener("pointerdown", onPointerDown);
|
|
335
|
-
return () => node.removeEventListener("pointerdown", onPointerDown);
|
|
336
|
-
}, [ref]);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// src/components/SlideRuleNav/variants/CursorRule.jsx
|
|
143
|
+
import { forwardRef, useMemo, useRef } from "react";
|
|
340
144
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
341
145
|
var CursorRule = forwardRef(function CursorRule2({ spec, count, stations, travel, toViewBoxX }, ref) {
|
|
342
146
|
const [vbW, vbH] = spec.viewBox;
|
|
343
147
|
const { x0, railX0, railX1, top, bot, cursorBox } = spec;
|
|
344
148
|
const pitch = pitchFor(spec, count);
|
|
345
149
|
const { minorsPerPitch, minorsPerDetent } = minorDivisions(pitch);
|
|
346
|
-
const cursorRef =
|
|
150
|
+
const cursorRef = useRef(null);
|
|
347
151
|
usePointerDrag(cursorRef, {
|
|
348
152
|
onBegin: () => travel.begin(),
|
|
349
153
|
onMove: (event) => {
|
|
@@ -529,7 +333,7 @@ var CursorRule = forwardRef(function CursorRule2({ spec, count, stations, travel
|
|
|
529
333
|
});
|
|
530
334
|
|
|
531
335
|
// src/components/SlideRuleNav/variants/SlideStator.jsx
|
|
532
|
-
import { forwardRef as forwardRef2, useId, useMemo as useMemo2, useRef as
|
|
336
|
+
import { forwardRef as forwardRef2, useId, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
533
337
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
534
338
|
function decadeTicks(mapX) {
|
|
535
339
|
const bands = [
|
|
@@ -560,8 +364,8 @@ var SlideStator = forwardRef2(function SlideStator2({ spec, count, stations, tra
|
|
|
560
364
|
const stops = useMemo2(() => logStops(spec, count), [spec, count]);
|
|
561
365
|
const values = slideValues(count);
|
|
562
366
|
const offset = index - localAt(stops, travel.pos);
|
|
563
|
-
const grabbed =
|
|
564
|
-
const slideRef =
|
|
367
|
+
const grabbed = useRef2(0);
|
|
368
|
+
const slideRef = useRef2(null);
|
|
565
369
|
usePointerDrag(slideRef, {
|
|
566
370
|
onBegin: (event) => {
|
|
567
371
|
travel.begin();
|
|
@@ -803,7 +607,7 @@ var SlideStator = forwardRef2(function SlideStator2({ spec, count, stations, tra
|
|
|
803
607
|
});
|
|
804
608
|
|
|
805
609
|
// src/components/SlideRuleNav/variants/VernierCarriage.jsx
|
|
806
|
-
import { forwardRef as forwardRef3, useMemo as useMemo3, useRef as
|
|
610
|
+
import { forwardRef as forwardRef3, useMemo as useMemo3, useRef as useRef3 } from "react";
|
|
807
611
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
808
612
|
var VernierCarriage = forwardRef3(function VernierCarriage2({ spec, count, stations, travel, toViewBoxX }, ref) {
|
|
809
613
|
const [vbW, vbH] = spec.viewBox;
|
|
@@ -811,7 +615,7 @@ var VernierCarriage = forwardRef3(function VernierCarriage2({ spec, count, stati
|
|
|
811
615
|
const bot = railY + railH;
|
|
812
616
|
const pitch = pitchFor(spec, count);
|
|
813
617
|
const { minor, vdiv, minorsPerPitch, minorsPerDetent } = minorDivisions(pitch);
|
|
814
|
-
const carriageRef =
|
|
618
|
+
const carriageRef = useRef3(null);
|
|
815
619
|
usePointerDrag(carriageRef, {
|
|
816
620
|
onBegin: () => travel.begin(),
|
|
817
621
|
onMove: (event) => {
|
|
@@ -1025,9 +829,9 @@ var VARIANTS = {
|
|
|
1025
829
|
layout: fixedLayout
|
|
1026
830
|
}
|
|
1027
831
|
};
|
|
1028
|
-
var
|
|
832
|
+
var isDev = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
1029
833
|
function ActiveProbe({ isActive, index, onRouteActive, children }) {
|
|
1030
|
-
|
|
834
|
+
useEffect(() => {
|
|
1031
835
|
if (isActive) onRouteActive(index);
|
|
1032
836
|
}, [isActive, index, onRouteActive]);
|
|
1033
837
|
return children;
|
|
@@ -1042,14 +846,14 @@ function SlideRuleNav({
|
|
|
1042
846
|
label = "Main",
|
|
1043
847
|
className = ""
|
|
1044
848
|
}) {
|
|
1045
|
-
const resolved = VARIANTS[variant] ? variant : (
|
|
849
|
+
const resolved = VARIANTS[variant] ? variant : (isDev && console.warn(
|
|
1046
850
|
`[SlideRuleNav] variant "${variant}" is not implemented yet; falling back to "vernier".`
|
|
1047
851
|
), "vernier");
|
|
1048
852
|
const { spec, render: Chrome, layout } = VARIANTS[resolved];
|
|
1049
853
|
const count = validateStationCount(items.length, "SlideRuleNav");
|
|
1050
854
|
const stations = useMemo4(() => items.slice(0, count), [items, count]);
|
|
1051
855
|
const [vbW, vbH] = spec.viewBox;
|
|
1052
|
-
const indexOfId =
|
|
856
|
+
const indexOfId = useCallback(
|
|
1053
857
|
(id) => {
|
|
1054
858
|
const i = stations.findIndex((item) => item.id === id);
|
|
1055
859
|
return i < 0 ? null : i;
|
|
@@ -1057,15 +861,15 @@ function SlideRuleNav({
|
|
|
1057
861
|
[stations]
|
|
1058
862
|
);
|
|
1059
863
|
const routerMode = stations.some((item) => item.to);
|
|
1060
|
-
const [routeIndex, setRouteIndex] =
|
|
1061
|
-
const syncedOnce =
|
|
864
|
+
const [routeIndex, setRouteIndex] = useState(null);
|
|
865
|
+
const syncedOnce = useRef4(false);
|
|
1062
866
|
const controlledIndex = value != null ? indexOfId(value) : routerMode ? routeIndex : void 0;
|
|
1063
|
-
const chromeRef =
|
|
1064
|
-
const navRef =
|
|
1065
|
-
const linkRefs =
|
|
1066
|
-
const stationRefs =
|
|
867
|
+
const chromeRef = useRef4(null);
|
|
868
|
+
const navRef = useRef4(null);
|
|
869
|
+
const linkRefs = useRef4([]);
|
|
870
|
+
const stationRefs = useRef4([]);
|
|
1067
871
|
const reducedMotion = useReducedMotion();
|
|
1068
|
-
const handleCommit =
|
|
872
|
+
const handleCommit = useCallback(
|
|
1069
873
|
(index) => {
|
|
1070
874
|
const item = stations[index];
|
|
1071
875
|
if (!item) return;
|
|
@@ -1083,7 +887,7 @@ function SlideRuleNav({
|
|
|
1083
887
|
settleMs: spec.settleMs
|
|
1084
888
|
});
|
|
1085
889
|
const { goTo } = travel;
|
|
1086
|
-
const onRouteActive =
|
|
890
|
+
const onRouteActive = useCallback(
|
|
1087
891
|
(index) => {
|
|
1088
892
|
setRouteIndex(index);
|
|
1089
893
|
if (!syncedOnce.current) {
|
|
@@ -1093,7 +897,7 @@ function SlideRuleNav({
|
|
|
1093
897
|
},
|
|
1094
898
|
[goTo]
|
|
1095
899
|
);
|
|
1096
|
-
const toViewBoxX =
|
|
900
|
+
const toViewBoxX = useCallback(
|
|
1097
901
|
(clientX) => {
|
|
1098
902
|
const box = chromeRef.current?.getBoundingClientRect();
|
|
1099
903
|
if (!box?.width) return null;
|
|
@@ -1102,7 +906,7 @@ function SlideRuleNav({
|
|
|
1102
906
|
[vbW]
|
|
1103
907
|
);
|
|
1104
908
|
const active = travel.station;
|
|
1105
|
-
|
|
909
|
+
useEffect(() => {
|
|
1106
910
|
const nav = navRef.current;
|
|
1107
911
|
if (!nav) return void 0;
|
|
1108
912
|
const centre = (behavior) => {
|
|
@@ -1216,4 +1020,4 @@ function SlideRuleNav({
|
|
|
1216
1020
|
export {
|
|
1217
1021
|
SlideRuleNav
|
|
1218
1022
|
};
|
|
1219
|
-
//# sourceMappingURL=chunk-
|
|
1023
|
+
//# sourceMappingURL=chunk-YLUQANAI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/SlideRuleNav/SlideRuleNav.jsx","../src/components/SlideRuleNav/geometry.js","../src/components/SlideRuleNav/variants/CursorRule.jsx","../src/components/SlideRuleNav/variants/SlideStator.jsx","../src/components/SlideRuleNav/variants/VernierCarriage.jsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { NavLink } from \"react-router-dom\";\nimport { useDetentTravel } from \"../_shared/useDetentTravel.js\";\nimport { validateStationCount } from \"../_shared/validateStationCount.js\";\nimport { useReducedMotion } from \"../_shared/useReducedMotion.js\";\nimport { LINEAR, pitchFor, pct, logStops, localAt } from \"./geometry.js\";\nimport { CursorRule } from \"./variants/CursorRule.jsx\";\nimport { SlideStator } from \"./variants/SlideStator.jsx\";\nimport { VernierCarriage } from \"./variants/VernierCarriage.jsx\";\nimport \"./SlideRuleNav.css\";\n\n/**\n * @typedef {object} SlideRuleItem\n * @property {string} id\n * @property {import(\"react\").ReactNode} label\n * @property {string} [to] - Route path. Renders a NavLink; requires a Router.\n * @property {boolean} [end] - Passed to NavLink for exact matching.\n * @property {() => void} [onSelect] - Used when the item has no `to`.\n * @property {string} [value] - Scarlet scale numeral. Defaults to `i.toFixed(2)`.\n */\n\n/**\n * Where each station label sits, and how far the whole label rail is shifted.\n *\n * L-I and L-III hold a fixed scale and move a cursor over it, so the labels\n * never move. L-II inverts that: the index is fixed and the *slide* travels,\n * carrying its stations with it — so the labels ride the same offset the\n * engraved slide does.\n */\nconst fixedLayout = (spec, count) => ({\n stationX: (i) => spec.x0 + i * pitchFor(spec, count),\n offset: 0,\n});\n\nconst slideLayout = (spec, count, pos) => {\n const stops = logStops(spec, count);\n return {\n stationX: (i) => stops[i],\n offset: spec.index - localAt(stops, pos),\n };\n};\n\nconst VARIANTS = {\n cursor: { spec: LINEAR.cursor, render: CursorRule, layout: fixedLayout },\n slide: { spec: LINEAR.slide, render: SlideStator, layout: slideLayout },\n vernier: {\n spec: LINEAR.vernier,\n render: VernierCarriage,\n layout: fixedLayout,\n },\n};\n\nconst isDev =\n typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\";\n\n/**\n * Reports its own router-active state upward. NavLink already knows which link\n * matches the location, so the rule reads activeness from it rather than\n * calling useLocation — which would make a Router mandatory even for items\n * that only use `onSelect`.\n */\nfunction ActiveProbe({ isActive, index, onRouteActive, children }) {\n useEffect(() => {\n if (isActive) onRouteActive(index);\n }, [isActive, index, onRouteActive]);\n return children;\n}\n\n/**\n * Navigation built on a linear slide rule. Travel is quantized to quarter-tick\n * detents: the mechanism ratchets between stations while you drag it and\n * settles onto one when you let go.\n *\n * Items carrying `to` render as NavLinks and the router drives the cursor;\n * items without one render as buttons and the component tracks its own\n * selection. Both forms may be mixed.\n *\n * The engraved chrome is decorative and hidden from assistive technology — the\n * navigation itself is an ordinary list of links, fully operable by keyboard.\n *\n * @param {object} props\n * @param {\"cursor\"|\"slide\"|\"vernier\"} [props.variant]\n * @param {SlideRuleItem[]} props.items\n * @param {string} [props.value] - Selected item id (controlled).\n * @param {string} [props.defaultValue] - Initial item id (uncontrolled).\n * @param {(id: string) => void} [props.onChange]\n * @param {boolean} [props.readout] - Show the instrument readout strip.\n * @param {string} [props.label] - Accessible name for the nav landmark.\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function SlideRuleNav({\n variant = \"vernier\",\n items = [],\n value,\n defaultValue,\n onChange,\n readout = false,\n label = \"Main\",\n className = \"\",\n}) {\n const resolved = VARIANTS[variant]\n ? variant\n : (isDev &&\n console.warn(\n `[SlideRuleNav] variant \"${variant}\" is not implemented yet; ` +\n `falling back to \"vernier\".`\n ),\n \"vernier\");\n const { spec, render: Chrome, layout } = VARIANTS[resolved];\n\n const count = validateStationCount(items.length, \"SlideRuleNav\");\n const stations = useMemo(() => items.slice(0, count), [items, count]);\n const [vbW, vbH] = spec.viewBox;\n\n const indexOfId = useCallback(\n (id) => {\n const i = stations.findIndex((item) => item.id === id);\n return i < 0 ? null : i;\n },\n [stations]\n );\n\n // In router mode the router is the controller: releasing the carriage clicks\n // the target link, the location changes, and the travel follows.\n const routerMode = stations.some((item) => item.to);\n const [routeIndex, setRouteIndex] = useState(null);\n const syncedOnce = useRef(false);\n\n const controlledIndex =\n value != null ? indexOfId(value) : routerMode ? routeIndex : undefined;\n\n const chromeRef = useRef(null);\n const navRef = useRef(null);\n const linkRefs = useRef([]);\n const stationRefs = useRef([]);\n const reducedMotion = useReducedMotion();\n\n const handleCommit = useCallback(\n (index) => {\n const item = stations[index];\n if (!item) return;\n if (item.to && linkRefs.current[index]) linkRefs.current[index].click();\n item.onSelect?.();\n onChange?.(item.id);\n },\n [onChange, stations]\n );\n\n const travel = useDetentTravel({\n count,\n value: controlledIndex ?? undefined,\n defaultValue: indexOfId(defaultValue) ?? 0,\n onChange: handleCommit,\n settleMs: spec.settleMs,\n });\n\n const { goTo } = travel;\n const onRouteActive = useCallback(\n (index) => {\n setRouteIndex(index);\n // Land on the current route without a settle animation on first paint.\n if (!syncedOnce.current) {\n syncedOnce.current = true;\n goTo(index, 0);\n }\n },\n [goTo]\n );\n\n /**\n * Pointer x (client space) to a viewBox x. Each variant turns that into\n * travel itself, because a fixed scale and a moving slide read it\n * differently.\n */\n const toViewBoxX = useCallback(\n (clientX) => {\n const box = chromeRef.current?.getBoundingClientRect();\n if (!box?.width) return null;\n return ((clientX - box.left) / box.width) * vbW;\n },\n [vbW]\n );\n\n const active = travel.station;\n\n // A rule too long for the viewport scrolls; keep the selected station in\n // view so the carriage never settles off-screen. Scrolls the nav's own\n // container, never the page.\n useEffect(() => {\n const nav = navRef.current;\n if (!nav) return undefined;\n\n const centre = (behavior) => {\n const station = stationRefs.current[active];\n if (!station || nav.scrollWidth <= nav.clientWidth) return;\n nav.scrollTo({\n left: Math.max(0, station.offsetLeft - nav.clientWidth / 2),\n behavior,\n });\n };\n\n centre(reducedMotion ? \"auto\" : \"smooth\");\n\n // The rule may start out fitting and only overflow once the viewport\n // narrows — on rotation, say — so re-centre when the container resizes.\n if (typeof ResizeObserver === \"undefined\") return undefined;\n const observer = new ResizeObserver(() => centre(\"auto\"));\n observer.observe(nav);\n return () => observer.disconnect();\n }, [active, reducedMotion]);\n\n function onKeyDown(event) {\n if (event.key === \"ArrowRight\") travel.step(1);\n else if (event.key === \"ArrowLeft\") travel.step(-1);\n else if (event.key === \"Home\") travel.goTo(0);\n else if (event.key === \"End\") travel.goTo(count - 1);\n else return;\n event.preventDefault();\n }\n\n const place = layout(spec, count, travel.pos);\n const linkClass = (isActive) =>\n \"ds-rule__link\" + (isActive ? \" ds-rule__link--active\" : \"\");\n\n return (\n <nav\n ref={navRef}\n className={[\"ds-rule\", `ds-rule--${resolved}`, className]\n .filter(Boolean)\n .join(\" \")}\n aria-label={label}\n onKeyDown={onKeyDown}\n >\n <div className=\"ds-rule__stage\">\n <Chrome\n ref={chromeRef}\n spec={spec}\n count={count}\n stations={stations}\n travel={travel}\n toViewBoxX={toViewBoxX}\n />\n\n <ul\n className=\"ds-rule__stations\"\n style={{ transform: `translateX(${pct(place.offset, vbW)})` }}\n >\n {stations.map((item, index) => (\n <li\n key={item.id}\n ref={(el) => (stationRefs.current[index] = el)}\n className=\"ds-rule__station\"\n style={{\n \"--station-x\": pct(place.stationX(index), vbW),\n \"--station-y\": pct(spec.labelY, vbH),\n }}\n >\n {item.to ? (\n <NavLink\n to={item.to}\n end={item.end}\n ref={(el) => (linkRefs.current[index] = el)}\n className={({ isActive }) => linkClass(isActive)}\n >\n {({ isActive }) => (\n <ActiveProbe\n isActive={isActive}\n index={index}\n onRouteActive={onRouteActive}\n >\n {item.label}\n </ActiveProbe>\n )}\n </NavLink>\n ) : (\n <button\n type=\"button\"\n className={linkClass(index === active)}\n aria-current={index === active ? \"true\" : undefined}\n onClick={() => travel.goTo(index)}\n >\n {item.label}\n </button>\n )}\n </li>\n ))}\n </ul>\n </div>\n\n {readout && (\n <div className=\"ds-rule__readout\">\n <span aria-hidden=\"true\">\n <span className=\"ds-rule__readout-key\">Carriage</span>{\" \"}\n <span className=\"ds-rule__readout-val\">\n {travel.dragging ? \"——\" : stations[active]?.label}\n </span>{\" \"}\n <span className=\"ds-rule__readout-key\">Vernier</span>{\" \"}\n <span className=\"ds-rule__readout-num\">\n {travel.pos.toFixed(2)}\n </span>\n </span>\n {/* Announce only committed changes, never every drag frame. */}\n <span className=\"ds-rule__sr-only\" aria-live=\"polite\">\n {travel.dragging || travel.settling\n ? \"\"\n : `${stations[active]?.label ?? \"\"} selected`}\n </span>\n </div>\n )}\n </nav>\n );\n}\n","/*\n Frozen geometry for the linear plates (L-I, L-II, L-III).\n\n These numbers come from the design artboards and were tuned against real\n clipping bounds in a browser. Treat the *span* as constant and derive pitch\n from the station count — the artboards were drawn with 6 stations, but the\n first integration (5voltfx.com, L-III) has 3.\n*/\n\n/** Preferred main-scale minor division, in viewBox units. */\nexport const MINOR_TARGET = 38;\n\nexport const LINEAR = Object.freeze({\n /**\n * L-I — fixed scale, cursor travels.\n *\n * The stock runs from `x0 - cursorBox.w / 2` to `x1 + cursorBox.w / 2` for\n * the same reason as L-III: the cursor is centred on the station it reads,\n * so the stock has to extend past the end graduations for it to sit on the\n * instrument at station 0 and at the last station.\n */\n cursor: Object.freeze({\n viewBox: Object.freeze([900, 136]),\n x0: 62,\n x1: 838,\n railX0: 30,\n railX1: 870,\n top: 40,\n bot: 112,\n labelY: 24,\n cursorBox: Object.freeze({ w: 60, h: 100, y: 28 }),\n settleMs: 420,\n }),\n /** L-II — fixed index, slide travels, true log10 station spacing. */\n slide: Object.freeze({\n viewBox: Object.freeze([900, 150]),\n l0: 60,\n lw: 1400,\n index: 450,\n clip: Object.freeze([30, 870]),\n statorL0: 40,\n statorLw: 800,\n slideY: 46,\n slideH: 58,\n labelY: 88,\n settleMs: 460,\n }),\n /**\n * L-III — notched rail, vernier carriage. Reference plate.\n *\n * The station span is 772 units, exactly as drawn in the artboards, but it\n * is inset 124 units from the left rather than 64. The carriage is centred\n * on the station it reads, and its widest form (5 stations) is 224 units\n * across — so at station 0 a 64-unit inset put half the vernier off the end\n * of the rule. The artboards never showed it because they opened on station\n * 2. Keeping the span identical preserves every derived division.\n *\n * The rail then runs from `x0 - maxCarriageHalf` to `x1 + maxCarriageHalf`,\n * because a real stock extends past its scale far enough for the cursor to\n * reach the end graduations without hanging off the instrument.\n */\n vernier: Object.freeze({\n viewBox: Object.freeze([1020, 160]),\n x0: 124,\n x1: 896,\n railX0: 12,\n railX1: 1008,\n railY: 46,\n railH: 42,\n labelY: 26,\n vernierDivisions: 5,\n mainDivisionsSpanned: 4,\n /** Widest half-carriage across all supported counts; the inset floor. */\n maxCarriageHalf: 112,\n settleMs: 380,\n }),\n});\n\n/**\n * Station scale values for L-II, per station count.\n *\n * The point of L-II is that the gaps are *uneven* — stations sit at true\n * log10 positions, so travel compresses toward the right of the rule. Spacing\n * values evenly in log space would make the gaps equal and collapse L-II into\n * L-I, so each count gets a hand-picked set of slide-rule gradations. The\n * six-station row is the artboards', unchanged.\n */\nexport const SLIDE_VALUES = Object.freeze({\n 2: Object.freeze([1, 8]),\n 3: Object.freeze([1, 2, 8]),\n 4: Object.freeze([1, 2, 3, 8]),\n 5: Object.freeze([1, 1.5, 2, 3, 8]),\n 6: Object.freeze([1, 1.5, 2, 3, 5, 8]),\n 7: Object.freeze([1, 1.5, 2, 3, 4, 6, 8]),\n 8: Object.freeze([1, 1.5, 2, 2.5, 3, 4, 6, 8]),\n});\n\n/** CSS percentage for a viewBox coordinate, for HTML overlaid on the SVG. */\nexport const pct = (v, extent) => `${(v / extent) * 100}%`;\n\nconst clamp = (v, lo, hi) => (v < lo ? lo : v > hi ? hi : v);\n\n/**\n * Distance between adjacent stations, in viewBox units.\n *\n * @param {{x0: number, x1: number}} spec\n * @param {number} count\n * @returns {number}\n */\nexport function pitchFor(spec, count) {\n return (spec.x1 - spec.x0) / Math.max(1, count - 1);\n}\n\n/** Travel position (0…count-1) to a viewBox x. */\nexport const travelX = (spec, count, pos) =>\n spec.x0 + pos * pitchFor(spec, count);\n\n/**\n * How many main-scale minor divisions fill one station gap.\n *\n * Always a multiple of 4, so a quarter-pitch detent is always a whole number\n * of minor divisions and the vernier's zero line coincides exactly when the\n * pawl seats. Without this the carriage scales with pitch: at 3 stations a\n * naive `pitch / 4` gives a vernier half the width of the rule.\n *\n * @param {number} pitch\n * @returns {{minorsPerPitch: number, minor: number, vdiv: number,\n * minorsPerDetent: number}}\n */\nexport function minorDivisions(pitch) {\n const minorsPerPitch =\n 4 * Math.max(1, Math.round(pitch / (4 * MINOR_TARGET)));\n const minor = pitch / minorsPerPitch;\n return {\n minorsPerPitch,\n minor,\n vdiv: (minor * 4) / 5,\n minorsPerDetent: minorsPerPitch / 4,\n };\n}\n\n/**\n * Which vernier line currently coincides with a main-scale line — the whole\n * trick of L-III. Five carriage divisions span four rail divisions, so at any\n * offset exactly one line pair lines up, and which one gives the fraction.\n *\n * @param {object} options\n * @param {number} options.pos - Travel, 0…count-1.\n * @param {number} options.count\n * @param {{x0: number, x1: number}} [options.spec]\n * @returns {{k: number, d: number, lit: boolean}} Coinciding line index, its\n * distance from the main line, and whether it is close enough to light.\n */\nexport function coincidence({ pos, count, spec = LINEAR.vernier }) {\n const pitch = pitchFor(spec, count);\n const { minor, vdiv } = minorDivisions(pitch);\n const cx = spec.x0 + pos * pitch;\n\n // Six lines are drawn but only five are readable: the closing line sits\n // exactly four minor divisions from the zero line, so both always coincide\n // together. Searching 0…4 keeps the reading unambiguous, and the epsilon\n // stops floating-point noise from picking a later line on an exact tie.\n let k = 0;\n let d = Infinity;\n for (let i = 0; i < LINEAR.vernier.vernierDivisions; i++) {\n const vx = cx + (i - 2.5) * vdiv;\n const off = vx - spec.x0;\n const dist = Math.abs(off - Math.round(off / minor) * minor);\n if (dist < d - 1e-9) {\n d = dist;\n k = i;\n }\n }\n return { k, d, lit: d < vdiv * 0.34 };\n}\n\n/* ── L-II: true log10 station spacing ─────────────────────────────────── */\n\n/** Slide-local x for a scale value. */\nexport const logX = (spec, v) => spec.l0 + Math.log10(v) * spec.lw;\n\n/** The scale values a slide of `count` stations is engraved with. */\nexport const slideValues = (count) =>\n SLIDE_VALUES[count] ?? SLIDE_VALUES[6];\n\n/** Slide-local x of every station. */\nexport const logStops = (spec, count) =>\n slideValues(count).map((v) => logX(spec, v));\n\n/** Static stator scale x — the whole decade, sized to the window. */\nexport const statorX = (spec, v) =>\n spec.statorL0 + Math.log10(v) * spec.statorLw;\n\n/**\n * Slide-local x for continuous travel. Linear *between* stops, so each gap's\n * quarter-detents inherit that gap's own width — wide at the left of the rule,\n * tight at the right. The unevenness is the point.\n *\n * @param {number[]} stops\n * @param {number} pos\n * @returns {number}\n */\nexport function localAt(stops, pos) {\n const last = stops.length - 1;\n const i = clamp(Math.floor(pos), 0, last - 1);\n const f = clamp(pos - i, 0, 1);\n return stops[i] + (stops[i + 1] - stops[i]) * f;\n}\n\n/**\n * Inverse of {@link localAt} — needed to turn a pointer position back into\n * travel while dragging the slide.\n *\n * @param {number[]} stops\n * @param {number} want - Slide-local x.\n * @returns {number}\n */\nexport function posFromLocal(stops, want) {\n const last = stops.length - 1;\n if (want <= stops[0]) return 0;\n if (want >= stops[last]) return last;\n for (let i = 0; i < last; i++) {\n if (want <= stops[i + 1]) {\n return i + (want - stops[i]) / (stops[i + 1] - stops[i]);\n }\n }\n return last;\n}\n","import { forwardRef, useMemo, useRef } from \"react\";\nimport { usePointerDrag } from \"../../_shared/usePointerDrag.js\";\nimport { minorDivisions, pitchFor } from \"../geometry.js\";\n\n/**\n * L-I — Cursor Rule.\n *\n * The scale is fixed and the cursor travels: a glass runner with a scarlet\n * hairline, brass shoes at each end. Mechanically the simplest of the three\n * linear plates, and a strict subset of L-III.\n *\n * Decorative only — the navigation is the HTML list the parent renders above.\n */\nexport const CursorRule = forwardRef(function CursorRule(\n { spec, count, stations, travel, toViewBoxX },\n ref\n) {\n const [vbW, vbH] = spec.viewBox;\n const { x0, railX0, railX1, top, bot, cursorBox } = spec;\n const pitch = pitchFor(spec, count);\n const { minorsPerPitch, minorsPerDetent } = minorDivisions(pitch);\n\n const cursorRef = useRef(null);\n usePointerDrag(cursorRef, {\n onBegin: () => travel.begin(),\n onMove: (event) => {\n const x = toViewBoxX(event.clientX);\n if (x != null) travel.track((x - x0) / pitch);\n },\n onEnd: (_event, wasTap) => {\n if (!wasTap) travel.release();\n },\n });\n\n // Tick density follows L-III's minor divisions, so a three-station rule is\n // engraved as finely as a six-station one instead of showing three lonely\n // quarter marks per gap.\n const ticks = useMemo(() => {\n const out = [];\n for (let q = 0; q <= (count - 1) * minorsPerPitch; q++) {\n const isStation = q % minorsPerPitch === 0;\n const isDetent = q % minorsPerDetent === 0;\n out.push({\n x: x0 + (q / minorsPerPitch) * pitch,\n kind: isStation ? \"major\" : isDetent ? \"half\" : \"quarter\",\n down: isStation ? 22 : isDetent ? 13 : 8,\n up: isStation ? 18 : isDetent ? 11 : 7,\n station: isStation ? q / minorsPerPitch : null,\n });\n }\n return out;\n }, [count, minorsPerDetent, minorsPerPitch, pitch, x0]);\n\n const cursorX = x0 + travel.pos * pitch;\n const half = cursorBox.w / 2;\n const cursorTop = cursorBox.y;\n const cursorBottom = cursorBox.y + cursorBox.h;\n\n return (\n <svg\n ref={ref}\n className=\"ds-rule__chrome\"\n viewBox={`0 0 ${vbW} ${vbH}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <rect\n className=\"ds-rule__rail\"\n x={railX0}\n y={top}\n width={railX1 - railX0}\n height={bot - top}\n rx={2}\n />\n <line\n className=\"ds-rule__tick ds-rule__tick--quarter\"\n x1={railX0}\n y1={(top + bot) / 2}\n x2={railX1}\n y2={(top + bot) / 2}\n />\n {[\n [railX0 + 10, top + 10],\n [railX1 - 10, top + 10],\n [railX0 + 10, bot - 10],\n [railX1 - 10, bot - 10],\n ].map(([cx, cy], i) => (\n <circle key={i} className=\"ds-rule__rivet\" cx={cx} cy={cy} r={3} />\n ))}\n\n {ticks.map((tick, i) => (\n <g key={i}>\n <line\n className={`ds-rule__tick ds-rule__tick--${tick.kind}`}\n x1={tick.x}\n y1={top}\n x2={tick.x}\n y2={top + tick.down}\n />\n <line\n className={`ds-rule__tick ds-rule__tick--${tick.kind}`}\n x1={tick.x}\n y1={bot}\n x2={tick.x}\n y2={bot - tick.up}\n />\n </g>\n ))}\n\n {/* Two number rows, one meaning each: the station's own scale value\n above, and the travel position below. */}\n {ticks\n .filter((tick) => tick.station != null)\n .map((tick) => (\n <g key={`n${tick.station}`}>\n <text\n className=\"ds-rule__numeral ds-rule__numeral--accent\"\n x={tick.x}\n y={top + 34}\n textAnchor=\"middle\"\n >\n {stations[tick.station]?.value ?? tick.station.toFixed(2)}\n </text>\n <text\n className=\"ds-rule__numeral\"\n x={tick.x}\n y={bot - 22}\n textAnchor=\"middle\"\n >\n {tick.station.toFixed(2)}\n </text>\n </g>\n ))}\n\n <g\n ref={cursorRef}\n className=\"ds-rule__carriage\"\n transform={`translate(${cursorX},0)`}\n >\n <rect\n className=\"ds-rule__glass\"\n x={-half}\n y={cursorTop}\n width={cursorBox.w}\n height={cursorBox.h}\n rx={2}\n />\n <rect\n className=\"ds-rule__frame\"\n x={-half}\n y={cursorTop}\n width={cursorBox.w}\n height={cursorBox.h}\n rx={3}\n />\n <rect\n className=\"ds-rule__shoe\"\n x={-half - 2}\n y={cursorTop}\n width={cursorBox.w + 4}\n height={7}\n rx={2}\n />\n <rect\n className=\"ds-rule__shoe\"\n x={-half - 2}\n y={cursorBottom - 7}\n width={cursorBox.w + 4}\n height={7}\n rx={2}\n />\n {[\n [-24, cursorTop + 3.5],\n [24, cursorTop + 3.5],\n [-24, cursorBottom - 3.5],\n [24, cursorBottom - 3.5],\n ].map(([cx, cy], i) => (\n <circle key={i} className=\"ds-rule__rivet\" cx={cx} cy={cy} r={2.2} />\n ))}\n <line\n className=\"ds-rule__hairline\"\n x1={0}\n y1={cursorTop}\n x2={0}\n y2={cursorBottom}\n />\n </g>\n </svg>\n );\n});\n","import { forwardRef, useId, useMemo, useRef } from \"react\";\nimport { usePointerDrag } from \"../../_shared/usePointerDrag.js\";\nimport {\n logStops,\n localAt,\n posFromLocal,\n slideValues,\n statorX,\n} from \"../geometry.js\";\n\n/** Ticks for a full log10 decade, at slide-rule gradations. */\nfunction decadeTicks(mapX) {\n const bands = [\n [1, 2, 0.02, 0.1],\n [2, 5, 0.05, 0.5],\n [5, 10, 0.1, 1],\n ];\n const out = [];\n for (const [a, b, step, major] of bands) {\n for (let v = a; v < b + 1e-9; v += step) {\n const isMajor = Math.abs(v / major - Math.round(v / major)) < 1e-6;\n const half = Math.abs(v / (major / 2) - Math.round(v / (major / 2))) < 1e-6;\n out.push({\n x: mapX(v),\n v,\n len: isMajor ? 12 : half ? 8 : 5,\n kind: isMajor ? \"major\" : half ? \"half\" : \"quarter\",\n label: isMajor ? String(Math.round(v * 10) / 10) : null,\n });\n }\n }\n return out;\n}\n\n/**\n * L-II — Slide & Stator.\n *\n * Inverted from L-I: the index stays put and the slide travels beneath it.\n * Stations sit at true log10 positions, so the gaps compress toward the right\n * of the rule and each quarter-detent inherits that compression — the\n * mechanism genuinely tightens at the far end. The unevenness is the point.\n *\n * Decorative only — the navigation is the HTML list the parent renders above,\n * which rides the same offset this slide does.\n */\nexport const SlideStator = forwardRef(function SlideStator(\n { spec, count, stations, travel, toViewBoxX },\n ref\n) {\n const [vbW, vbH] = spec.viewBox;\n const { index, slideY, slideH, clip } = spec;\n const clipId = useId();\n\n const stops = useMemo(() => logStops(spec, count), [spec, count]);\n const values = slideValues(count);\n const offset = index - localAt(stops, travel.pos);\n\n const grabbed = useRef(0);\n const slideRef = useRef(null);\n usePointerDrag(slideRef, {\n onBegin: (event) => {\n travel.begin();\n const x = toViewBoxX(event.clientX);\n grabbed.current = localAt(stops, travel.pos) + (x == null ? 0 : x - index);\n },\n onMove: (event) => {\n const x = toViewBoxX(event.clientX);\n if (x == null) return;\n travel.track(posFromLocal(stops, grabbed.current - (x - index)));\n },\n onEnd: (_event, wasTap) => {\n if (!wasTap) travel.release();\n },\n });\n\n const statorTicks = useMemo(\n () => decadeTicks((v) => statorX(spec, v)),\n [spec]\n );\n\n // Quarter-detent marks on the slide, unevenly spaced by construction.\n const detents = useMemo(() => {\n const out = [];\n for (let q = 0; q <= (count - 1) * 4; q++) {\n if (q % 4 === 0) continue;\n out.push({ x: localAt(stops, q / 4), half: q % 2 === 0 });\n }\n return out;\n }, [count, stops]);\n\n const slideBottom = slideY + slideH;\n\n return (\n <svg\n ref={ref}\n className=\"ds-rule__chrome\"\n viewBox={`0 0 ${vbW} ${vbH}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <defs>\n <clipPath id={clipId}>\n <rect x={clip[0]} y={0} width={clip[1] - clip[0]} height={vbH} />\n </clipPath>\n </defs>\n\n {/* Fixed stators, above and below the slide. */}\n <rect\n className=\"ds-rule__rail\"\n x={clip[0]}\n y={6}\n width={clip[1] - clip[0]}\n height={40}\n />\n <rect\n className=\"ds-rule__rail\"\n x={clip[0]}\n y={slideBottom}\n width={clip[1] - clip[0]}\n height={40}\n />\n {statorTicks.map((tick, i) => (\n <g key={i}>\n <line\n className={`ds-rule__tick ds-rule__tick--${tick.kind}`}\n x1={tick.x}\n y1={46}\n x2={tick.x}\n y2={46 - tick.len}\n />\n <line\n className={`ds-rule__tick ds-rule__tick--${tick.kind}`}\n x1={tick.x}\n y1={slideBottom}\n x2={tick.x}\n y2={slideBottom + tick.len}\n />\n {tick.label && (\n <>\n <text\n className=\"ds-rule__numeral\"\n x={tick.x}\n y={46 - tick.len - 3}\n textAnchor=\"middle\"\n >\n {tick.label}\n </text>\n <text\n className=\"ds-rule__numeral\"\n x={tick.x}\n y={slideBottom + tick.len + 9}\n textAnchor=\"middle\"\n >\n {tick.label}\n </text>\n </>\n )}\n </g>\n ))}\n <text className=\"ds-rule__caption\" x={clip[0] + 6} y={44}>\n C\n </text>\n <text className=\"ds-rule__caption\" x={clip[0] + 6} y={slideBottom + 12}>\n D\n </text>\n\n {/* The slide itself, clipped to the window. */}\n <g clipPath={`url(#${clipId})`}>\n <g\n ref={slideRef}\n className=\"ds-rule__carriage\"\n transform={`translate(${offset},0)`}\n >\n <rect\n className=\"ds-rule__slide\"\n x={stops[0] - 400}\n y={slideY}\n width={stops[stops.length - 1] - stops[0] + 800}\n height={slideH}\n />\n {detents.map((d, i) => (\n <g key={i}>\n <line\n className=\"ds-rule__tick ds-rule__tick--quarter\"\n x1={d.x}\n y1={slideY}\n x2={d.x}\n y2={slideY + (d.half ? 10 : 6)}\n />\n <line\n className=\"ds-rule__tick ds-rule__tick--quarter\"\n x1={d.x}\n y1={slideBottom}\n x2={d.x}\n y2={slideBottom - (d.half ? 10 : 6)}\n />\n </g>\n ))}\n {stops.map((x, i) => (\n <g key={i}>\n <line\n className=\"ds-rule__tick ds-rule__tick--major\"\n x1={x}\n y1={slideY}\n x2={x}\n y2={slideY + 14}\n />\n <line\n className=\"ds-rule__tick ds-rule__tick--major\"\n x1={x}\n y1={slideBottom}\n x2={x}\n y2={slideBottom - 14}\n />\n <text\n className=\"ds-rule__numeral ds-rule__numeral--accent\"\n x={x}\n y={slideBottom - 4}\n textAnchor=\"middle\"\n >\n {stations[i]?.value ?? values[i].toFixed(2)}\n </text>\n </g>\n ))}\n </g>\n </g>\n\n {/* Fixed index and glass window. */}\n <g className=\"ds-rule__window\">\n <rect\n className=\"ds-rule__glass\"\n x={index - 58}\n y={2}\n width={116}\n height={vbH - 4}\n rx={2}\n />\n <rect\n className=\"ds-rule__frame\"\n x={index - 58}\n y={2}\n width={116}\n height={vbH - 4}\n rx={3}\n />\n <line\n className=\"ds-rule__hairline\"\n x1={index}\n y1={2}\n x2={index}\n y2={vbH - 2}\n />\n <path\n className=\"ds-rule__index\"\n d={`M${index - 7},2 L${index + 7},2 L${index},13 Z`}\n />\n <path\n className=\"ds-rule__index\"\n d={`M${index - 7},${vbH - 2} L${index + 7},${vbH - 2} L${index},${\n vbH - 13\n } Z`}\n />\n <text\n className=\"ds-rule__caption ds-rule__caption--accent\"\n x={index + 64}\n y={12}\n >\n INDEX\n </text>\n </g>\n </svg>\n );\n});\n","import { forwardRef, useMemo, useRef } from \"react\";\nimport { usePointerDrag } from \"../../_shared/usePointerDrag.js\";\nimport { minorDivisions, pitchFor, coincidence } from \"../geometry.js\";\n\n/**\n * L-III — Vernier Carriage.\n *\n * A notched rail with a sprung pawl and a working vernier: five carriage\n * divisions span four rail divisions, so at any offset exactly one line pair\n * coincides and its index gives the fraction. Release and the pawl seats, the\n * zero lines coincide, and the reading goes whole.\n *\n * Everything here is decorative and hidden from assistive technology; the\n * navigation is the HTML list the parent renders over it.\n */\nexport const VernierCarriage = forwardRef(function VernierCarriage(\n { spec, count, stations, travel, toViewBoxX },\n ref\n) {\n const [vbW, vbH] = spec.viewBox;\n const { x0, railX0, railX1, railY, railH } = spec;\n const bot = railY + railH;\n const pitch = pitchFor(spec, count);\n const { minor, vdiv, minorsPerPitch, minorsPerDetent } =\n minorDivisions(pitch);\n\n const carriageRef = useRef(null);\n usePointerDrag(carriageRef, {\n onBegin: () => travel.begin(),\n onMove: (event) => {\n const x = toViewBoxX(event.clientX);\n if (x != null) travel.track((x - x0) / pitch);\n },\n onEnd: (_event, wasTap) => {\n if (!wasTap) travel.release();\n },\n });\n\n const totalMinors = (count - 1) * minorsPerPitch;\n\n /* Rail outline: the notches are cut geometry, so they survive the fill. */\n const rail = useMemo(() => {\n let d = `M${railX0},${bot} L${railX0},${railY} `;\n for (let q = 0; q <= totalMinors; q++) {\n const tx = x0 + (q / minorsPerPitch) * pitch;\n const deep = q % minorsPerDetent === 0;\n const w = deep ? 7 : 4;\n const dep = deep ? 9 : 5;\n d += `L${tx - w},${railY} L${tx},${railY + dep} L${tx + w},${railY} `;\n }\n return `${d}L${railX1},${railY} L${railX1},${bot} Z`;\n }, [bot, minorsPerDetent, minorsPerPitch, pitch, railX0, railX1, railY, totalMinors, x0]);\n\n const ticks = useMemo(() => {\n const out = [];\n for (let q = 0; q <= totalMinors; q++) {\n const tx = x0 + (q / minorsPerPitch) * pitch;\n const isStation = q % minorsPerPitch === 0;\n const isDetent = q % minorsPerDetent === 0;\n out.push({\n x: tx,\n len: isStation ? 20 : isDetent ? 13 : 8,\n kind: isStation ? \"major\" : isDetent ? \"half\" : \"quarter\",\n station: isStation ? q / minorsPerPitch : null,\n });\n }\n return out;\n }, [minorsPerDetent, minorsPerPitch, pitch, totalMinors, x0]);\n\n const { pos } = travel;\n const carriageX = x0 + pos * pitch;\n const lit = coincidence({ pos, count, spec });\n\n // The pawl rides up out of its notch between detents and drops back in.\n const frac = Math.abs(pos * minorsPerPitch - Math.round(pos * minorsPerPitch));\n const pawlY = railY + 2 - frac * 13;\n\n const glassW = vdiv * 5.8;\n const captionLeft = carriageX + glassW / 2 + 60 > railX1;\n\n return (\n <svg\n ref={ref}\n className=\"ds-rule__chrome\"\n viewBox={`0 0 ${vbW} ${vbH}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path className=\"ds-rule__rail\" d={rail} />\n <circle className=\"ds-rule__rivet\" cx={railX0 + 10} cy={bot - 8} r={3} />\n <circle className=\"ds-rule__rivet\" cx={railX1 - 10} cy={bot - 8} r={3} />\n\n {ticks.map((tick, i) => (\n <line\n key={i}\n className={`ds-rule__tick ds-rule__tick--${tick.kind}`}\n x1={tick.x}\n y1={bot}\n x2={tick.x}\n y2={bot - tick.len}\n />\n ))}\n\n {ticks\n .filter((tick) => tick.station != null)\n .map((tick) => (\n <text\n key={`n${tick.station}`}\n className=\"ds-rule__numeral ds-rule__numeral--accent\"\n x={tick.x}\n y={bot - 24}\n textAnchor=\"middle\"\n >\n {stations[tick.station]?.value ?? tick.station.toFixed(2)}\n </text>\n ))}\n\n <text className=\"ds-rule__caption\" x={railX0 + 6} y={railY - 8}>\n MAIN\n </text>\n\n {/* Leader ticks up to the HTML station labels. */}\n {ticks\n .filter((tick) => tick.station != null)\n .map((tick) => (\n <line\n key={`l${tick.station}`}\n className=\"ds-rule__tick ds-rule__tick--quarter\"\n x1={tick.x}\n y1={railY - 16}\n x2={tick.x}\n y2={railY - 9}\n />\n ))}\n\n <g\n ref={carriageRef}\n className=\"ds-rule__carriage\"\n transform={`translate(${carriageX},0)`}\n >\n <rect\n className=\"ds-rule__pawl-spring\"\n x={-2}\n y={pawlY - 24}\n width={4}\n height={11}\n />\n <path\n className=\"ds-rule__pawl\"\n d=\"M-6,-11 L6,-11 L0,2 Z\"\n transform={`translate(0,${pawlY})`}\n />\n <rect\n className=\"ds-rule__glass\"\n x={-glassW / 2}\n y={railY - 2}\n width={glassW}\n height={railH + 50}\n rx={2}\n />\n <rect\n className=\"ds-rule__frame\"\n x={-glassW / 2}\n y={railY - 2}\n width={glassW}\n height={railH + 50}\n rx={3}\n />\n <line\n className=\"ds-rule__hairline\"\n x1={0}\n y1={railY - 2}\n x2={0}\n y2={bot + 48}\n />\n {Array.from({ length: 6 }, (_, k) => {\n const vx = (k - 2.5) * vdiv;\n const on = k === lit.k && lit.lit;\n return (\n <g key={k}>\n <line\n className={\n \"ds-rule__vernier-line\" +\n (on ? \" ds-rule__vernier-line--lit\" : \"\")\n }\n x1={vx}\n y1={bot + 2}\n x2={vx}\n y2={bot + 18}\n />\n {k < 5 && (\n <text\n className=\"ds-rule__numeral\"\n x={vx}\n y={bot + 28}\n textAnchor=\"middle\"\n >\n {k}\n </text>\n )}\n </g>\n );\n })}\n {/* Flip the caption inboard once the carriage nears the right end. */}\n <text\n className=\"ds-rule__caption\"\n x={captionLeft ? -glassW / 2 - 6 : glassW / 2 + 6}\n y={bot + 18}\n textAnchor={captionLeft ? \"end\" : \"start\"}\n >\n VERNIER\n </text>\n </g>\n </svg>\n );\n});\n"],"mappings":"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","names":["useMemo","useRef","CursorRule","forwardRef","useMemo","useRef","jsx","jsxs","forwardRef","SlideStator","useMemo","useRef","forwardRef","useMemo","useRef","jsx","jsxs","forwardRef","VernierCarriage","useRef","useMemo","jsx","jsxs","useMemo","useRef"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar navigation built on a circular slide rule.
|
|
3
|
+
*
|
|
4
|
+
* At rest only a crescent of the disc breaks the edge — enough to read as an
|
|
5
|
+
* instrument, not enough to cost layout. Approaching the rail swings the whole
|
|
6
|
+
* rule out; spinning it brings a station to the fixed index, ratcheting
|
|
7
|
+
* through quarter-pitch detents on the way.
|
|
8
|
+
*
|
|
9
|
+
* As with SlideRuleNav, the engraved chrome is an aria-hidden SVG and the
|
|
10
|
+
* navigation is an ordinary list of links positioned over it, so the dial is
|
|
11
|
+
* fully operable by keyboard and the spin is a progressive enhancement.
|
|
12
|
+
*
|
|
13
|
+
* @param {object} props
|
|
14
|
+
* @param {"volvelle"|"concentric"|"fan"} [props.variant]
|
|
15
|
+
* @param {DialItem[]} props.items
|
|
16
|
+
* @param {string} [props.value] - Selected item id (controlled).
|
|
17
|
+
* @param {string} [props.defaultValue]
|
|
18
|
+
* @param {(id: string) => void} [props.onChange]
|
|
19
|
+
* @param {boolean} [props.expanded] - Controlled extract state.
|
|
20
|
+
* @param {boolean} [props.defaultExpanded]
|
|
21
|
+
* @param {(expanded: boolean) => void} [props.onExpandedChange]
|
|
22
|
+
* @param {string} [props.label] - Accessible name for the nav landmark.
|
|
23
|
+
* @param {string} [props.className]
|
|
24
|
+
* @returns {import("react").ReactElement}
|
|
25
|
+
*/
|
|
26
|
+
export function DialNav({ variant, items, value, defaultValue, onChange, expanded, defaultExpanded, onExpandedChange, label, className, }: {
|
|
27
|
+
variant?: "volvelle" | "concentric" | "fan" | undefined;
|
|
28
|
+
items: DialItem[];
|
|
29
|
+
value?: string | undefined;
|
|
30
|
+
defaultValue?: string | undefined;
|
|
31
|
+
onChange?: ((id: string) => void) | undefined;
|
|
32
|
+
expanded?: boolean | undefined;
|
|
33
|
+
defaultExpanded?: boolean | undefined;
|
|
34
|
+
onExpandedChange?: ((expanded: boolean) => void) | undefined;
|
|
35
|
+
label?: string | undefined;
|
|
36
|
+
className?: string | undefined;
|
|
37
|
+
}): import("react").ReactElement;
|
|
38
|
+
export type DialItem = {
|
|
39
|
+
id: string;
|
|
40
|
+
label: import("react").ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* - Route path. Renders a NavLink; requires a Router.
|
|
43
|
+
*/
|
|
44
|
+
to?: string | undefined;
|
|
45
|
+
end?: boolean | undefined;
|
|
46
|
+
onSelect?: (() => void) | undefined;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=DialNav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialNav.d.ts","sourceRoot":"","sources":["../../../src/components/DialNav/DialNav.jsx"],"names":[],"mappings":"AAmFA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,2IAZG;IAA8C,OAAO;IAC3B,KAAK,EAAvB,QAAQ,EAAE;IACK,KAAK;IACL,YAAY;IACE,QAAQ,SAAhC,MAAM,KAAK,IAAI;IACJ,QAAQ;IACR,eAAe;IACK,gBAAgB,eAAzC,OAAO,KAAK,IAAI;IACZ,KAAK;IACL,SAAS;CAChC,GAAU,OAAO,OAAO,EAAE,YAAY,CAmVxC;;QA1aa,MAAM;WACN,OAAO,OAAO,EAAE,SAAS;;;;;;sBAGnB,IAAI"}
|