@tremolo-ui/react 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compose-refs.cjs +72 -0
- package/dist/compose-refs.cjs.map +1 -0
- package/dist/compose-refs.d.cts +43 -0
- package/dist/compose-refs.d.cts.map +1 -0
- package/dist/compose-refs.d.ts +43 -0
- package/dist/compose-refs.d.ts.map +1 -0
- package/dist/compose-refs.js +70 -0
- package/dist/compose-refs.js.map +1 -0
- package/dist/index.cjs +2272 -1650
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1461 -475
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1461 -475
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2258 -1648
- package/dist/index.js.map +1 -1
- package/package.json +32 -31
- package/src/components/AnimationCanvas/index.tsx +162 -194
- package/src/components/Knob/ActiveLine.tsx +9 -8
- package/src/components/Knob/InactiveLine.tsx +11 -14
- package/src/components/Knob/SVGRoot.tsx +10 -17
- package/src/components/Knob/Thumb.tsx +33 -11
- package/src/components/Knob/context.tsx +73 -89
- package/src/components/Knob/index.tsx +227 -105
- package/src/components/NumberInput/DecrementStepper.tsx +21 -50
- package/src/components/NumberInput/IncrementStepper.tsx +22 -50
- package/src/components/NumberInput/InputField.tsx +206 -0
- package/src/components/NumberInput/Stepper.tsx +150 -21
- package/src/components/NumberInput/StepperButton.tsx +95 -0
- package/src/components/NumberInput/context.tsx +104 -149
- package/src/components/NumberInput/index.tsx +461 -110
- package/src/components/Piano/index.tsx +413 -263
- package/src/components/Piano/keyboard-shortcuts.ts +61 -0
- package/src/components/PointsEditor/Background.tsx +19 -4
- package/src/components/PointsEditor/Container.tsx +92 -22
- package/src/components/PointsEditor/Point.tsx +293 -53
- package/src/components/PointsEditor/SelectionBox.tsx +54 -0
- package/src/components/PointsEditor/context.tsx +97 -75
- package/src/components/PointsEditor/index.tsx +497 -62
- package/src/components/Slider/Marks.tsx +84 -0
- package/src/components/Slider/MarksOption.tsx +115 -0
- package/src/components/Slider/Thumb.tsx +99 -54
- package/src/components/Slider/Track.tsx +73 -62
- package/src/components/Slider/context.tsx +40 -53
- package/src/components/Slider/index.tsx +237 -190
- package/src/components/Slider/type.ts +27 -9
- package/src/components/XYPad/Area.tsx +52 -33
- package/src/components/XYPad/Thumb.tsx +121 -60
- package/src/components/XYPad/context.tsx +49 -0
- package/src/components/XYPad/index.tsx +338 -246
- package/src/components/_util/Placement.tsx +68 -0
- package/src/components/_util/VisuallyHiddenRangeInput.tsx +24 -0
- package/src/components/_util/css-length.ts +13 -0
- package/src/{components/_util/composeRefs.tsx → compose-refs/index.tsx} +33 -5
- package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +3 -1
- package/src/hooks/_internal/useCheckSteps.ts +170 -0
- package/src/hooks/useAnimationFrame.ts +17 -7
- package/src/hooks/useDrag.ts +39 -9
- package/src/hooks/useDragValue.ts +194 -0
- package/src/hooks/useEventListener.ts +11 -7
- package/src/hooks/useLongPress.ts +42 -8
- package/src/hooks/useMIDIAccess.ts +82 -11
- package/src/hooks/useMIDIInput.ts +45 -13
- package/src/hooks/useMIDIMessage.ts +23 -5
- package/src/hooks/useWheel.ts +29 -9
- package/src/index.ts +50 -27
- package/src/input-event.ts +32 -0
- package/dist/index.css +0 -536
- package/src/Globals.d.ts +0 -3
- package/src/components/AnimationCanvas/canvas.ts +0 -46
- package/src/components/Knob/index.css +0 -77
- package/src/components/NumberInput/InternalInput.tsx +0 -216
- package/src/components/NumberInput/index.css +0 -185
- package/src/components/NumberInput/type.ts +0 -58
- package/src/components/Piano/KeyLabel.tsx +0 -51
- package/src/components/Piano/context.tsx +0 -73
- package/src/components/Piano/index.css +0 -90
- package/src/components/Piano/key.tsx +0 -205
- package/src/components/Piano/keyboardShortcuts.ts +0 -31
- package/src/components/PointsEditor/index.css +0 -30
- package/src/components/Slider/Scale.tsx +0 -52
- package/src/components/Slider/ScaleOption.tsx +0 -103
- package/src/components/Slider/index.css +0 -112
- package/src/components/XYPad/index.css +0 -48
- package/src/components/_util/index.ts +0 -28
- package/src/components/_util/type.ts +0 -1
- package/src/hooks/useDragWithElement.ts +0 -100
- package/src/hooks/usePianoDrag.ts +0 -75
- package/src/hooks/useRefCallbackEvent.ts +0 -34
- package/src/styles/global.css +0 -19
|
@@ -1,162 +1,117 @@
|
|
|
1
|
-
import { createContext,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { createContext, RefObject, useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
InputEventOption,
|
|
5
|
+
ModifierState,
|
|
6
|
+
ModifierValue,
|
|
7
|
+
Scale,
|
|
8
|
+
ValueRange,
|
|
9
|
+
} from '@tremolo-ui/functions'
|
|
10
|
+
|
|
11
|
+
export type NumberInputContextValue = {
|
|
12
|
+
value: number
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The range the caller asked for. Left undefined when unbounded, which is
|
|
16
|
+
* what `aria-valuemin` / `aria-valuemax` and the steppers need to tell apart
|
|
17
|
+
* from a range that happens to sit at the edge of the safe integers.
|
|
18
|
+
*/
|
|
13
19
|
min?: number
|
|
14
20
|
max?: number
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
step: number
|
|
22
|
+
/** How the value is distributed across the travel. */
|
|
23
|
+
scale: Scale
|
|
24
|
+
|
|
25
|
+
disabled: boolean
|
|
26
|
+
readonly: boolean
|
|
27
|
+
clampValue: boolean
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The effective scaling for `applyDelta`, with unbounded ends filled in.
|
|
31
|
+
* Ignores `min` / `max` when `clampValue` is off.
|
|
32
|
+
*/
|
|
33
|
+
range: ValueRange
|
|
34
|
+
/** The effective range for raw input, whose open ends cover finite numbers. */
|
|
35
|
+
rawRange: ValueRange
|
|
36
|
+
|
|
37
|
+
keyboard: ModifierValue<InputEventOption> | null
|
|
38
|
+
/** Pixels of vertical drag on `Stepper` that move the value by one `step`. */
|
|
39
|
+
drag: number | null
|
|
40
|
+
/** How much a `Stepper` drag counts, per modifier key. */
|
|
41
|
+
dragSensitivity: ModifierValue<number>
|
|
42
|
+
/** Whether a `Stepper` drag hides the pointer and reads its movement. */
|
|
43
|
+
pointerLock: boolean
|
|
44
|
+
|
|
45
|
+
/** What `InputField` selects when it takes focus. */
|
|
46
|
+
selectOnFocus: 'all' | 'number' | 'none'
|
|
47
|
+
/** Whether `InputField` shows the plain value while it has focus. */
|
|
48
|
+
unformatOnFocus: boolean
|
|
49
|
+
/** Whether `InputField` puts the caret back after an arrow key steps the value. */
|
|
50
|
+
keepCaretOnStep: boolean
|
|
51
|
+
/** Whether Enter leaves `InputField` as well as committing. */
|
|
52
|
+
blurOnEnter: boolean
|
|
53
|
+
|
|
54
|
+
/** What the input shows: the draft while editing, the formatted value otherwise. */
|
|
55
|
+
text: string
|
|
56
|
+
editing: boolean
|
|
57
|
+
/** Whether the value sits outside `min` / `max`. Not judged while editing. */
|
|
58
|
+
outOfRange: boolean
|
|
59
|
+
/** Whether a stepper would have no effect, for `aria-disabled`. */
|
|
60
|
+
atMin: boolean
|
|
61
|
+
atMax: boolean
|
|
62
|
+
|
|
63
|
+
format: (value: number) => string
|
|
64
|
+
parse: (text: string) => number
|
|
65
|
+
|
|
66
|
+
/** Typing. Replaces the draft and reports the parsed value, unclamped. */
|
|
67
|
+
setDraft: (text: string) => void
|
|
68
|
+
/** Blur or Enter. Optionally clamps the draft, reports it, and drops it. */
|
|
69
|
+
commitDraft: () => void
|
|
70
|
+
/** Drops the draft and replaces the value: steppers, wheel, keyboard, drag. */
|
|
71
|
+
changeValue: (next: number) => void
|
|
72
|
+
/** Moves the value by one `option` in `direction`, normally +1 or -1. */
|
|
73
|
+
nudge: (
|
|
74
|
+
direction: number,
|
|
75
|
+
option: ModifierValue<InputEventOption>,
|
|
76
|
+
modifiers?: ModifierState,
|
|
77
|
+
) => void
|
|
78
|
+
|
|
79
|
+
/** `InputField` registers itself here; `Root` focuses it through its methods. */
|
|
80
|
+
inputRef: RefObject<HTMLInputElement | null>
|
|
26
81
|
}
|
|
27
82
|
|
|
28
|
-
|
|
83
|
+
const NumberInputContext =
|
|
84
|
+
/* @__PURE__ */ createContext<NumberInputContextValue | null>(null)
|
|
29
85
|
|
|
30
|
-
|
|
86
|
+
export const NumberInputProvider = NumberInputContext.Provider
|
|
31
87
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
88
|
+
/**
|
|
89
|
+
* The only state behind this is the editing draft; `value` comes from the props
|
|
90
|
+
* of `Root` and everything else is derived during render.
|
|
91
|
+
*/
|
|
92
|
+
export function useNumberInputContext(): NumberInputContextValue
|
|
93
|
+
export function useNumberInputContext<T>(
|
|
94
|
+
selector: (state: NumberInputContextValue) => T,
|
|
95
|
+
): T
|
|
96
|
+
export function useNumberInputContext<T>(
|
|
97
|
+
selector?: (state: NumberInputContextValue) => T,
|
|
36
98
|
) {
|
|
37
|
-
|
|
99
|
+
const context = useContext(NumberInputContext)
|
|
100
|
+
if (!context)
|
|
101
|
+
throw new Error('Missing NumberInputContext.Provider in the tree')
|
|
102
|
+
return selector ? selector(context) : context
|
|
38
103
|
}
|
|
39
104
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
valueAsNumber: 0,
|
|
44
|
-
min: 0,
|
|
45
|
-
max: 0,
|
|
46
|
-
step: 1,
|
|
47
|
-
readonly: false,
|
|
48
|
-
keepWithinRange: true,
|
|
49
|
-
onChange: (_v: number, _t: string) => {},
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return createStore<State & Action>()((set) => ({
|
|
53
|
-
...DEFAULT_PROPS,
|
|
54
|
-
...initProps,
|
|
55
|
-
...{
|
|
56
|
-
value: parseValue(
|
|
57
|
-
String(initProps?.value || ''),
|
|
58
|
-
initProps?.units,
|
|
59
|
-
initProps?.digit,
|
|
60
|
-
).formatValue,
|
|
61
|
-
valueAsNumber: parseValue(
|
|
62
|
-
String(initProps?.value || ''),
|
|
63
|
-
initProps?.units,
|
|
64
|
-
initProps?.digit,
|
|
65
|
-
).rawValue,
|
|
66
|
-
},
|
|
67
|
-
increment: () =>
|
|
68
|
-
set((state) => {
|
|
69
|
-
if (state.readonly) return {}
|
|
70
|
-
const current = parseValue(
|
|
71
|
-
state.value,
|
|
72
|
-
state.units,
|
|
73
|
-
state.digit,
|
|
74
|
-
).rawValue
|
|
75
|
-
let next = current + (state.step ?? 1)
|
|
76
|
-
if (state.keepWithinRange) {
|
|
77
|
-
next = safeClamp(next, state.min, state.max)
|
|
78
|
-
}
|
|
79
|
-
const v = parseValue(String(next), state.units, state.digit).formatValue
|
|
80
|
-
state?.onChange?.(next, v)
|
|
81
|
-
return {
|
|
82
|
-
value: v,
|
|
83
|
-
valueAsNumber: next,
|
|
84
|
-
}
|
|
85
|
-
}),
|
|
86
|
-
decrement: () =>
|
|
87
|
-
set((state) => {
|
|
88
|
-
if (state.readonly) return {}
|
|
89
|
-
const current = parseValue(
|
|
90
|
-
state.value,
|
|
91
|
-
state.units,
|
|
92
|
-
state.digit,
|
|
93
|
-
).rawValue
|
|
94
|
-
let next = current - (state.step ?? 1)
|
|
95
|
-
if (state.keepWithinRange) {
|
|
96
|
-
next = safeClamp(next, state.min, state.max)
|
|
97
|
-
}
|
|
98
|
-
const v = parseValue(String(next), state.units, state.digit).formatValue
|
|
99
|
-
state?.onChange?.(next, v)
|
|
100
|
-
return {
|
|
101
|
-
value: v,
|
|
102
|
-
valueAsNumber: next,
|
|
103
|
-
}
|
|
104
|
-
}),
|
|
105
|
-
change: (value) =>
|
|
106
|
-
set((state) => {
|
|
107
|
-
if (state.readonly) return {}
|
|
108
|
-
const v = value ?? ''
|
|
109
|
-
return {
|
|
110
|
-
value: v,
|
|
111
|
-
valueAsNumber: parseValue(v, state.units, state.digit).rawValue,
|
|
112
|
-
}
|
|
113
|
-
}),
|
|
114
|
-
}))
|
|
105
|
+
/** Set by `Stepper` once a drag has actually moved, so the steppers stand down. */
|
|
106
|
+
export type StepperContextValue = {
|
|
107
|
+
draggingRef: RefObject<boolean>
|
|
115
108
|
}
|
|
116
109
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
type NumberInputProviderProps = React.PropsWithChildren<ProviderProps>
|
|
120
|
-
|
|
121
|
-
export function NumberInputProvider({
|
|
122
|
-
children,
|
|
123
|
-
...props
|
|
124
|
-
}: NumberInputProviderProps) {
|
|
125
|
-
const storeRef = useRef<NumberInputStore>(null)
|
|
126
|
-
if (!storeRef.current) {
|
|
127
|
-
storeRef.current = createNumberInputStore(props)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
if (storeRef.current) {
|
|
132
|
-
const parsed = parseValue(
|
|
133
|
-
String(props?.value || ''),
|
|
134
|
-
props?.units,
|
|
135
|
-
props?.digit,
|
|
136
|
-
)
|
|
137
|
-
storeRef.current.setState({
|
|
138
|
-
...props,
|
|
139
|
-
...{
|
|
140
|
-
value: parsed.formatValue,
|
|
141
|
-
valueAsNumber: parsed.rawValue,
|
|
142
|
-
},
|
|
143
|
-
})
|
|
144
|
-
} else {
|
|
145
|
-
storeRef.current = createNumberInputStore(props)
|
|
146
|
-
}
|
|
147
|
-
}, [props])
|
|
148
|
-
|
|
149
|
-
return (
|
|
150
|
-
<NumberInputContext.Provider value={storeRef.current}>
|
|
151
|
-
{children}
|
|
152
|
-
</NumberInputContext.Provider>
|
|
153
|
-
)
|
|
154
|
-
}
|
|
110
|
+
const StepperContext =
|
|
111
|
+
/* @__PURE__ */ createContext<StepperContextValue | null>(null)
|
|
155
112
|
|
|
156
|
-
export
|
|
157
|
-
|
|
158
|
-
):
|
|
159
|
-
|
|
160
|
-
if (!store) throw new Error('Missing NumberInputContext.Provider in the tree')
|
|
161
|
-
return useStore(store, selector)
|
|
113
|
+
export const StepperProvider = StepperContext.Provider
|
|
114
|
+
|
|
115
|
+
export function useStepperContext(): StepperContextValue | null {
|
|
116
|
+
return useContext(StepperContext)
|
|
162
117
|
}
|