@tremolo-ui/dom 0.3.0 → 0.5.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/index.cjs +623 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +416 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +416 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +614 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/src/canvas/animation.ts +304 -0
- package/src/canvas/context.ts +81 -0
- package/src/index.ts +43 -0
- package/src/piano/input.ts +185 -0
- package/src/pointer/drag.ts +295 -0
- package/src/pointer/dragValue.ts +229 -0
- package/src/pointer/wheel.ts +58 -0
- package/src/xy.ts +38 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface WheelOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Only report events while the focus is inside the element.
|
|
4
|
+
*
|
|
5
|
+
* A control that reacts to the wheel on hover alone takes the scroll away
|
|
6
|
+
* from the page, so passing over one in a long form silently changes its
|
|
7
|
+
* value. Requiring focus makes that an explicit act.
|
|
8
|
+
*
|
|
9
|
+
* The check is `contains`, not an identity test: the element that actually
|
|
10
|
+
* takes focus is usually a descendant, such as a thumb or an `<input>`, and
|
|
11
|
+
* a caller may have replaced it with markup of their own.
|
|
12
|
+
*
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
requireFocus?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface WheelInstance {
|
|
19
|
+
/** Replace the given options, keeping the listener in place. */
|
|
20
|
+
update: (options: WheelOptions) => void
|
|
21
|
+
destroy: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Listen to wheel events on an element.
|
|
26
|
+
*
|
|
27
|
+
* The listener is registered with `passive: false` so that the handler can call
|
|
28
|
+
* `preventDefault()` to stop the page from scrolling.
|
|
29
|
+
*/
|
|
30
|
+
export function createWheel(
|
|
31
|
+
element: Element,
|
|
32
|
+
onWheel: (event: WheelEvent) => void,
|
|
33
|
+
options: WheelOptions = {},
|
|
34
|
+
): WheelInstance {
|
|
35
|
+
let opts = options
|
|
36
|
+
|
|
37
|
+
function hasFocus() {
|
|
38
|
+
const active = element.ownerDocument?.activeElement
|
|
39
|
+
return !!active && element.contains(active)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handler = (event: Event) => {
|
|
43
|
+
if (opts.requireFocus && !hasFocus()) return
|
|
44
|
+
onWheel(event as WheelEvent)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
element.addEventListener('wheel', handler, { passive: false })
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
update: (next) => {
|
|
51
|
+
opts = { ...opts, ...next }
|
|
52
|
+
},
|
|
53
|
+
destroy: () => {
|
|
54
|
+
// Only `capture` matters when removing, and it is false here.
|
|
55
|
+
element.removeEventListener('wheel', handler)
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/xy.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pair of per-axis values. The tuple elements are labelled, so editors show
|
|
3
|
+
* `[x: number, y: number]` rather than a bare pair.
|
|
4
|
+
*/
|
|
5
|
+
export type XY<T> = [x: T, y: T]
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A setting that may be given once for both axes, or per axis.
|
|
9
|
+
*
|
|
10
|
+
* A single value is told from a pair with `Array.isArray`, so the single form
|
|
11
|
+
* is only offered while `T` cannot itself be an array. Where it can, the pair
|
|
12
|
+
* is the only way to write it, since a lone array would be read as a pair.
|
|
13
|
+
*/
|
|
14
|
+
export type XYInput<T> = [T] extends [readonly unknown[]]
|
|
15
|
+
? readonly [x: T, y: T]
|
|
16
|
+
: T | readonly [x: T, y: T]
|
|
17
|
+
|
|
18
|
+
// `Array.isArray` narrows a mutable tuple on its own, but not a readonly one,
|
|
19
|
+
// hence the explicit predicate.
|
|
20
|
+
function isPair<T>(
|
|
21
|
+
value: T | readonly [x: T, y: T],
|
|
22
|
+
): value is readonly [x: T, y: T] {
|
|
23
|
+
return Array.isArray(value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Spread a setting that may have been given as a single value.
|
|
28
|
+
*
|
|
29
|
+
* The parameter is written out rather than taken as `XYInput<T>`: a
|
|
30
|
+
* conditional type cannot be narrowed, so the constraint stays where it is
|
|
31
|
+
* declared and this takes both forms.
|
|
32
|
+
*
|
|
33
|
+
* The pair is copied rather than passed along, so that the result is a tuple
|
|
34
|
+
* the caller owns even when a readonly one was given.
|
|
35
|
+
*/
|
|
36
|
+
export function toXY<T>(value: T | readonly [x: T, y: T]): XY<T> {
|
|
37
|
+
return isPair(value) ? [value[0], value[1]] : [value, value]
|
|
38
|
+
}
|