@tremolo-ui/vue 0.7.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/LICENSE +21 -0
- package/README.md +13 -0
- package/dist/index.cjs +2486 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1678 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1678 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2444 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
- package/src/components/AnimationCanvas/AnimationCanvas.ts +105 -0
- package/src/components/AnimationCanvas/index.ts +5 -0
- package/src/components/DropZone/DropZone.ts +43 -0
- package/src/components/DropZone/index.ts +1 -0
- package/src/components/FileInput/FileInput.ts +82 -0
- package/src/components/FileInput/FileInputTrigger.ts +24 -0
- package/src/components/FileInput/context.ts +18 -0
- package/src/components/FileInput/index.ts +3 -0
- package/src/components/Knob/Knob.ts +237 -0
- package/src/components/Knob/KnobActiveLine.ts +31 -0
- package/src/components/Knob/KnobInactiveLine.ts +36 -0
- package/src/components/Knob/KnobSVGRoot.ts +25 -0
- package/src/components/Knob/KnobThumb.ts +53 -0
- package/src/components/Knob/context.ts +30 -0
- package/src/components/Knob/index.ts +6 -0
- package/src/components/NumberInput/NumberInput.ts +258 -0
- package/src/components/NumberInput/NumberInputDecrementStepper.ts +7 -0
- package/src/components/NumberInput/NumberInputField.ts +128 -0
- package/src/components/NumberInput/NumberInputIncrementStepper.ts +7 -0
- package/src/components/NumberInput/NumberInputStepper.ts +54 -0
- package/src/components/NumberInput/context.ts +63 -0
- package/src/components/NumberInput/index.ts +6 -0
- package/src/components/NumberInput/stepper-button.ts +65 -0
- package/src/components/Piano/Piano.ts +243 -0
- package/src/components/Piano/index.ts +1 -0
- package/src/components/PointsEditor/PointsEditor.ts +146 -0
- package/src/components/PointsEditor/PointsEditorBackground.ts +17 -0
- package/src/components/PointsEditor/PointsEditorContainer.ts +73 -0
- package/src/components/PointsEditor/PointsEditorPoint.ts +253 -0
- package/src/components/PointsEditor/PointsEditorSelectionBox.ts +36 -0
- package/src/components/PointsEditor/context.ts +39 -0
- package/src/components/PointsEditor/index.ts +9 -0
- package/src/components/Slider/Slider.ts +224 -0
- package/src/components/Slider/SliderMarks.ts +57 -0
- package/src/components/Slider/SliderMarksOption.ts +85 -0
- package/src/components/Slider/SliderThumb.ts +92 -0
- package/src/components/Slider/SliderTrack.ts +50 -0
- package/src/components/Slider/context.ts +33 -0
- package/src/components/Slider/index.ts +6 -0
- package/src/components/XYPad/XYPad.ts +250 -0
- package/src/components/XYPad/XYPadArea.ts +20 -0
- package/src/components/XYPad/XYPadThumb.ts +105 -0
- package/src/components/XYPad/context.ts +33 -0
- package/src/components/XYPad/index.ts +4 -0
- package/src/components/_util/context.ts +8 -0
- package/src/components/_util/placement.ts +36 -0
- package/src/components/_util/props.ts +32 -0
- package/src/components/_util/useCheckSteps.ts +22 -0
- package/src/components/_util/visually-hidden-range-input.ts +16 -0
- package/src/composables/useDrag.ts +22 -0
- package/src/composables/useDragValue.ts +20 -0
- package/src/composables/useDropZone.ts +32 -0
- package/src/composables/useInstance.ts +52 -0
- package/src/composables/useLongPress.ts +20 -0
- package/src/composables/useMIDIAccess.ts +58 -0
- package/src/composables/useMIDIInput.ts +33 -0
- package/src/composables/useMIDIMessage.ts +30 -0
- package/src/composables/useWheel.ts +23 -0
- package/src/index.ts +67 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { inject, provide, type InjectionKey } from 'vue'
|
|
2
|
+
|
|
3
|
+
const PLACEMENT: InjectionKey<string> = Symbol('tremolo-ui placement')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mark the children of this component as being inside `name`, for
|
|
7
|
+
* {@link checkPlacement}. Call it during `setup`.
|
|
8
|
+
*/
|
|
9
|
+
export function providePlacement(name: string) {
|
|
10
|
+
provide(PLACEMENT, name)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Warn, in development only, when `child` is rendered outside `parent`. A
|
|
15
|
+
* part positioned against its parent still renders anywhere else, so nothing
|
|
16
|
+
* fails when it is misplaced — its position just comes out wrong. The names
|
|
17
|
+
* are the ones written in templates (`'KnobThumb'`), since they are what the
|
|
18
|
+
* warning shows. Call it during `setup`.
|
|
19
|
+
*/
|
|
20
|
+
export function checkPlacement(child: string, parent: string) {
|
|
21
|
+
try {
|
|
22
|
+
// Inline and first, so that a bundler folds the comparison and drops the
|
|
23
|
+
// message with it. Wrapped because `process` may not exist at all.
|
|
24
|
+
if (process.env.NODE_ENV === 'production') return
|
|
25
|
+
const found = inject(PLACEMENT, null)
|
|
26
|
+
if (found === parent) return
|
|
27
|
+
console.warn(
|
|
28
|
+
`[tremolo-ui] ${child} has to be rendered inside ${parent}` +
|
|
29
|
+
(found === null ? '.' : `, but it is inside ${found}.`) +
|
|
30
|
+
' It renders either way, so nothing fails — its position just' +
|
|
31
|
+
' comes out wrong.',
|
|
32
|
+
)
|
|
33
|
+
} catch {
|
|
34
|
+
// No bundler substituted NODE_ENV. Say nothing rather than break.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type PropType } from 'vue'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_DRAG_SENSITIVITY,
|
|
5
|
+
DEFAULT_KEYBOARD_OPTIONS,
|
|
6
|
+
DEFAULT_WHEEL_OPTIONS,
|
|
7
|
+
type InputEventOption,
|
|
8
|
+
type ModifierValue,
|
|
9
|
+
} from '@tremolo-ui/dom'
|
|
10
|
+
|
|
11
|
+
// The input props every value control shares, with the core's defaults. One
|
|
12
|
+
// constant each rather than an object to spread: a spread inside
|
|
13
|
+
// `defineComponent(...)` counts as a possible side effect to a bundler, which
|
|
14
|
+
// then keeps every component that uses it even when none is imported.
|
|
15
|
+
|
|
16
|
+
/** How much one notch of the wheel moves the value. `null` turns it off. */
|
|
17
|
+
export const wheelProp = {
|
|
18
|
+
type: [Array, Object] as PropType<ModifierValue<InputEventOption> | null>,
|
|
19
|
+
default: () => DEFAULT_WHEEL_OPTIONS,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** How much one arrow key press moves the value. `null` turns it off. */
|
|
23
|
+
export const keyboardProp = {
|
|
24
|
+
type: [Array, Object] as PropType<ModifierValue<InputEventOption> | null>,
|
|
25
|
+
default: () => DEFAULT_KEYBOARD_OPTIONS,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** How much a drag moves the value, per modifier key. */
|
|
29
|
+
export const dragSensitivityProp = {
|
|
30
|
+
type: [Number, Object] as PropType<ModifierValue<number>>,
|
|
31
|
+
default: () => DEFAULT_DRAG_SENSITIVITY,
|
|
32
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { watchEffect } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { checkSteps, type CheckStepsOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Warn, in development only, when a key press or a wheel notch cannot produce
|
|
7
|
+
* a change the user can see. The probing is `checkSteps` in the core.
|
|
8
|
+
*
|
|
9
|
+
* Takes a getter, so that the check runs again when the settings change.
|
|
10
|
+
*/
|
|
11
|
+
export function useCheckSteps(options: () => CheckStepsOptions) {
|
|
12
|
+
try {
|
|
13
|
+
// Inline and first, so that a bundler folds the comparison and drops the
|
|
14
|
+
// only call to `checkSteps`, which then falls out of the bundle too.
|
|
15
|
+
if (process.env.NODE_ENV === 'production') return
|
|
16
|
+
watchEffect(() => {
|
|
17
|
+
for (const warning of checkSteps(options())) console.warn(warning)
|
|
18
|
+
})
|
|
19
|
+
} catch {
|
|
20
|
+
// No bundler substituted NODE_ENV. Say nothing rather than break.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { h, type VNodeProps } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { visuallyHiddenStyle } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The control underneath a headless part: out of sight, but in the
|
|
7
|
+
* accessibility tree and the tab order, carrying the ARIA and the keyboard.
|
|
8
|
+
*
|
|
9
|
+
* A plain element rather than a component, so that a `ref` on it reaches the
|
|
10
|
+
* `<input>` itself.
|
|
11
|
+
*/
|
|
12
|
+
export function visuallyHiddenRangeInput(
|
|
13
|
+
props: VNodeProps & Record<string, unknown>,
|
|
14
|
+
) {
|
|
15
|
+
return h('input', { ...props, type: 'range', style: visuallyHiddenStyle })
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { createDrag, type DragOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
import { useInstance } from './useInstance'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Track a pointer drag on an element. See `createDrag` in `@tremolo-ui/dom`
|
|
9
|
+
* for the options.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const el = useTemplateRef('el')
|
|
13
|
+
* useDrag(el, { onDrag: (state) => (x.value += state.deltaX) })
|
|
14
|
+
*/
|
|
15
|
+
export function useDrag(
|
|
16
|
+
target: MaybeRefOrGetter<Element | null | undefined>,
|
|
17
|
+
options: MaybeRefOrGetter<DragOptions> = {},
|
|
18
|
+
) {
|
|
19
|
+
useInstance(target, options, createDrag, (instance, next) =>
|
|
20
|
+
instance.update(next),
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { createDragValue, type DragValueOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
import { useInstance } from './useInstance'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drive a value with a pointer drag. See `createDragValue` in
|
|
9
|
+
* `@tremolo-ui/dom` for the options; `elementMapping` and `relativeMapping`
|
|
10
|
+
* from there choose how the pointer becomes a value. `mapping` is read when
|
|
11
|
+
* the element is attached.
|
|
12
|
+
*/
|
|
13
|
+
export function useDragValue(
|
|
14
|
+
target: MaybeRefOrGetter<Element | null | undefined>,
|
|
15
|
+
options: MaybeRefOrGetter<DragValueOptions>,
|
|
16
|
+
) {
|
|
17
|
+
useInstance(target, options, createDragValue, (instance, next) =>
|
|
18
|
+
instance.update(next),
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { shallowRef, toValue, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createDropZone,
|
|
5
|
+
type DropZoneOptions,
|
|
6
|
+
type DropZoneState,
|
|
7
|
+
} from '@tremolo-ui/dom'
|
|
8
|
+
|
|
9
|
+
import { useInstance } from './useInstance'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Take files dropped on an element. See `createDropZone` in
|
|
13
|
+
* `@tremolo-ui/dom`. Returns whether files are over it, and whether they
|
|
14
|
+
* would be refused, for styling it while they are.
|
|
15
|
+
*/
|
|
16
|
+
export function useDropZone(
|
|
17
|
+
target: MaybeRefOrGetter<Element | null | undefined>,
|
|
18
|
+
options: MaybeRefOrGetter<DropZoneOptions> = {},
|
|
19
|
+
) {
|
|
20
|
+
const state = shallowRef<DropZoneState>({ over: false, invalid: false })
|
|
21
|
+
const withState = () => ({
|
|
22
|
+
...toValue(options),
|
|
23
|
+
onStateChange: (next: DropZoneState) => {
|
|
24
|
+
state.value = next
|
|
25
|
+
toValue(options).onStateChange?.(next)
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
useInstance(target, withState, createDropZone, (instance, next) =>
|
|
29
|
+
instance.update(next),
|
|
30
|
+
)
|
|
31
|
+
return state
|
|
32
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { toValue, watch, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { replaceOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create an instance for the element `target` holds, and destroy it when the
|
|
7
|
+
* element goes away or changes. Options are pushed to the instance with
|
|
8
|
+
* `update` as they change, so that changing them does not interrupt a drag in
|
|
9
|
+
* progress.
|
|
10
|
+
*
|
|
11
|
+
* Call it during `setup`: the watchers stop with the component.
|
|
12
|
+
*/
|
|
13
|
+
export function useInstance<
|
|
14
|
+
E extends Element,
|
|
15
|
+
O extends object,
|
|
16
|
+
I extends { destroy: () => void },
|
|
17
|
+
>(
|
|
18
|
+
target: MaybeRefOrGetter<E | null | undefined>,
|
|
19
|
+
options: MaybeRefOrGetter<O>,
|
|
20
|
+
create: (element: E, options: O) => I,
|
|
21
|
+
update: (instance: I, options: Partial<O>) => void,
|
|
22
|
+
): { current: () => I | null } {
|
|
23
|
+
let instance: I | null = null
|
|
24
|
+
|
|
25
|
+
watch(
|
|
26
|
+
() => toValue(target),
|
|
27
|
+
(element, _, onCleanup) => {
|
|
28
|
+
if (!element) return
|
|
29
|
+
const current = create(element, toValue(options))
|
|
30
|
+
instance = current
|
|
31
|
+
onCleanup(() => {
|
|
32
|
+
current.destroy()
|
|
33
|
+
if (instance === current) instance = null
|
|
34
|
+
})
|
|
35
|
+
},
|
|
36
|
+
{ immediate: true, flush: 'post' },
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// A copy, so that the watch reads every property: a `reactive()` object
|
|
40
|
+
// whose `requireFocus` changes is then seen, not only a new object. The
|
|
41
|
+
// copy is what the instance is left with — what it no longer carries is
|
|
42
|
+
// cleared rather than kept.
|
|
43
|
+
watch(
|
|
44
|
+
() => ({ ...toValue(options) }),
|
|
45
|
+
(next, previous) => {
|
|
46
|
+
if (instance) update(instance, replaceOptions(previous, next))
|
|
47
|
+
},
|
|
48
|
+
{ flush: 'post' },
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return { current: () => instance }
|
|
52
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { onScopeDispose, toValue, watch, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { createLongPress, type LongPressOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Repeat `onPress` while a pointer is held down: once on the press, then
|
|
7
|
+
* every `interval` after `delay`. See `createLongPress` in `@tremolo-ui/dom`.
|
|
8
|
+
*
|
|
9
|
+
* Returns the function that starts a press, for `@pointerdown`.
|
|
10
|
+
*/
|
|
11
|
+
export function useLongPress(options: MaybeRefOrGetter<LongPressOptions>) {
|
|
12
|
+
const instance = createLongPress(toValue(options))
|
|
13
|
+
watch(
|
|
14
|
+
() => toValue(options),
|
|
15
|
+
(next) => instance.update(next),
|
|
16
|
+
)
|
|
17
|
+
onScopeDispose(() => instance.destroy())
|
|
18
|
+
return (event?: Pick<PointerEvent, 'button' | 'pointerId'>) =>
|
|
19
|
+
instance.start(event)
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
onMounted,
|
|
4
|
+
onScopeDispose,
|
|
5
|
+
shallowRef,
|
|
6
|
+
toValue,
|
|
7
|
+
watch,
|
|
8
|
+
type MaybeRefOrGetter,
|
|
9
|
+
} from 'vue'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
createMIDIAccess,
|
|
13
|
+
type MIDIAccessOptions,
|
|
14
|
+
type MIDIAccessState,
|
|
15
|
+
} from '@tremolo-ui/dom'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Request MIDI access in the browser. Call it during `setup`.
|
|
19
|
+
*
|
|
20
|
+
* `state` holds the access, the error and the connected inputs, and follows
|
|
21
|
+
* devices as they are plugged in and unplugged.
|
|
22
|
+
*
|
|
23
|
+
* @param requestOnMount ask for access once the component is mounted — never
|
|
24
|
+
* during server rendering, where there is no MIDI to ask for. Leave it off to
|
|
25
|
+
* ask from a click instead, so that opening a page does not bring up a
|
|
26
|
+
* permission prompt. A ref or a getter asks once it turns true.
|
|
27
|
+
*/
|
|
28
|
+
export function useMIDIAccess(
|
|
29
|
+
requestOnMount: MaybeRefOrGetter<boolean> = true,
|
|
30
|
+
) {
|
|
31
|
+
const instance = createMIDIAccess()
|
|
32
|
+
const state = shallowRef<MIDIAccessState>(instance.getState())
|
|
33
|
+
const unsubscribe = instance.subscribe(() => {
|
|
34
|
+
state.value = instance.getState()
|
|
35
|
+
})
|
|
36
|
+
onMounted(() => {
|
|
37
|
+
watch(
|
|
38
|
+
() => toValue(requestOnMount),
|
|
39
|
+
(request) => {
|
|
40
|
+
if (request) instance.request()
|
|
41
|
+
},
|
|
42
|
+
{ immediate: true },
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
onScopeDispose(() => {
|
|
46
|
+
unsubscribe()
|
|
47
|
+
instance.destroy()
|
|
48
|
+
})
|
|
49
|
+
return {
|
|
50
|
+
// Read-only, and without wrapping MIDIAccess in a proxy.
|
|
51
|
+
state: computed(() => state.value),
|
|
52
|
+
/**
|
|
53
|
+
* Ask for access. Takes `{ sysex: true }` when the app needs system
|
|
54
|
+
* exclusive messages, which browsers treat as a separate permission.
|
|
55
|
+
*/
|
|
56
|
+
request: (options?: MIDIAccessOptions) => instance.request(options),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { toValue, watch, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { createMIDIInput, type MIDIInputHandlers } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handle the channel voice messages of every connected input. Call it during
|
|
7
|
+
* `setup`. See `createMIDIInput` in `@tremolo-ui/dom` for the handlers.
|
|
8
|
+
*
|
|
9
|
+
* The listeners follow `midiAccess` as it is granted, and new handlers
|
|
10
|
+
* replace the old ones without re-subscribing.
|
|
11
|
+
*/
|
|
12
|
+
export function useMIDIInput(
|
|
13
|
+
midiAccess: MaybeRefOrGetter<MIDIAccess | null>,
|
|
14
|
+
handlers: MaybeRefOrGetter<MIDIInputHandlers>,
|
|
15
|
+
) {
|
|
16
|
+
let instance: ReturnType<typeof createMIDIInput> | null = null
|
|
17
|
+
watch(
|
|
18
|
+
() => toValue(midiAccess),
|
|
19
|
+
(access, _, onCleanup) => {
|
|
20
|
+
const current = createMIDIInput(access, toValue(handlers))
|
|
21
|
+
instance = current
|
|
22
|
+
onCleanup(() => {
|
|
23
|
+
current.destroy()
|
|
24
|
+
if (instance === current) instance = null
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
{ immediate: true },
|
|
28
|
+
)
|
|
29
|
+
watch(
|
|
30
|
+
() => toValue(handlers),
|
|
31
|
+
(next) => instance?.update(next),
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { toValue, watch, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { createMIDIMessage } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Listen to raw `midimessage` events on every input. Call it during `setup`.
|
|
7
|
+
* For note and controller events, `useMIDIInput` decodes the message for you.
|
|
8
|
+
*/
|
|
9
|
+
export function useMIDIMessage(
|
|
10
|
+
midiAccess: MaybeRefOrGetter<MIDIAccess | null>,
|
|
11
|
+
onMIDIMessage: MaybeRefOrGetter<(event: MIDIMessageEvent) => void>,
|
|
12
|
+
) {
|
|
13
|
+
let instance: ReturnType<typeof createMIDIMessage> | null = null
|
|
14
|
+
watch(
|
|
15
|
+
() => toValue(midiAccess),
|
|
16
|
+
(access, _, onCleanup) => {
|
|
17
|
+
const current = createMIDIMessage(access, toValue(onMIDIMessage))
|
|
18
|
+
instance = current
|
|
19
|
+
onCleanup(() => {
|
|
20
|
+
current.destroy()
|
|
21
|
+
if (instance === current) instance = null
|
|
22
|
+
})
|
|
23
|
+
},
|
|
24
|
+
{ immediate: true },
|
|
25
|
+
)
|
|
26
|
+
watch(
|
|
27
|
+
() => toValue(onMIDIMessage),
|
|
28
|
+
(next) => instance?.update(next),
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { createWheel, type WheelOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
import { useInstance } from './useInstance'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Listen to the wheel on an element, as a non-passive listener so that
|
|
9
|
+
* `preventDefault()` can keep the page from scrolling. See `createWheel` in
|
|
10
|
+
* `@tremolo-ui/dom`.
|
|
11
|
+
*/
|
|
12
|
+
export function useWheel(
|
|
13
|
+
target: MaybeRefOrGetter<Element | null | undefined>,
|
|
14
|
+
onWheel: (event: WheelEvent) => void,
|
|
15
|
+
options: MaybeRefOrGetter<WheelOptions> = {},
|
|
16
|
+
) {
|
|
17
|
+
useInstance(
|
|
18
|
+
target,
|
|
19
|
+
options,
|
|
20
|
+
(element, opts) => createWheel(element, (event) => onWheel(event), opts),
|
|
21
|
+
(instance, next) => instance.update(next),
|
|
22
|
+
)
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// components
|
|
2
|
+
export {
|
|
3
|
+
AnimationCanvas,
|
|
4
|
+
type DrawFunction,
|
|
5
|
+
type InitFunction,
|
|
6
|
+
} from './components/AnimationCanvas'
|
|
7
|
+
export { DropZone } from './components/DropZone'
|
|
8
|
+
export {
|
|
9
|
+
FileInput,
|
|
10
|
+
FileInputTrigger,
|
|
11
|
+
useFileInputContext,
|
|
12
|
+
type FileInputContextValue,
|
|
13
|
+
} from './components/FileInput'
|
|
14
|
+
export {
|
|
15
|
+
Knob,
|
|
16
|
+
KnobActiveLine,
|
|
17
|
+
KnobInactiveLine,
|
|
18
|
+
KnobSVGRoot,
|
|
19
|
+
KnobThumb,
|
|
20
|
+
useKnobContext,
|
|
21
|
+
type KnobContextValue,
|
|
22
|
+
} from './components/Knob'
|
|
23
|
+
export {
|
|
24
|
+
NumberInput,
|
|
25
|
+
NumberInputDecrementStepper,
|
|
26
|
+
NumberInputField,
|
|
27
|
+
NumberInputIncrementStepper,
|
|
28
|
+
NumberInputStepper,
|
|
29
|
+
useNumberInputContext,
|
|
30
|
+
type NumberInputContextValue,
|
|
31
|
+
} from './components/NumberInput'
|
|
32
|
+
export { Piano, type KeyAttributes, type KeyState } from './components/Piano'
|
|
33
|
+
export {
|
|
34
|
+
PointsEditor,
|
|
35
|
+
PointsEditorBackground,
|
|
36
|
+
PointsEditorContainer,
|
|
37
|
+
PointsEditorPoint,
|
|
38
|
+
PointsEditorSelectionBox,
|
|
39
|
+
usePointsEditorContext,
|
|
40
|
+
type PointsEditorContextValue,
|
|
41
|
+
} from './components/PointsEditor'
|
|
42
|
+
export {
|
|
43
|
+
Slider,
|
|
44
|
+
SliderMarks,
|
|
45
|
+
SliderMarksOption,
|
|
46
|
+
SliderThumb,
|
|
47
|
+
SliderTrack,
|
|
48
|
+
useSliderContext,
|
|
49
|
+
type SliderContextValue,
|
|
50
|
+
} from './components/Slider'
|
|
51
|
+
export {
|
|
52
|
+
XYPad,
|
|
53
|
+
XYPadArea,
|
|
54
|
+
XYPadThumb,
|
|
55
|
+
useXYPadContext,
|
|
56
|
+
type XYPadContextValue,
|
|
57
|
+
} from './components/XYPad'
|
|
58
|
+
|
|
59
|
+
// composables
|
|
60
|
+
export { useDrag } from './composables/useDrag'
|
|
61
|
+
export { useDragValue } from './composables/useDragValue'
|
|
62
|
+
export { useDropZone } from './composables/useDropZone'
|
|
63
|
+
export { useLongPress } from './composables/useLongPress'
|
|
64
|
+
export { useMIDIAccess } from './composables/useMIDIAccess'
|
|
65
|
+
export { useMIDIInput } from './composables/useMIDIInput'
|
|
66
|
+
export { useMIDIMessage } from './composables/useMIDIMessage'
|
|
67
|
+
export { useWheel } from './composables/useWheel'
|