@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,42 +0,0 @@
1
- import { seriesColour } from '../series.ts'
2
- import { num, useRamp, type Row } from '../charts.tsx'
3
- import type { DonutProps } from './donut.tsx'
4
-
5
- const RADIUS = 40
6
- const ROUND = 2 * Math.PI * RADIUS
7
-
8
- export function uses(props: DonutProps): {
9
- readonly holds?: readonly string[]
10
- readonly animates?: boolean
11
- readonly on?: Readonly<Record<string, unknown>>
12
- readonly ramp: readonly string[]
13
- } {
14
- const ramp = useRamp(props.rows.length)
15
- return {
16
- ...(props.rows.length === 0 ? { holds: ['empty'] as const } : {}),
17
- on: { viewBox: '0 0 100 100' },
18
- ramp,
19
- }
20
- }
21
-
22
- export function slice(
23
- props: DonutProps,
24
- kept: { readonly ramp: readonly string[] },
25
- row: Row,
26
- at: number,
27
- ): Readonly<Record<string, string | number>> {
28
- const whole = props.rows.reduce((sum: number, one: Row) => sum + num(one, props.value), 0) || 1
29
- const share = num(row, props.value) / whole
30
- const before = props.rows.slice(0, at).reduce((sum: number, one: Row) => sum + num(one, props.value), 0) / whole
31
- return {
32
- cx: 50,
33
- cy: 50,
34
- r: RADIUS,
35
- fill: 'none',
36
- stroke: seriesColour(kept.ramp, at),
37
- strokeWidth: 16,
38
- strokeDasharray: `${(share * ROUND).toFixed(2)} ${ROUND.toFixed(2)}`,
39
- strokeDashoffset: (-before * ROUND).toFixed(2),
40
- transform: 'rotate(-90 50 50)',
41
- }
42
- }
@@ -1,34 +0,0 @@
1
- 'use client'
2
-
3
- import type { ReactNode } from 'react'
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 { shows } from './each.works.tsx'
10
-
11
-
12
- export interface EachProps {
13
- readonly rows: readonly object[]
14
- readonly by: string
15
- readonly children: (row: Row, at: number) => ReactNode
16
- readonly settles?: string
17
- readonly paced?: string
18
- readonly dispatch?: keyof Dispatching
19
- readonly input?: Readonly<Record<string, unknown>>
20
- readonly invokeOn?: 'press' | 'shown'
21
- }
22
-
23
- export const Each: Element<'Each', EachProps> = element(
24
- 'Each',
25
- (props: EachProps, className) => {
26
- const surface = useSurface({ className: roleClass(undefined, className),
27
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) }, })
28
- return (
29
- shows(props, surface)
30
- )
31
- },
32
- 'e-each',
33
- {},
34
- )
@@ -1,82 +0,0 @@
1
- import { Fragment, useLayoutEffect, useRef, type ReactNode } from 'react'
2
-
3
- import type { Row } from '../charts.tsx'
4
- import { nameOf } from '../token.ts'
5
- import type { EachProps } from './each.tsx'
6
-
7
- function keyOf(row: object, by: string, at: number): string {
8
- const value = (row as Record<string, unknown>)[by]
9
- if (value === undefined || value === null) {
10
- throw new Error(
11
- `Each: row ${String(at)} has no value for "${by}", so it has no key. React reuses DOM by key, and a ` +
12
- "row without one is matched by position — which silently keeps the previous row's state when " +
13
- 'the list reorders. Name the field that identifies a row in this view.',
14
- )
15
- }
16
- return String(value)
17
- }
18
-
19
- function following(marker: HTMLElement): readonly HTMLElement[] {
20
- const found: HTMLElement[] = []
21
- let node = marker.nextElementSibling
22
- while (node !== null) {
23
- found.push(node as HTMLElement)
24
- node = node.nextElementSibling
25
- }
26
- return found
27
- }
28
-
29
- function boxes(marker: HTMLElement, ids: readonly string[]): Map<string, DOMRect> {
30
- const found = new Map<string, DOMRect>()
31
- following(marker).forEach((node, at) => {
32
- const id = ids[at]
33
- if (id !== undefined) found.set(id, node.getBoundingClientRect())
34
- })
35
- return found
36
- }
37
-
38
- function settle(marker: HTMLElement, ids: readonly string[], was: Map<string, DOMRect>, over: string, paced: string): void {
39
- following(marker).forEach((node, at) => {
40
- const id = ids[at]
41
- if (id === undefined) return
42
- const before = was.get(id)
43
- if (before === undefined) return
44
- const after = node.getBoundingClientRect()
45
- const dx = before.left - after.left
46
- const dy = before.top - after.top
47
- if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) return
48
-
49
- node.style.transition = `transform var(${nameOf('motion-instant')})`
50
- node.style.transform = `translate(${String(dx)}px, ${String(dy)}px)`
51
- void node.offsetWidth
52
- node.style.transition = `transform var(--${over}) var(--${paced})`
53
- node.style.transform = ''
54
- })
55
- }
56
-
57
- export function shows(props: EachProps, _surface: unknown): ReactNode {
58
- const within = useRef<HTMLElement | null>(null)
59
- const was = useRef<Map<string, DOMRect>>(new Map())
60
- const drawn = useRef('')
61
-
62
- const order = props.rows.map((row, at) => keyOf(row, props.by, at)).join('|')
63
-
64
- useLayoutEffect(() => {
65
- const marker = within.current
66
- if (marker === null || props.settles === undefined || props.paced === undefined) return
67
- const ids = order === '' ? [] : order.split('|')
68
- const moved = drawn.current !== '' && drawn.current !== order
69
- if (moved) settle(marker, ids, was.current, props.settles, props.paced)
70
- drawn.current = order
71
- was.current = boxes(marker, ids)
72
- }, [order, props.settles, props.paced])
73
-
74
- return (
75
- <>
76
- <span ref={within} hidden />
77
- {props.rows.map((row, at) => (
78
- <Fragment key={keyOf(row, props.by, at)}>{props.children(row as Row, at)}</Fragment>
79
- ))}
80
- </>
81
- )
82
- }
@@ -1,33 +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 EmptyProps {
11
- readonly labelKey: MessageKey
12
- readonly dispatch?: keyof Dispatching
13
- readonly input?: Readonly<Record<string, unknown>>
14
- readonly invokeOn?: 'press' | 'shown'
15
- }
16
-
17
- export const Empty: Element<'Empty', EmptyProps> = element(
18
- 'Empty',
19
- (props: EmptyProps, className) => {
20
- const surface = useSurface({ className: roleClass(undefined, className),
21
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
22
- holds: [
23
- 'empty' as const,
24
- ], })
25
- return (
26
- <div className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y}>
27
- {surface.text(props.labelKey)}
28
- </div>
29
- )
30
- },
31
- 'e-empty',
32
- {"card":"thread-card"},
33
- )
@@ -1,45 +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
- import { shown, entry, uses } from './field.works.ts'
9
-
10
-
11
- export interface FieldProps {
12
- readonly name: string
13
- readonly validator: string
14
- readonly labelKey: MessageKey
15
- readonly value?: string
16
- readonly onValue?: (said: string) => void
17
- readonly context?: Readonly<Record<string, unknown>>
18
- readonly autocomplete?: string
19
- readonly disabled?: boolean
20
- readonly dispatch?: keyof Dispatching
21
- readonly input?: Readonly<Record<string, unknown>>
22
- readonly invokeOn?: 'press' | 'shown'
23
- }
24
-
25
- export const Field: Element<'Field', FieldProps> = element(
26
- 'Field',
27
- (props: FieldProps, className) => {
28
- const kept = uses(props)
29
- const surface = useSurface({ className: roleClass(undefined, className),
30
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
31
- holds: [
32
- ...(kept.holds ?? []),
33
- ],
34
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
35
- return (
36
- <label className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})}>
37
- <span>{surface.text(props.labelKey)}</span>
38
- <input {...entry(props, kept)} />
39
- <span>{shown(props, surface, kept).fault}</span>
40
- </label>
41
- )
42
- },
43
- 'e-field',
44
- {"input":"thread-input"},
45
- )
@@ -1,65 +0,0 @@
1
- import { useState, type ReactNode } from 'react'
2
-
3
- import { acceptsKeystroke, applyMask, entryConstraints, evaluateMoney, runValidator } from '../validators.ts'
4
- import type { Surface } from '../door.ts'
5
- import type { FieldProps } from './field.tsx'
6
-
7
- export function uses(props: FieldProps): {
8
- readonly holds?: readonly string[]
9
- readonly animates?: boolean
10
- readonly on?: Readonly<Record<string, unknown>>
11
- readonly wrong: string | null
12
- readonly said: (to: string | null) => void
13
- } {
14
- const [wrong, said] = useState<string | null>(null)
15
- return { ...(props.disabled === true ? { holds: ['disabled'] as const } : {}), wrong, said }
16
- }
17
-
18
- export function entry(
19
- props: FieldProps,
20
- kept: { readonly wrong: string | null; readonly said: (to: string | null) => void },
21
- ): Readonly<Record<string, unknown>> {
22
- const rules = entryConstraints(props.validator, props.context ?? {})
23
- return {
24
- name: props.name,
25
- ...(rules.secret === true ? { type: 'password' } : {}),
26
- ...(props.autocomplete ?? rules.autocomplete === undefined
27
- ? {}
28
- : { autoComplete: props.autocomplete ?? rules.autocomplete }),
29
- ...(rules.keyboard === 'text' ? {} : { inputMode: rules.keyboard }),
30
- ...(rules.maxChars === undefined ? {} : { maxLength: rules.maxChars }),
31
- value: props.value ?? '',
32
- ...(kept.wrong === null ? {} : { 'aria-invalid': true }),
33
-
34
- onChange: (event: { readonly target: { readonly value: string } }): void => {
35
- let kept_ = ''
36
- for (const ch of event.target.value) {
37
- if (acceptsKeystroke(props.validator, kept_, ch)) kept_ += ch
38
- }
39
- props.onValue?.(applyMask(props.validator, kept_))
40
- },
41
-
42
- onBlur: (): void => {
43
- const text = props.value ?? ''
44
- let judged: unknown = text
45
- if (rules.entry === 'calculator') {
46
- const currency = props.context?.['currency']
47
- const resolved = typeof currency === 'string' ? evaluateMoney(text, currency) : undefined
48
- if (resolved !== undefined) {
49
- judged = { amount: resolved.amount, currency }
50
- props.onValue?.(String(resolved.amount))
51
- }
52
- }
53
- const result = runValidator(props.validator, judged, props.context ?? {})
54
- kept.said(result.ok ? null : result.errorKey)
55
- },
56
- }
57
- }
58
-
59
- export function shown(
60
- _props: FieldProps,
61
- surface: Surface,
62
- kept: { readonly wrong: string | null },
63
- ): Readonly<Record<string, ReactNode>> {
64
- return { fault: kept.wrong === null ? null : surface.text(kept.wrong) }
65
- }
@@ -1,50 +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
- import { measured as marking } from '../charts.tsx'
7
-
8
- import type { Dispatching } from '../checked.ts'
9
- import { type Element, element, useSurface } from '../wrappers.tsx'
10
- import { Each } from '../elements/each.tsx'
11
- import { shown, sized, uses } from './funnel.works.ts'
12
-
13
-
14
- export interface FunnelProps {
15
- readonly rows: readonly Row[]
16
- readonly by: string
17
- readonly value: string
18
- readonly format?: string
19
- readonly titleKey?: MessageKey
20
- readonly dispatch?: keyof Dispatching
21
- readonly input?: Readonly<Record<string, unknown>>
22
- readonly invokeOn?: 'press' | 'shown'
23
- }
24
-
25
- export const Funnel: Element<'Funnel', FunnelProps> = element(
26
- 'Funnel',
27
- (props: FunnelProps, className) => {
28
- const kept = uses(props)
29
- const surface = useSurface({ className: roleClass(undefined, className),
30
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
31
- holds: [
32
- ...(kept.holds ?? []),
33
- ],
34
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
35
- return (
36
- <div className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})} role="img" {...(props.titleKey === undefined ? {} : { 'aria-label': surface.text(props.titleKey) })}>
37
- <Each rows={props.rows} by={props.by}>
38
- {(each0, at) => (
39
- <span style={marking(sized(props, kept, each0, at))}>
40
- <span>{shown(props, each0, surface, kept).label}</span>
41
- <span>{shown(props, each0, surface, kept).amount}</span>
42
- </span>
43
- )}
44
- </Each>
45
- </div>
46
- )
47
- },
48
- 'e-funnel',
49
- {"card":"thread-card"},
50
- )
@@ -1,37 +0,0 @@
1
- import { formatValue } from '../validators.ts'
2
- import { seriesColour } from '../series.ts'
3
- import { num, str, useRamp, type Row } from '../charts.tsx'
4
- import type { FunnelProps } from './funnel.tsx'
5
-
6
- export function uses(props: FunnelProps): {
7
- readonly holds?: readonly string[]
8
- readonly animates?: boolean
9
- readonly on?: Readonly<Record<string, unknown>>
10
- readonly ramp: readonly string[]
11
- } {
12
- const ramp = useRamp(props.rows.length)
13
- return { ...(props.rows.length === 0 ? { holds: ['empty'] as const } : {}), ramp }
14
- }
15
-
16
- export function sized(
17
- props: FunnelProps,
18
- kept: { readonly ramp: readonly string[] },
19
- row: Row,
20
- at: number,
21
- ): Readonly<Record<string, string | number>> {
22
- const top = num(props.rows[0] as Row, props.value) || 1
23
- const before = at === 0 ? null : num(props.rows[at - 1] as Row, props.value)
24
- const drop = before === null || before === 0 ? null : 1 - num(row, props.value) / before
25
- return {
26
- series: seriesColour(kept.ramp, at),
27
- fraction: `${((num(row, props.value) / top) * 100).toFixed(1)}%`,
28
- ...(drop === null ? {} : { drop: `${(drop * 100).toFixed(1)}%` }),
29
- }
30
- }
31
-
32
- export function shown(props: FunnelProps, row: Row, _surface: unknown, _kept?: unknown): Readonly<Record<string, string>> {
33
- return {
34
- label: str(row, props.by),
35
- amount: String(formatValue(props.format ?? 'number', num(row, props.value))),
36
- }
37
- }
@@ -1,40 +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
- import { track, needle, uses } from './gauge.works.ts'
9
-
10
-
11
- export interface GaugeProps {
12
- readonly value?: number
13
- readonly min?: number
14
- readonly max?: number
15
- readonly labelKey: MessageKey
16
- readonly dispatch?: keyof Dispatching
17
- readonly input?: Readonly<Record<string, unknown>>
18
- readonly invokeOn?: 'press' | 'shown'
19
- }
20
-
21
- export const Gauge: Element<'Gauge', GaugeProps> = element(
22
- 'Gauge',
23
- (props: GaugeProps, className) => {
24
- const kept = uses(props)
25
- const surface = useSurface({ className: roleClass(undefined, className),
26
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
27
- holds: [
28
- ...(kept.holds ?? []),
29
- ],
30
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
31
- return (
32
- <svg className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})} role="img" aria-label={surface.text(props.labelKey)}>
33
- <path {...track(props, kept)}></path>
34
- <path {...needle(props, kept)}></path>
35
- </svg>
36
- )
37
- },
38
- 'e-gauge',
39
- {"statTile":"thread-stat-tile"},
40
- )
@@ -1,42 +0,0 @@
1
- import { useRamp } from '../charts.tsx'
2
- import type { GaugeProps } from './gauge.tsx'
3
-
4
- const LEFT = 10
5
- const RIGHT = 90
6
- const MIDDLE = 50
7
- const RADIUS = 40
8
-
9
- export function uses(_props: GaugeProps): {
10
- readonly holds?: readonly string[]
11
- readonly animates?: boolean
12
- readonly on?: Readonly<Record<string, unknown>>
13
- readonly ramp: readonly string[]
14
- } {
15
- return { on: { viewBox: `0 0 100 56` }, ramp: useRamp(1) }
16
- }
17
-
18
- export function track(_props: GaugeProps, _kept: unknown): Readonly<Record<string, string | number>> {
19
- return {
20
- d: `M ${String(LEFT)} ${String(MIDDLE)} A ${String(RADIUS)} ${String(RADIUS)} 0 0 1 ${String(RIGHT)} ${String(MIDDLE)}`,
21
- fill: 'none',
22
- stroke: 'currentColor',
23
- strokeOpacity: 0.15,
24
- strokeWidth: 8,
25
- }
26
- }
27
-
28
- export function needle(props: GaugeProps, kept: { readonly ramp: readonly string[] }): Readonly<Record<string, string | number>> {
29
- const least = props.min ?? 0
30
- const most = props.max ?? 0
31
- const span = most - least
32
- const part = span <= 0 ? 0 : Math.min(1, Math.max(0, ((props.value ?? 0) - least) / span))
33
- const angle = Math.PI * (1 - part)
34
- return {
35
- d:
36
- `M ${String(LEFT)} ${String(MIDDLE)} A ${String(RADIUS)} ${String(RADIUS)} 0 0 1 ` +
37
- `${(MIDDLE + RADIUS * Math.cos(angle)).toFixed(2)} ${(MIDDLE - RADIUS * Math.sin(angle)).toFixed(2)}`,
38
- fill: 'none',
39
- stroke: kept.ramp[0] ?? 'currentColor',
40
- strokeWidth: 8,
41
- }
42
- }
@@ -1,30 +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
- import { shows } from './glyph.works.tsx'
9
-
10
-
11
- export interface GlyphProps {
12
- readonly name: string
13
- readonly labelKey?: MessageKey
14
- readonly dispatch?: keyof Dispatching
15
- readonly input?: Readonly<Record<string, unknown>>
16
- readonly invokeOn?: 'press' | 'shown'
17
- }
18
-
19
- export const Glyph: Element<'Glyph', GlyphProps> = element(
20
- 'Glyph',
21
- (props: GlyphProps, className) => {
22
- const surface = useSurface({ className: roleClass(undefined, className),
23
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) }, })
24
- return (
25
- shows(props, surface)
26
- )
27
- },
28
- 'e-glyph',
29
- {"glyphPlain":"thread-glyph-plain"},
30
- )
@@ -1,42 +0,0 @@
1
- import type { ReactNode } from 'react'
2
-
3
- import type { Surface } from '../door.ts'
4
- import { drawnFor, iconSize, installedSet, strokeFor } from '../icons.ts'
5
- import type { GlyphProps } from './glyph.tsx'
6
-
7
- type Drawn = (props: {
8
- readonly size?: number
9
- readonly strokeWidth?: number
10
- readonly className?: string
11
- readonly 'aria-hidden'?: boolean
12
- readonly 'aria-label'?: string
13
- readonly role?: string
14
- }) => ReactNode
15
-
16
- export function drawn(name: string, semantics: Readonly<Record<string, unknown>>, className?: string): ReactNode {
17
- const Symbol = drawnFor(name) as Drawn
18
- const set = installedSet()
19
- const stroke = set === undefined ? undefined : strokeFor(set)
20
-
21
- return (
22
- <Symbol
23
- size={iconSize()}
24
- {...(className === undefined ? {} : { className })}
25
- {...(stroke === undefined ? {} : { strokeWidth: stroke })}
26
- {...semantics}
27
- />
28
- )
29
- }
30
-
31
- export function shows(props: GlyphProps, surface: Surface): ReactNode {
32
- return drawn(
33
- props.name,
34
- {
35
- ...surface.marks,
36
- ...(props.labelKey === undefined
37
- ? ({ 'aria-hidden': true } as const)
38
- : ({ role: 'img', 'aria-label': surface.text(props.labelKey) } as const)),
39
- },
40
- surface.className,
41
- )
42
- }
@@ -1,29 +0,0 @@
1
- 'use client'
2
-
3
- import type { ReactNode } from 'react'
4
-
5
- import type { Dispatching } from '../checked.ts'
6
- import { type Element, element, useSurface } from '../wrappers.tsx'
7
-
8
-
9
- export interface GridProps {
10
- readonly children: ReactNode
11
- readonly dispatch?: keyof Dispatching
12
- readonly input?: Readonly<Record<string, unknown>>
13
- readonly invokeOn?: 'press' | 'shown'
14
- }
15
-
16
- export const Grid: Element<'Grid', GridProps> = element(
17
- 'Grid',
18
- (props: GridProps, className) => {
19
- const surface = useSurface({ className,
20
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) }, })
21
- return (
22
- <div className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y}>
23
- {props.children}
24
- </div>
25
- )
26
- },
27
- 'e-grid',
28
- {},
29
- )
@@ -1,48 +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
- import { measured as marking } from '../charts.tsx'
7
-
8
- import type { Dispatching } from '../checked.ts'
9
- import { type Element, element, useSurface } from '../wrappers.tsx'
10
- import { Each } from '../elements/each.tsx'
11
- import { sized, uses } from './heatmap.works.ts'
12
-
13
-
14
- export interface HeatmapProps {
15
- readonly rows: readonly Row[]
16
- readonly by: string
17
- readonly x: string
18
- readonly y: string
19
- readonly value?: string
20
- readonly titleKey?: MessageKey
21
- readonly dispatch?: keyof Dispatching
22
- readonly input?: Readonly<Record<string, unknown>>
23
- readonly invokeOn?: 'press' | 'shown'
24
- }
25
-
26
- export const Heatmap: Element<'Heatmap', HeatmapProps> = element(
27
- 'Heatmap',
28
- (props: HeatmapProps, className) => {
29
- const kept = uses(props)
30
- const surface = useSurface({ className: roleClass(undefined, className),
31
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
32
- holds: [
33
- ...(kept.holds ?? []),
34
- ],
35
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
36
- return (
37
- <div className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})} role="img" {...(props.titleKey === undefined ? {} : { 'aria-label': surface.text(props.titleKey) })}>
38
- <Each rows={props.rows} by={props.by}>
39
- {(each0, at) => (
40
- <span style={marking(sized(props, kept, each0, at))}></span>
41
- )}
42
- </Each>
43
- </div>
44
- )
45
- },
46
- 'e-heatmap',
47
- {"card":"thread-card"},
48
- )
@@ -1,24 +0,0 @@
1
- import { num, useRamp, type Row } from '../charts.tsx'
2
- import type { HeatmapProps } from './heatmap.tsx'
3
-
4
- export function uses(props: HeatmapProps): {
5
- readonly holds?: readonly string[]
6
- readonly animates?: boolean
7
- readonly on?: Readonly<Record<string, unknown>>
8
- readonly ramp: readonly string[]
9
- } {
10
- const ramp = useRamp(1)
11
- return { ...(props.rows.length === 0 ? { holds: ['empty'] as const } : {}), ramp }
12
- }
13
-
14
- export function sized(
15
- props: HeatmapProps,
16
- kept: { readonly ramp: readonly string[] },
17
- row: Row,
18
- _at: number,
19
- ): Readonly<Record<string, string | number>> {
20
- const readings = props.rows.map((one: Row) => (props.value === undefined ? 1 : num(one, props.value)))
21
- const most = Math.max(...readings, 0) || 1
22
- const here = props.value === undefined ? 1 : num(row, props.value)
23
- return { series: kept.ramp[0] ?? '', share: Math.max(0.08, here / most) }
24
- }