@prestack/thread-ui 0.1.9 → 0.1.11

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.
Files changed (210) hide show
  1. package/CHANGELOG.md +366 -0
  2. package/dist/ui/attach.js +6 -7
  3. package/dist/ui/compositions.d.ts +0 -2
  4. package/dist/ui/compositions.js +2 -112
  5. package/dist/ui/generators/routes.js +1 -0
  6. package/dist/ui/variant.d.ts +1 -3
  7. package/package.json +2 -2
  8. package/dist/ui/animations/anm_appear.ts +0 -13
  9. package/dist/ui/animations/anm_blur_in.ts +0 -19
  10. package/dist/ui/animations/anm_blur_out.ts +0 -18
  11. package/dist/ui/animations/anm_dismiss.ts +0 -18
  12. package/dist/ui/animations/anm_drop.ts +0 -19
  13. package/dist/ui/animations/anm_fall.ts +0 -18
  14. package/dist/ui/animations/anm_flash.ts +0 -18
  15. package/dist/ui/animations/anm_glow.ts +0 -16
  16. package/dist/ui/animations/anm_lift.ts +0 -16
  17. package/dist/ui/animations/anm_nudge.ts +0 -16
  18. package/dist/ui/animations/anm_pop_in.ts +0 -23
  19. package/dist/ui/animations/anm_pop_out.ts +0 -22
  20. package/dist/ui/animations/anm_press.ts +0 -12
  21. package/dist/ui/animations/anm_pulse.ts +0 -13
  22. package/dist/ui/animations/anm_rise.ts +0 -19
  23. package/dist/ui/animations/anm_shake.ts +0 -13
  24. package/dist/ui/animations/anm_slide_in_left.ts +0 -19
  25. package/dist/ui/animations/anm_slide_in_right.ts +0 -19
  26. package/dist/ui/animations/anm_slide_out_left.ts +0 -18
  27. package/dist/ui/animations/anm_slide_out_right.ts +0 -18
  28. package/dist/ui/animations/anm_spin.ts +0 -12
  29. package/dist/ui/animations/anm_swell.ts +0 -16
  30. package/dist/ui/animations/anm_wobble.ts +0 -24
  31. package/dist/ui/animations/anm_zoom_in.ts +0 -19
  32. package/dist/ui/animations/anm_zoom_out.ts +0 -18
  33. package/dist/ui/animations.ts +0 -308
  34. package/dist/ui/attach.ts +0 -553
  35. package/dist/ui/charts.tsx +0 -69
  36. package/dist/ui/checked.ts +0 -32
  37. package/dist/ui/collector.ts +0 -140
  38. package/dist/ui/compositions.ts +0 -555
  39. package/dist/ui/consent.ts +0 -55
  40. package/dist/ui/content.tsx +0 -27
  41. package/dist/ui/contrast.ts +0 -27
  42. package/dist/ui/controls.tsx +0 -25
  43. package/dist/ui/disclosure.tsx +0 -20
  44. package/dist/ui/dismiss-layer.ts +0 -87
  45. package/dist/ui/door.ts +0 -140
  46. package/dist/ui/drag.ts +0 -316
  47. package/dist/ui/elements/accordion.tsx +0 -47
  48. package/dist/ui/elements/accordion.works.ts +0 -66
  49. package/dist/ui/elements/aspect-ratio.tsx +0 -31
  50. package/dist/ui/elements/aspect-ratio.works.ts +0 -5
  51. package/dist/ui/elements/avatar.tsx +0 -34
  52. package/dist/ui/elements/avatar.works.ts +0 -16
  53. package/dist/ui/elements/badge.tsx +0 -33
  54. package/dist/ui/elements/badge.works.ts +0 -9
  55. package/dist/ui/elements/bars.tsx +0 -50
  56. package/dist/ui/elements/bars.works.ts +0 -29
  57. package/dist/ui/elements/box.tsx +0 -36
  58. package/dist/ui/elements/breadcrumb.tsx +0 -38
  59. package/dist/ui/elements/breadcrumb.works.ts +0 -24
  60. package/dist/ui/elements/button.tsx +0 -46
  61. package/dist/ui/elements/button.works.tsx +0 -52
  62. package/dist/ui/elements/column.tsx +0 -29
  63. package/dist/ui/elements/consent.tsx +0 -71
  64. package/dist/ui/elements/consent.works.tsx +0 -132
  65. package/dist/ui/elements/donut.tsx +0 -45
  66. package/dist/ui/elements/donut.works.ts +0 -42
  67. package/dist/ui/elements/each.tsx +0 -34
  68. package/dist/ui/elements/each.works.tsx +0 -82
  69. package/dist/ui/elements/empty.tsx +0 -33
  70. package/dist/ui/elements/field.tsx +0 -45
  71. package/dist/ui/elements/field.works.ts +0 -65
  72. package/dist/ui/elements/funnel.tsx +0 -50
  73. package/dist/ui/elements/funnel.works.ts +0 -37
  74. package/dist/ui/elements/gauge.tsx +0 -40
  75. package/dist/ui/elements/gauge.works.ts +0 -42
  76. package/dist/ui/elements/glyph.tsx +0 -30
  77. package/dist/ui/elements/glyph.works.tsx +0 -42
  78. package/dist/ui/elements/grid.tsx +0 -29
  79. package/dist/ui/elements/heatmap.tsx +0 -48
  80. package/dist/ui/elements/heatmap.works.ts +0 -24
  81. package/dist/ui/elements/hit-box.tsx +0 -47
  82. package/dist/ui/elements/hit-box.works.tsx +0 -576
  83. package/dist/ui/elements/image.tsx +0 -29
  84. package/dist/ui/elements/line-chart.tsx +0 -45
  85. package/dist/ui/elements/line-chart.works.ts +0 -41
  86. package/dist/ui/elements/link.tsx +0 -38
  87. package/dist/ui/elements/link.works.ts +0 -25
  88. package/dist/ui/elements/media.tsx +0 -57
  89. package/dist/ui/elements/menu.tsx +0 -51
  90. package/dist/ui/elements/menu.works.tsx +0 -154
  91. package/dist/ui/elements/money.tsx +0 -36
  92. package/dist/ui/elements/money.works.ts +0 -9
  93. package/dist/ui/elements/pagination.tsx +0 -39
  94. package/dist/ui/elements/pagination.works.ts +0 -20
  95. package/dist/ui/elements/progress.tsx +0 -34
  96. package/dist/ui/elements/progress.works.ts +0 -8
  97. package/dist/ui/elements/radio-group.tsx +0 -50
  98. package/dist/ui/elements/radio-group.works.ts +0 -33
  99. package/dist/ui/elements/row.tsx +0 -29
  100. package/dist/ui/elements/scatter.tsx +0 -42
  101. package/dist/ui/elements/scatter.works.ts +0 -38
  102. package/dist/ui/elements/select.tsx +0 -43
  103. package/dist/ui/elements/select.works.ts +0 -5
  104. package/dist/ui/elements/separator.tsx +0 -31
  105. package/dist/ui/elements/skeleton.tsx +0 -31
  106. package/dist/ui/elements/slider.tsx +0 -34
  107. package/dist/ui/elements/slider.works.ts +0 -5
  108. package/dist/ui/elements/sparkline.tsx +0 -37
  109. package/dist/ui/elements/sparkline.works.ts +0 -32
  110. package/dist/ui/elements/spinner.tsx +0 -33
  111. package/dist/ui/elements/stack.tsx +0 -29
  112. package/dist/ui/elements/stat.tsx +0 -36
  113. package/dist/ui/elements/stat.works.ts +0 -14
  114. package/dist/ui/elements/surface.tsx +0 -36
  115. package/dist/ui/elements/surface.works.ts +0 -9
  116. package/dist/ui/elements/table.tsx +0 -62
  117. package/dist/ui/elements/table.works.ts +0 -30
  118. package/dist/ui/elements/tabs.tsx +0 -47
  119. package/dist/ui/elements/tabs.works.ts +0 -84
  120. package/dist/ui/elements/text.tsx +0 -33
  121. package/dist/ui/elements/textarea.tsx +0 -38
  122. package/dist/ui/elements/textarea.works.ts +0 -5
  123. package/dist/ui/elements/time.tsx +0 -40
  124. package/dist/ui/elements/time.works.ts +0 -29
  125. package/dist/ui/elements/toggle.tsx +0 -34
  126. package/dist/ui/elements/toggle.works.ts +0 -5
  127. package/dist/ui/elements/tooltip.tsx +0 -40
  128. package/dist/ui/elements/tooltip.works.ts +0 -77
  129. package/dist/ui/elements/value.tsx +0 -38
  130. package/dist/ui/elements/value.works.ts +0 -9
  131. package/dist/ui/elements.ts +0 -127
  132. package/dist/ui/focus-scope.ts +0 -121
  133. package/dist/ui/fonts.ts +0 -355
  134. package/dist/ui/generators/dev.d.ts +0 -1
  135. package/dist/ui/generators/dev.js +0 -61
  136. package/dist/ui/generators/dev.ts +0 -74
  137. package/dist/ui/generators/elements.ts +0 -796
  138. package/dist/ui/generators/i18n.ts +0 -283
  139. package/dist/ui/generators/icons.ts +0 -207
  140. package/dist/ui/generators/react.ts +0 -267
  141. package/dist/ui/generators/routes.ts +0 -847
  142. package/dist/ui/generators/show.ts +0 -261
  143. package/dist/ui/generators/styles.ts +0 -677
  144. package/dist/ui/generators/variants.ts +0 -196
  145. package/dist/ui/i18n/catalogs/en.ts +0 -191
  146. package/dist/ui/i18n/format.ts +0 -221
  147. package/dist/ui/i18n/locales.ts +0 -33
  148. package/dist/ui/i18n/parse.ts +0 -315
  149. package/dist/ui/i18n/translator.ts +0 -31
  150. package/dist/ui/i18n/types.ts +0 -96
  151. package/dist/ui/icons.ts +0 -331
  152. package/dist/ui/islands/map.ts +0 -58
  153. package/dist/ui/localizations.ts +0 -75
  154. package/dist/ui/material.ts +0 -236
  155. package/dist/ui/materials/mat_bordered.ts +0 -13
  156. package/dist/ui/materials/mat_ceramic.ts +0 -13
  157. package/dist/ui/materials/mat_clay.ts +0 -13
  158. package/dist/ui/materials/mat_elevated.ts +0 -24
  159. package/dist/ui/materials/mat_flat.ts +0 -34
  160. package/dist/ui/materials/mat_glass.ts +0 -20
  161. package/dist/ui/materials/mat_lit.ts +0 -13
  162. package/dist/ui/materials/mat_moulded.ts +0 -11
  163. package/dist/ui/materials.ts +0 -197
  164. package/dist/ui/modes.ts +0 -44
  165. package/dist/ui/navigation.tsx +0 -29
  166. package/dist/ui/positioning.ts +0 -90
  167. package/dist/ui/presented.tsx +0 -119
  168. package/dist/ui/provider.tsx +0 -184
  169. package/dist/ui/render-loop.ts +0 -133
  170. package/dist/ui/roving-focus.ts +0 -161
  171. package/dist/ui/screens.ts +0 -141
  172. package/dist/ui/scroll-lock.ts +0 -28
  173. package/dist/ui/series.ts +0 -97
  174. package/dist/ui/signals.ts +0 -386
  175. package/dist/ui/styleguide.ts +0 -231
  176. package/dist/ui/styleguides.ts +0 -374
  177. package/dist/ui/themes.ts +0 -22
  178. package/dist/ui/token.ts +0 -78
  179. package/dist/ui/tokens/shadow.ts +0 -25
  180. package/dist/ui/tokens.ts +0 -234
  181. package/dist/ui/validators.ts +0 -232
  182. package/dist/ui/variant.ts +0 -352
  183. package/dist/ui/variants/var_avatar-round.ts +0 -14
  184. package/dist/ui/variants/var_bubble-me.ts +0 -14
  185. package/dist/ui/variants/var_bubble-them.ts +0 -13
  186. package/dist/ui/variants/var_button-border.ts +0 -76
  187. package/dist/ui/variants/var_button-cta.ts +0 -76
  188. package/dist/ui/variants/var_button-destructive-ghost.ts +0 -72
  189. package/dist/ui/variants/var_button-destructive.ts +0 -76
  190. package/dist/ui/variants/var_button-fill.ts +0 -76
  191. package/dist/ui/variants/var_button-ghost.ts +0 -72
  192. package/dist/ui/variants/var_button-glass.ts +0 -55
  193. package/dist/ui/variants/var_button-normal.ts +0 -76
  194. package/dist/ui/variants/var_button-quiet.ts +0 -74
  195. package/dist/ui/variants/var_card.ts +0 -17
  196. package/dist/ui/variants/var_chip.ts +0 -13
  197. package/dist/ui/variants/var_choice.ts +0 -15
  198. package/dist/ui/variants/var_framed.ts +0 -10
  199. package/dist/ui/variants/var_glyph-plain.ts +0 -11
  200. package/dist/ui/variants/var_input.ts +0 -43
  201. package/dist/ui/variants/var_link-inline.ts +0 -29
  202. package/dist/ui/variants/var_list-row.ts +0 -34
  203. package/dist/ui/variants/var_rule.ts +0 -10
  204. package/dist/ui/variants/var_skeleton-block.ts +0 -12
  205. package/dist/ui/variants/var_spinner-turning.ts +0 -12
  206. package/dist/ui/variants/var_stat-tile.ts +0 -13
  207. package/dist/ui/variants/var_tab-bar.ts +0 -15
  208. package/dist/ui/variants/var_tap-target.ts +0 -25
  209. package/dist/ui/variants.ts +0 -64
  210. package/dist/ui/wrappers.tsx +0 -429
@@ -1,47 +0,0 @@
1
- 'use client'
2
-
3
- import type { ReactNode } from 'react'
4
- import type { Subject } from '../drag.ts'
5
-
6
- import type { Dispatching } from '../checked.ts'
7
- import { type Element, element, useSurface } from '../wrappers.tsx'
8
- import { shown, uses, wires } from './hit-box.works.tsx'
9
-
10
-
11
- export interface HitBoxProps {
12
- readonly children: ReactNode
13
- readonly gesture: string
14
- readonly tuning?: Readonly<Record<string, number>>
15
- readonly axis?: string
16
- readonly grabs?: Subject | readonly Subject[]
17
- readonly accepts: readonly string[]
18
- readonly collects?: Subject | readonly Subject[]
19
- readonly dwell?: number
20
- readonly moves?: boolean
21
- readonly onDrop?: () => void
22
- readonly input?: Readonly<Record<string, unknown>>
23
- readonly dispatch?: keyof Dispatching
24
- readonly invokeOn?: 'press' | 'shown'
25
- }
26
-
27
- export const HitBox: Element<'HitBox', HitBoxProps> = element(
28
- 'HitBox',
29
- (props: HitBoxProps, className) => {
30
- const kept = uses(props)
31
- const surface = useSurface({ className,
32
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
33
- holds: [
34
- ...(kept.holds ?? []),
35
- ],
36
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
37
- const wired = wires(props, kept, surface)
38
- return (
39
- <div className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})} {...(wired.on ?? {})}>
40
- {props.children}
41
- <div className="e-hit-box-carried">{shown(props, surface, kept, wired).carried}</div>
42
- </div>
43
- )
44
- },
45
- 'e-hit-box',
46
- {"tapTarget":"thread-tap-target"},
47
- )
@@ -1,576 +0,0 @@
1
- 'use client'
2
-
3
- import { gestureFor, gestureNames, holds as declared, recognise, type GesturePhase, type PointerSample } from '../signals.ts'
4
- import { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore, type PointerEvent, type ReactNode } from 'react'
5
- import { createPortal } from 'react-dom'
6
-
7
- import {
8
- accepted,
9
- beginDrag,
10
- claimGesture,
11
- collect,
12
- currentLayout,
13
- dragSession,
14
- dragStatusOf,
15
- dwell,
16
- endDrag,
17
- innermostTarget,
18
- moveDrag,
19
- pullEdges,
20
- registerTarget,
21
- releaseGesture,
22
- subscribeDrag,
23
- type DragSession,
24
- type DragStatus,
25
- type DropTarget,
26
- } from '../drag.ts'
27
- import { measured, placed } from '../charts.tsx'
28
- import { nameOf } from '../token.ts'
29
- import { useSurface, useUi } from '../wrappers.tsx'
30
- import type { Dispatching } from '../checked.ts'
31
- import type { HitBoxProps } from './hit-box.tsx'
32
-
33
- export type Axis = 'x' | 'y' | 'both'
34
-
35
- const held = (row: string): string => `var(${nameOf(row)})`
36
-
37
- const TOUCH_ACTION: Record<Axis, string> = {
38
- x: 'touch-down',
39
- y: 'touch-across',
40
- both: 'touch-own',
41
- }
42
-
43
- const wallClock = (): number => (typeof performance === 'object' ? performance.now() : Date.now()) / 1000
44
-
45
- const settleMs = (): number => {
46
- if (typeof document !== 'object') return 0
47
- const said = getComputedStyle(document.documentElement).getPropertyValue('--motion-normal').trim()
48
- const ms = /^([\d.]+)ms$/.exec(said)
49
- if (ms !== null) return Number(ms[1])
50
- const seconds = /^([\d.]+)s$/.exec(said)
51
- return seconds === null ? 0 : Number(seconds[1]) * 1000
52
- }
53
-
54
- const SWING_AT = 220
55
- const SWING_MAX = 12
56
-
57
- const STIFFNESS = 180
58
- const DAMPING = 14
59
- const SETTLED = 0.05
60
-
61
- const EMPTY: Readonly<Record<string, number>> = {}
62
-
63
- interface Tracked {
64
- readonly id: number
65
- x: number
66
- y: number
67
- readonly startX: number
68
- readonly startY: number
69
- readonly startT: number
70
- down: boolean
71
- }
72
-
73
- export function uses(props: HitBoxProps) {
74
- const id = useId()
75
- const { edgeScroll: pull } = useUi()
76
-
77
- const claimed = gestureFor(props.gesture)
78
- if (claimed === undefined) {
79
- throw new Error(
80
- `<HitBox gesture="${props.gesture}"> — nothing registered that gesture, so this region would ` +
81
- `take the pointer and do nothing. Registered: ${gestureNames().join(' · ')}. Contribute one with defineGesture.`,
82
- )
83
- }
84
-
85
- const [down, setDown] = useState(false)
86
- const [phase, setPhase] = useState<GesturePhase>('idle')
87
- const [produced, setProduced] = useState<Readonly<Record<string, number>>>({})
88
-
89
- const released = useRef(Number.NEGATIVE_INFINITY)
90
-
91
- const took = useRef<{
92
- readonly width: number
93
- readonly height: number
94
- readonly gripX: number
95
- readonly gripY: number
96
- } | null>(null)
97
-
98
- const [pointer, setPointer] = useState<{ readonly x: number; readonly y: number } | null>(null)
99
-
100
- const [settling, setSettling] = useState(false)
101
-
102
- const speed = useRef(0)
103
- const moved = useRef(0)
104
-
105
- const [lean, setLean] = useState(0)
106
- const leanSpeed = useRef(0)
107
- const lastTick = useRef(0)
108
-
109
- const grabbed = useRef(props.grabs)
110
- grabbed.current = props.grabs
111
- const grabsKey = props.grabs === undefined ? '' : JSON.stringify(props.grabs)
112
-
113
- const tuned = useRef(props.tuning)
114
- tuned.current = props.tuning
115
- const tuningKey = props.tuning === undefined ? '' : JSON.stringify(props.tuning)
116
-
117
- const node = useRef<HTMLDivElement | null>(null)
118
-
119
- const clockNow = useRef(0)
120
-
121
- const wants = props.gesture === 'pinch' || props.gesture === 'rotate'
122
- const synthesised = useRef<number | null>(null)
123
-
124
- useEffect(() => {
125
- const surfaceNode = node.current
126
- if (!wants || surfaceNode === null) return undefined
127
-
128
- const SPAN = 100
129
-
130
- const put = (scale: number, radians: number): void => {
131
- const box = surfaceNode.getBoundingClientRect()
132
- const cx = box.left + box.width / 2
133
- const cy = box.top + box.height / 2
134
- const reach = (SPAN / 2) * scale
135
- const dxr = Math.cos(radians) * reach
136
- const dyr = Math.sin(radians) * reach
137
-
138
- tracked.current.set(-1, { id: -1, x: cx + dxr, y: cy + dyr, startX: cx + SPAN / 2, startY: cy, startT: clockNow.current, down: true })
139
- tracked.current.set(-2, { id: -2, x: cx - dxr, y: cy - dyr, startX: cx - SPAN / 2, startY: cy, startT: clockNow.current, down: true })
140
- at.current = { x: cx, y: cy }
141
- setDown(true)
142
-
143
- if (synthesised.current !== null) clearTimeout(synthesised.current)
144
- synthesised.current = window.setTimeout(() => {
145
- tracked.current.delete(-1)
146
- tracked.current.delete(-2)
147
- setDown(false)
148
- releaseGesture(id)
149
- setPhase('idle')
150
- setProduced(EMPTY)
151
- }, 140)
152
- }
153
-
154
- let scale = 1
155
- let radians = 0
156
-
157
- const onWheel = (e: WheelEvent): void => {
158
- if (!e.ctrlKey) return
159
- e.preventDefault()
160
- scale = Math.min(4, Math.max(0.25, scale * Math.exp(-e.deltaY / 100)))
161
- put(scale, radians)
162
- }
163
-
164
- const onGesture = (e: Event): void => {
165
- const held = e as Event & { scale?: number; rotation?: number }
166
- e.preventDefault()
167
- scale = held.scale ?? scale
168
- radians = ((held.rotation ?? 0) * Math.PI) / 180
169
- put(scale, radians)
170
- }
171
-
172
- surfaceNode.addEventListener('wheel', onWheel, { passive: false })
173
- surfaceNode.addEventListener('gesturechange', onGesture)
174
- return () => {
175
- surfaceNode.removeEventListener('wheel', onWheel)
176
- surfaceNode.removeEventListener('gesturechange', onGesture)
177
- if (synthesised.current !== null) clearTimeout(synthesised.current)
178
- }
179
- }, [wants, id])
180
-
181
- const tracked = useRef(new Map<number, Tracked>())
182
- const at = useRef({ x: 0, y: 0 })
183
- const lastFrame = useRef(0)
184
-
185
- const key = props.accepts?.join(',') ?? ''
186
- const status = useSyncExternalStore(
187
- subscribeDrag,
188
- useCallback(() => dragStatusOf(id, key === '' ? undefined : key.split(',')), [id, key]),
189
- useCallback((): DragStatus => 'idle', []),
190
- )
191
-
192
- const carrying = status === 'carrying'
193
- const raises = claimed.raises
194
-
195
- return {
196
- on: {},
197
- pull,
198
- animates: down || carrying || settling,
199
- holds: [
200
-
201
- ...(phase === 'active' || phase === 'done'
202
- ? declared.filter((one) => raises.some((s) => s.name === one.id)).map((one) => one.id)
203
- : []),
204
- ...(carrying ? (['carrying'] as const) : []),
205
- ...(status === 'over' && dwell() >= (props.dwell ?? 0) ? (['draggedOver'] as const) : []),
206
- ...(status === 'refusing' ? (['refusing'] as const) : []),
207
- ],
208
- id,
209
- claimed,
210
- down,
211
- setDown,
212
- phase,
213
- setPhase,
214
- produced,
215
- setProduced,
216
- released,
217
- took,
218
- pointer,
219
- setPointer,
220
- settling,
221
- setSettling,
222
- speed,
223
- moved,
224
- lean,
225
- setLean,
226
- leanSpeed,
227
- lastTick,
228
- grabbed,
229
- grabsKey,
230
- tuned,
231
- tuningKey,
232
- node,
233
- clockNow,
234
- wants,
235
- synthesised,
236
- tracked,
237
- at,
238
- lastFrame,
239
- key,
240
- status,
241
- carrying,
242
- raises,
243
- }
244
- }
245
-
246
- export function wires(props: HitBoxProps, kept: ReturnType<typeof uses>, surface: ReturnType<typeof useSurface>) {
247
- const { dispatch: invoke } = useUi()
248
- const { pull, id, down, setDown, phase, setPhase, setProduced, released, took, pointer, setPointer, settling, setSettling, speed, moved, lean, setLean, leanSpeed, lastTick, grabbed, grabsKey, tuned, tuningKey, node, clockNow, tracked, at, lastFrame, key, carrying } = kept
249
-
250
- useEffect(() => {
251
- if (!down) return undefined
252
- const give = (e: globalThis.PointerEvent): void => {
253
-
254
- const session = dragSession()
255
- if (session?.from === id) {
256
- const target = innermostTarget(currentLayout().hitBoxesAt(e.clientX, e.clientY))
257
- const finished = endDrag()
258
- if (target?.onDrop !== undefined && finished !== undefined && accepted(target.accepts, finished)) {
259
- target.onDrop(finished)
260
- }
261
- }
262
- tracked.current.clear()
263
- setDown(false)
264
- setPointer(null)
265
- took.current = null
266
- releaseGesture(id)
267
-
268
- setPhase('idle')
269
- }
270
- const follow = (e: globalThis.PointerEvent): void => {
271
- advance(e.pointerId, e.clientX, e.clientY)
272
- }
273
- window.addEventListener('pointermove', follow)
274
- window.addEventListener('pointerup', give)
275
- window.addEventListener('pointercancel', give)
276
- return () => {
277
- window.removeEventListener('pointermove', follow)
278
- window.removeEventListener('pointerup', give)
279
- window.removeEventListener('pointercancel', give)
280
- }
281
- }, [down, id])
282
-
283
- useEffect(
284
- () => () => {
285
- releaseGesture(id)
286
- if (dragSession()?.from === id) endDrag()
287
- },
288
- [id],
289
- )
290
-
291
- const onDrop = props.onDrop
292
- const named: keyof Dispatching | undefined = props.dispatch
293
- const carriedInput = props.input
294
- const collects = props.collects
295
- useEffect(() => {
296
- if (props.accepts === undefined) return undefined
297
- const target: DropTarget = {
298
- id,
299
- accepts: key.split(','),
300
- ...(props.dwell === undefined ? {} : { dwell: props.dwell }),
301
- ...(collects === undefined ? {} : { collects: () => collects }),
302
- ...(onDrop === undefined && named === undefined
303
- ? {}
304
- : {
305
- onDrop: (finished: DragSession) => {
306
- if (onDrop !== undefined) onDrop()
307
- if (named === undefined) return
308
- if (invoke === undefined) throw new Error(`\`${named}\` cannot be invoked from here — no dispatch is wired`)
309
- void invoke(named, { ...(carriedInput ?? {}), ...(finished.subject[0] ?? {}) })
310
- },
311
- }),
312
- }
313
- return registerTarget(target)
314
- }, [id, key, props.accepts, props.dwell, collects, onDrop, named, carriedInput, invoke])
315
-
316
- const t = surface.t
317
- clockNow.current = t
318
- useEffect(() => {
319
- if (!down) return
320
- const dt = Math.max(0, t - lastFrame.current)
321
- lastFrame.current = t
322
-
323
- const pointers: PointerSample[] = [...tracked.current.values()].map((p) => ({
324
- id: p.id,
325
- x: p.x,
326
- y: p.y,
327
- startX: p.startX,
328
- startY: p.startY,
329
- held: t - p.startT,
330
- down: p.down,
331
- }))
332
-
333
- const result = recognise(props.gesture, {
334
- pointers,
335
- t,
336
- params: tuned.current ?? {},
337
-
338
- sinceRelease: wallClock() - released.current,
339
- })
340
-
341
- const takesPointer = grabbed.current !== undefined
342
- const mine = result.phase === 'idle' || !takesPointer ? true : claimGesture(id)
343
- setPhase(mine ? result.phase : 'idle')
344
-
345
- setProduced(mine && result.values !== undefined ? result.values : EMPTY)
346
- if (!mine) return
347
-
348
- const session = dragSession()
349
-
350
- if (result.phase !== 'idle' && grabbed.current !== undefined && session === undefined) {
351
- beginDrag({ gesture: props.gesture, subject: grabbed.current, from: id, x: at.current.x, y: at.current.y, t })
352
- return
353
- }
354
-
355
- if (session === undefined || session.from !== id) return
356
-
357
- const target = innermostTarget(currentLayout().hitBoxesAt(at.current.x, at.current.y))
358
- moveDrag({ x: at.current.x, y: at.current.y, ...(target === undefined ? {} : { over: target.id }), t })
359
-
360
- if (target?.collects !== undefined && dwell() >= (target.dwell ?? 0)) collect(target.collects())
361
-
362
- pullEdges(at.current.x, at.current.y, dt, pull ?? {})
363
-
364
- }, [down, t, pointer, id, props.gesture, grabsKey, tuningKey, pull])
365
-
366
- useEffect(() => {
367
- if (phase !== 'done') return undefined
368
- const frame = requestAnimationFrame(() => {
369
- releaseGesture(id)
370
- setPhase('idle')
371
- setProduced(EMPTY)
372
- })
373
- return () => cancelAnimationFrame(frame)
374
- }, [phase, id])
375
-
376
- const onPointerDown = (e: PointerEvent<HTMLDivElement>): void => {
377
-
378
- if (grabbed.current !== undefined) {
379
- e.currentTarget.setPointerCapture?.(e.pointerId)
380
-
381
- e.preventDefault()
382
- }
383
-
384
- const box = e.currentTarget.getBoundingClientRect()
385
- setPointer({ x: e.clientX, y: e.clientY })
386
- took.current =
387
- box.width > 0 && box.height > 0
388
- ? { width: box.width, height: box.height, gripX: e.clientX - box.left, gripY: e.clientY - box.top }
389
- : null
390
- speed.current = 0
391
- moved.current = wallClock()
392
- tracked.current.set(e.pointerId, { id: e.pointerId, x: e.clientX, y: e.clientY, startX: e.clientX, startY: e.clientY, startT: t, down: true })
393
- at.current = { x: e.clientX, y: e.clientY }
394
- lastFrame.current = t
395
- setDown(true)
396
- }
397
-
398
- const advance = (pointerId: number, x: number, y: number): void => {
399
- const p = tracked.current.get(pointerId)
400
- if (p === undefined) return
401
- if (p.x === x && p.y === y) return
402
- const e = { pointerId, clientX: x, clientY: y }
403
-
404
- const at2 = wallClock()
405
- const dt = Math.max(1 / 240, at2 - moved.current)
406
- const raw = (e.clientX - p.x) / dt
407
- speed.current = speed.current * 0.7 + raw * 0.3
408
- moved.current = at2
409
-
410
- p.x = e.clientX
411
- p.y = e.clientY
412
- at.current = { x: e.clientX, y: e.clientY }
413
- setPointer({ x: e.clientX, y: e.clientY })
414
- }
415
-
416
- const onPointerMove = (e: PointerEvent<HTMLDivElement>): void => {
417
- advance(e.pointerId, e.clientX, e.clientY)
418
- }
419
-
420
- const finish = (e: PointerEvent<HTMLDivElement>, cancelled: boolean): void => {
421
- const p = tracked.current.get(e.pointerId)
422
- if (p !== undefined) {
423
-
424
- p.x = e.clientX
425
- p.y = e.clientY
426
- p.down = false
427
- at.current = { x: e.clientX, y: e.clientY }
428
- }
429
-
430
- const session = dragSession()
431
- if (session?.from === id) {
432
-
433
- const target = cancelled ? undefined : innermostTarget(currentLayout().hitBoxesAt(e.clientX, e.clientY))
434
- const finished = endDrag()
435
- if (target?.onDrop !== undefined && finished !== undefined && accepted(target.accepts, finished)) {
436
- target.onDrop(finished)
437
- }
438
-
439
- releaseGesture(id)
440
-
441
- speed.current = 0
442
- }
443
-
444
- const last = p !== undefined && tracked.current.size === 1 && !cancelled ? p : undefined
445
- let completed = false
446
- if (last !== undefined) {
447
- const result = recognise(props.gesture, {
448
- pointers: [
449
- { id: last.id, x: last.x, y: last.y, startX: last.startX, startY: last.startY, held: t - last.startT, down: false },
450
- ],
451
- t,
452
- params: props.tuning ?? {},
453
- sinceRelease: wallClock() - released.current,
454
- })
455
- if (result.phase !== 'idle' && claimGesture(id)) {
456
- setPhase(result.phase)
457
- setProduced(result.values ?? EMPTY)
458
- completed = true
459
- }
460
- }
461
-
462
- tracked.current.delete(e.pointerId)
463
- if (tracked.current.size === 0) {
464
- setDown(false)
465
-
466
- releaseGesture(id)
467
- setPointer(null)
468
- took.current = null
469
- if (!completed) setPhase('idle')
470
-
471
- if (!cancelled) released.current = wallClock()
472
- }
473
- }
474
-
475
- const session = dragSession()
476
- const mine = carrying && session !== undefined && session.from === id
477
-
478
- useEffect(() => {
479
- if (!mine && !settling) {
480
- lastTick.current = t
481
- return
482
- }
483
- const step = Math.min(1 / 20, Math.max(0, t - lastTick.current))
484
- lastTick.current = t
485
- if (step === 0) return
486
-
487
- const target = mine ? Math.max(-SWING_MAX, Math.min(SWING_MAX, speed.current / SWING_AT)) : 0
488
- const force = -STIFFNESS * (lean - target) - DAMPING * leanSpeed.current
489
- const next = lean + leanSpeed.current * step
490
- leanSpeed.current += force * step
491
-
492
- if (!mine && Math.abs(next) < SETTLED && Math.abs(leanSpeed.current) < SETTLED) {
493
- leanSpeed.current = 0
494
- if (lean !== 0) setLean(0)
495
- return
496
- }
497
- setLean(next)
498
- }, [t, mine, settling, lean])
499
-
500
- useEffect(() => {
501
- if (mine) {
502
- setSettling(true)
503
- return undefined
504
- }
505
- if (!settling) return undefined
506
-
507
- if (Math.abs(lean) < SETTLED && Math.abs(leanSpeed.current) < SETTLED) {
508
- const timer = setTimeout(() => setSettling(false), settleMs())
509
- return () => clearTimeout(timer)
510
- }
511
- return undefined
512
- }, [mine, settling, lean])
513
-
514
- const swing = lean
515
-
516
- useEffect(() => {
517
- if (!mine) return undefined
518
- const root = document.documentElement
519
- const was = root.style.userSelect
520
- root.style.userSelect = held('picking-off')
521
- return () => {
522
- root.style.userSelect = was
523
- }
524
- }, [mine])
525
-
526
- const travels = props.moves !== false
527
-
528
- const flying = mine && travels && took.current !== null && pointer !== null
529
-
530
- const layer =
531
- flying && took.current !== null && pointer !== null ? (
532
-
533
- <div
534
- aria-hidden
535
- className={placed('hit-box', 'carried')}
536
- style={measured({
537
- width: `${String(took.current.width)}px`,
538
- height: `${String(took.current.height)}px`,
539
- carry:
540
- `translate3d(${String(pointer.x - took.current.gripX)}px, ${String(pointer.y - took.current.gripY)}px, 0)` +
541
- ' var(--transform-carried, scale(1)) var(--transform-carried-turn, rotate(0deg))' +
542
- ` rotate(${swing.toFixed(2)}deg)`,
543
- grip: `${took.current.gripX.toFixed(1)}px ${took.current.gripY.toFixed(1)}px`,
544
- })}
545
- >
546
- {props.children}
547
- </div>
548
- ) : null
549
-
550
- return {
551
- on: {
552
- ref: node,
553
- 'data-hitbox': id,
554
- style: measured({
555
- touch: held(props.grabs === undefined ? 'touch-page' : (TOUCH_ACTION[(props.axis ?? 'both') as Axis] ?? 'touch-own')),
556
- pick: held(props.grabs === undefined ? 'picking-open' : 'picking-off'),
557
- shown: held(flying ? 'visibility-hidden' : 'visibility-shown'),
558
- flow: held(flying ? 'position-lifted' : 'position-flow'),
559
- }),
560
- onPointerDown,
561
- onPointerMove,
562
- onPointerUp: (e: PointerEvent<HTMLDivElement>) => finish(e, false),
563
- onPointerCancel: (e: PointerEvent<HTMLDivElement>) => finish(e, true),
564
- },
565
- layer,
566
- }
567
- }
568
-
569
- export function shown(
570
- _props: HitBoxProps,
571
- _surface: ReturnType<typeof useSurface>,
572
- _kept: ReturnType<typeof uses>,
573
- wired: ReturnType<typeof wires>,
574
- ): Readonly<Record<string, ReactNode>> {
575
- return { carried: wired.layer === null ? null : createPortal(wired.layer, document.body) }
576
- }
@@ -1,29 +0,0 @@
1
- 'use client'
2
-
3
- import type { MessageKey } from '../content.tsx'
4
- import { roleClass } from '../fonts.ts'
5
-
6
- import type { Dispatching } from '../checked.ts'
7
- import { type Element, element, useSurface } from '../wrappers.tsx'
8
-
9
-
10
- export interface ImageProps {
11
- readonly src: string
12
- readonly altKey: MessageKey
13
- readonly dispatch?: keyof Dispatching
14
- readonly input?: Readonly<Record<string, unknown>>
15
- readonly invokeOn?: 'press' | 'shown'
16
- }
17
-
18
- export const Image: Element<'Image', ImageProps> = element(
19
- 'Image',
20
- (props: ImageProps, className) => {
21
- const surface = useSurface({ className: roleClass(undefined, className),
22
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) }, })
23
- return (
24
- <img className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} src={props.src} alt={surface.text(props.altKey)} />
25
- )
26
- },
27
- 'e-image',
28
- {"framed":"thread-framed"},
29
- )
@@ -1,45 +0,0 @@
1
- 'use client'
2
-
3
- import type { MessageKey } from '../content.tsx'
4
- import type { Row } from '../charts.tsx'
5
- import { roleClass } from '../fonts.ts'
6
-
7
- import type { Dispatching } from '../checked.ts'
8
- import { type Element, element, useSurface } from '../wrappers.tsx'
9
- import { Each } from '../elements/each.tsx'
10
- import { drawing, lines, uses } from './line-chart.works.ts'
11
-
12
-
13
- export interface LineChartProps {
14
- readonly rows: readonly Row[]
15
- readonly by?: string
16
- readonly value: string
17
- readonly titleKey?: MessageKey
18
- readonly dispatch?: keyof Dispatching
19
- readonly input?: Readonly<Record<string, unknown>>
20
- readonly invokeOn?: 'press' | 'shown'
21
- }
22
-
23
- export const LineChart: Element<'LineChart', LineChartProps> = element(
24
- 'LineChart',
25
- (props: LineChartProps, className) => {
26
- const kept = uses(props)
27
- const surface = useSurface({ className: roleClass(undefined, className),
28
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
29
- holds: [
30
- ...(kept.holds ?? []),
31
- ],
32
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
33
- return (
34
- <svg className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})} role="img" {...(props.titleKey === undefined ? {} : { 'aria-label': surface.text(props.titleKey) })}>
35
- <Each rows={lines(props, kept)} by={'group'}>
36
- {(each0, at) => (
37
- <polyline {...drawing(props, kept, each0, at)}></polyline>
38
- )}
39
- </Each>
40
- </svg>
41
- )
42
- },
43
- 'e-line-chart',
44
- {"card":"thread-card"},
45
- )