@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,41 +0,0 @@
1
- import { seriesColour } from '../series.ts'
2
- import { num, polyline, str, useRamp, type Row } from '../charts.tsx'
3
- import type { LineChartProps } from './line-chart.tsx'
4
-
5
- const WIDE = 300
6
- const TALL = 120
7
-
8
- export function lines(props: LineChartProps, _kept?: unknown): readonly Row[] {
9
- if (props.by === undefined) return [{ group: '' }]
10
- return [...new Set(props.rows.map((one: Row) => str(one, props.by as string)))].map((group) => ({ group }))
11
- }
12
-
13
- export function uses(props: LineChartProps): {
14
- readonly holds?: readonly string[]
15
- readonly animates?: boolean
16
- readonly on?: Readonly<Record<string, unknown>>
17
- readonly ramp: readonly string[]
18
- } {
19
- const ramp = useRamp(lines(props).length)
20
- return {
21
- ...(props.rows.length === 0 ? { holds: ['empty'] as const } : {}),
22
- on: { viewBox: `0 0 ${String(WIDE)} ${String(TALL)}` },
23
- ramp,
24
- }
25
- }
26
-
27
- export function drawing(
28
- props: LineChartProps,
29
- kept: { readonly ramp: readonly string[] },
30
- row: Row,
31
- at: number,
32
- ): Readonly<Record<string, string | number>> {
33
- const group = str(row, 'group')
34
- const mine = props.rows.filter((one: Row) => props.by === undefined || str(one, props.by) === group)
35
- return {
36
- points: polyline(mine.map((one: Row) => num(one, props.value)), WIDE, TALL),
37
- fill: 'none',
38
- stroke: seriesColour(kept.ramp, at),
39
- strokeWidth: 2,
40
- }
41
- }
@@ -1,38 +0,0 @@
1
- 'use client'
2
-
3
- import type { MessageKey } from '../content.tsx'
4
- import { roleClass, type FontRoleId } from '../fonts.ts'
5
-
6
- import type { Dispatching } from '../checked.ts'
7
- import { type Element, element, useSurface } from '../wrappers.tsx'
8
- import { uses } from './link.works.ts'
9
-
10
-
11
- export interface LinkProps {
12
- readonly navigateTo: string
13
- readonly labelKey: MessageKey
14
- readonly is?: FontRoleId
15
- readonly dispatch?: keyof Dispatching
16
- readonly input?: Readonly<Record<string, unknown>>
17
- readonly invokeOn?: 'press' | 'shown'
18
- }
19
-
20
- export const Link: Element<'Link', LinkProps> = element(
21
- 'Link',
22
- (props: LinkProps, className) => {
23
- const kept = uses(props)
24
- const surface = useSurface({ className: roleClass(props.is, className),
25
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
26
- holds: [
27
- ...(kept.holds ?? []),
28
- ],
29
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
30
- return (
31
- <a className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})}>
32
- {surface.text(props.labelKey)}
33
- </a>
34
- )
35
- },
36
- 'e-link',
37
- {"linkInline":"thread-link-inline"},
38
- )
@@ -1,25 +0,0 @@
1
- import type { MouseEvent } from 'react'
2
-
3
- import { usePress } from '../wrappers.tsx'
4
- import type { LinkProps } from './link.tsx'
5
-
6
- export function uses(props: LinkProps): {
7
- readonly holds?: readonly string[]
8
- readonly animates?: boolean
9
- readonly on: {
10
- readonly href: string
11
- readonly onClick: (event: MouseEvent<HTMLAnchorElement>) => void
12
- }
13
- } {
14
- const press = usePress({ navigateTo: props.navigateTo }, { element: 'Link', name: props.labelKey })
15
- return {
16
- on: {
17
- href: `#${props.navigateTo}`,
18
- onClick: (event: MouseEvent<HTMLAnchorElement>): void => {
19
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.button !== 0) return
20
- event.preventDefault()
21
- press?.()
22
- },
23
- },
24
- }
25
- }
@@ -1,57 +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 MediaProps {
11
- readonly as: 'image' | 'video' | 'audio' | 'file'
12
- readonly src: string
13
- readonly altKey: MessageKey
14
- readonly dispatch?: keyof Dispatching
15
- readonly input?: Readonly<Record<string, unknown>>
16
- readonly invokeOn?: 'press' | 'shown'
17
- }
18
-
19
- export const Media: Element<'Media', MediaProps> = element(
20
- 'Media',
21
- (props: MediaProps, 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
- (() => {
26
- switch (props.as) {
27
- case 'image':
28
- return (
29
- <img className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} src={props.src} alt={surface.text(props.altKey)} />
30
- )
31
- case 'video':
32
- return (
33
- <video className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} src={props.src} aria-label={surface.text(props.altKey)} controls preload="metadata">
34
- {null}
35
- </video>
36
- )
37
- case 'audio':
38
- return (
39
- <audio className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} src={props.src} aria-label={surface.text(props.altKey)} controls preload="metadata">
40
- {null}
41
- </audio>
42
- )
43
- case 'file':
44
- return (
45
- <a className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} href={props.src} download>
46
- {surface.text(props.altKey)}
47
- </a>
48
- )
49
- default:
50
- return null
51
- }
52
- })()
53
- )
54
- },
55
- 'e-media',
56
- {"card":"thread-card","framed":"thread-framed"},
57
- )
@@ -1,51 +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 { Presented, MenuRow } from './menu.works.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 { measured, opener, portal, listing, row, uses } from './menu.works.tsx'
12
-
13
-
14
- export interface MenuProps {
15
- readonly labelKey: MessageKey
16
- readonly items: readonly Row[]
17
- readonly side?: string
18
- readonly dispatch?: keyof Dispatching
19
- readonly input?: Readonly<Record<string, unknown>>
20
- readonly invokeOn?: 'press' | 'shown'
21
- }
22
-
23
- export const Menu: Element<'Menu', MenuProps> = element(
24
- 'Menu',
25
- (props: MenuProps, 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
- measures: measured(props, kept),
33
- ...(kept.animates === undefined ? {} : { animates: kept.animates }), })
34
- return (
35
- <span {...surface.marks} {...surface.invokes} {...surface.a11y} {...(kept.on ?? {})} style={surface.measures}>
36
- <button className={`e-menu-opener ${surface.className ?? ''}`} {...opener(props, kept)}>{surface.text(props.labelKey)}</button>
37
- <Presented {...portal(props, kept)}>
38
- <div className="e-menu-layer" {...listing(props, kept)}>
39
- <Each rows={props.items} by={props.labelKey}>
40
- {(each0, at) => (
41
- <MenuRow {...row(props, kept, each0, at)}></MenuRow>
42
- )}
43
- </Each>
44
- </div>
45
- </Presented>
46
- </span>
47
- )
48
- },
49
- 'e-menu',
50
- {"card":"thread-card"},
51
- )
@@ -1,154 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useId, useRef, useState, type ReactNode } from 'react'
4
-
5
- import type { Dispatching } from '../checked.ts'
6
- import type { MessageKey } from '../content.tsx'
7
- import { positionOf, type AnchorOptions, type Position } from '../positioning.ts'
8
- import { ITEM_ATTR, useRovingFocus } from '../roving-focus.ts'
9
- import { pressOf, usePress, useSurface } from '../wrappers.tsx'
10
- import { drawn } from './glyph.works.tsx'
11
- import type { MenuProps } from './menu.tsx'
12
-
13
- export interface MenuItem {
14
- readonly labelKey: MessageKey
15
- readonly dispatch?: keyof Dispatching
16
- readonly navigateTo?: string
17
- readonly icon?: string
18
- readonly disabled?: boolean
19
- }
20
-
21
- export { Presented } from '../presented.tsx'
22
- import type { PresentedProps } from '../presented.tsx'
23
-
24
- const SIDE = 'bottom'
25
-
26
- export function MenuRow({
27
- item,
28
- onDone,
29
- }: {
30
- readonly item: MenuItem
31
- readonly onDone: () => void
32
- }): ReactNode {
33
-
34
- const { text } = useSurface({})
35
- const press = usePress(pressOf(item), { element: 'Menu', name: item.labelKey })
36
- return (
37
- <button
38
- {...{ [ITEM_ATTR]: '' }}
39
- type="button"
40
- role="menuitem"
41
- disabled={item.disabled === true}
42
- onClick={() => {
43
- press?.()
44
- onDone()
45
- }}
46
- >
47
- {item.icon === undefined ? null : drawn(item.icon, { 'aria-hidden': true })}
48
- {text(item.labelKey)}
49
- </button>
50
- )
51
- }
52
-
53
- type Kept = {
54
- readonly trigger: React.RefObject<HTMLButtonElement | null>
55
- readonly layer: React.RefObject<HTMLDivElement | null>
56
- readonly open: boolean
57
- readonly at: Position | null
58
- readonly names: { readonly trigger: string; readonly menu: string }
59
- readonly show: (to: boolean | ((was: boolean) => boolean)) => void
60
- }
61
-
62
- export function uses(props: MenuProps): {
63
- readonly holds?: readonly string[]
64
- readonly animates?: boolean
65
- readonly on?: Readonly<Record<string, unknown>>
66
- readonly trigger: React.RefObject<HTMLButtonElement | null>
67
- readonly layer: React.RefObject<HTMLDivElement | null>
68
- readonly open: boolean
69
- readonly at: Position | null
70
- readonly names: { readonly trigger: string; readonly menu: string }
71
- readonly show: (to: boolean | ((was: boolean) => boolean)) => void
72
- } {
73
- const trigger = useRef<HTMLButtonElement>(null)
74
- const layer = useRef<HTMLDivElement>(null)
75
- const [open, setOpen] = useState(false)
76
- const [at, setAt] = useState<Position | null>(null)
77
- const base = useId()
78
-
79
- useRovingFocus(layer, { orientation: 'vertical', wrap: true, active: open })
80
-
81
- useEffect(() => {
82
- if (!open) return undefined
83
- const anchor = trigger.current
84
- const floating = layer.current
85
- if (anchor === null || floating === null) return undefined
86
- let live = true
87
- void positionOf(anchor, floating, {
88
- placement: (props.side ?? SIDE) as NonNullable<AnchorOptions['placement']>,
89
- }).then((position) => {
90
- if (live) setAt(position)
91
- })
92
- return () => {
93
- live = false
94
- }
95
- }, [open, props.side])
96
-
97
- return {
98
- trigger,
99
- layer,
100
- open,
101
- at,
102
- names: { trigger: `${base}-trigger`, menu: `${base}-menu` },
103
- show: setOpen,
104
- }
105
- }
106
-
107
- export function opener(_props: MenuProps, kept: Kept): Readonly<Record<string, unknown>> {
108
- return {
109
- ref: kept.trigger,
110
- type: 'button',
111
- id: kept.names.trigger,
112
- 'aria-haspopup': 'menu',
113
- 'aria-expanded': kept.open,
114
- ...(kept.open ? { 'aria-controls': kept.names.menu } : {}),
115
- onPointerDown: (event: React.PointerEvent<HTMLButtonElement>): void => {
116
- if (event.button > 0 || event.ctrlKey) return
117
- event.currentTarget.focus()
118
- event.preventDefault()
119
- kept.show((was) => !was)
120
- },
121
- onKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>): void => {
122
- if (event.key !== 'Enter' && event.key !== ' ' && event.key !== 'ArrowDown') return
123
- event.preventDefault()
124
- if (event.key === 'ArrowDown') kept.show(true)
125
- else kept.show((was) => !was)
126
- },
127
- }
128
- }
129
-
130
- export function portal(_props: MenuProps, kept: Kept): PresentedProps {
131
- return { presentation: 'popover', open: kept.open, onDismiss: () => kept.show(false), children: null }
132
- }
133
-
134
- export function listing(props: MenuProps, kept: Kept): Readonly<Record<string, unknown>> {
135
- return {
136
- ref: kept.layer,
137
- id: kept.names.menu,
138
- role: 'menu',
139
- 'aria-labelledby': kept.names.trigger,
140
- 'data-side': kept.at?.placement ?? props.side ?? SIDE,
141
- onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>): void => {
142
- if (event.key !== 'Tab') return
143
- kept.show(false)
144
- },
145
- }
146
- }
147
-
148
- export function row(_props: MenuProps, kept: Kept, item: Record<string, unknown>, _at: number): { readonly item: MenuItem; readonly onDone: () => void } {
149
- return { item: item as unknown as MenuItem, onDone: () => kept.show(false) }
150
- }
151
-
152
- export function measured(_props: MenuProps, kept: Kept): Readonly<Record<string, string | number>> {
153
- return kept.at === null ? {} : { x: `${String(kept.at.x)}px`, y: `${String(kept.at.y)}px` }
154
- }
@@ -1,36 +0,0 @@
1
- 'use client'
2
-
3
- import { roleClass, type FontRoleId } from '../fonts.ts'
4
-
5
- import type { Dispatching } from '../checked.ts'
6
- import { type Element, element, useSurface } from '../wrappers.tsx'
7
- import { shows } from './money.works.ts'
8
-
9
-
10
- export interface MoneyProps {
11
- readonly value?: unknown
12
- readonly is?: FontRoleId
13
- readonly invalid?: boolean
14
- readonly dispatch?: keyof Dispatching
15
- readonly input?: Readonly<Record<string, unknown>>
16
- readonly invokeOn?: 'press' | 'shown'
17
- }
18
-
19
- export const Money: Element<'Money', MoneyProps> = element(
20
- 'Money',
21
- (props: MoneyProps, className) => {
22
- const surface = useSurface({ className: roleClass(props.is, className),
23
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
24
- holds: [
25
- ...(props.invalid === true ? ['invalid' as const] : []),
26
- ],
27
- raises: ['invalid' as const], })
28
- return (
29
- <span className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y}>
30
- {shows(props, surface)}
31
- </span>
32
- )
33
- },
34
- 'e-money',
35
- {},
36
- )
@@ -1,9 +0,0 @@
1
- import type { ReactNode } from 'react'
2
-
3
- import { renderValue } from '../content.tsx'
4
- import type { Surface } from '../door.ts'
5
- import type { MoneyProps } from './money.tsx'
6
-
7
- export function shows(props: MoneyProps, surface: Surface): ReactNode {
8
- return renderValue({ validator: 'money', value: props.value }, surface)
9
- }
@@ -1,39 +0,0 @@
1
- 'use client'
2
-
3
- import { roleClass } from '../fonts.ts'
4
- import type { MapIsland } from '../islands/map.ts'
5
- import { Link } from './link.tsx'
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 { step, steps } from './pagination.works.ts'
11
-
12
-
13
- export interface PaginationProps {
14
- readonly map: MapIsland
15
- readonly sequence: readonly string[]
16
- readonly current: string
17
- readonly dispatch?: keyof Dispatching
18
- readonly input?: Readonly<Record<string, unknown>>
19
- readonly invokeOn?: 'press' | 'shown'
20
- }
21
-
22
- export const Pagination: Element<'Pagination', PaginationProps> = element(
23
- 'Pagination',
24
- (props: PaginationProps, className) => {
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
- return (
28
- <nav className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} aria-label={surface.text('nav.pagination')}>
29
- <Each rows={steps(props)} by={'id'}>
30
- {(each0, at) => (
31
- <Link.linkInline {...step(props, each0, at)}></Link.linkInline>
32
- )}
33
- </Each>
34
- </nav>
35
- )
36
- },
37
- 'e-pagination',
38
- {"tabBar":"thread-tab-bar"},
39
- )
@@ -1,20 +0,0 @@
1
- import type { Row } from '../charts.tsx'
2
- import type { LinkProps } from './link.tsx'
3
- import type { PaginationProps } from './pagination.tsx'
4
-
5
- export function steps(props: PaginationProps): readonly Row[] {
6
- const known = new Map(props.map.screens.map((one) => [one.id, one]))
7
- const walk = props.sequence.filter((id) => known.has(id))
8
- const at = walk.indexOf(props.current)
9
- if (at === -1) return []
10
-
11
- const either = [
12
- ...(at > 0 ? [{ id: walk[at - 1] as string, way: 'previous' }] : []),
13
- ...(at < walk.length - 1 ? [{ id: walk[at + 1] as string, way: 'next' }] : []),
14
- ]
15
- return either.map((one) => ({ ...one, nameKey: known.get(one.id)?.nameKey ?? `nav.${one.way}` }))
16
- }
17
-
18
- export function step(_props: PaginationProps, row: Row, _at: number): LinkProps {
19
- return { navigateTo: String(row['id']), labelKey: String(row['nameKey']), is: 'label' }
20
- }
@@ -1,34 +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 { measured } from './progress.works.ts'
9
-
10
-
11
- export interface ProgressProps {
12
- readonly value?: number
13
- readonly target?: number
14
- readonly labelKey: MessageKey
15
- readonly dispatch?: keyof Dispatching
16
- readonly input?: Readonly<Record<string, unknown>>
17
- readonly invokeOn?: 'press' | 'shown'
18
- }
19
-
20
- export const Progress: Element<'Progress', ProgressProps> = element(
21
- 'Progress',
22
- (props: ProgressProps, className) => {
23
- const surface = useSurface({ className: roleClass(undefined, className),
24
- invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },
25
- measures: measured(props), })
26
- return (
27
- <div className={surface.className} {...surface.marks} {...surface.invokes} {...surface.a11y} role="progressbar" aria-valuenow={Number(measured(props).fraction)} aria-valuemin={0} aria-valuemax={100} style={surface.measures} aria-label={surface.text(props.labelKey)}>
28
- <span className="e-progress-bar"></span>
29
- </div>
30
- )
31
- },
32
- 'e-progress',
33
- {"statTile":"thread-stat-tile"},
34
- )
@@ -1,8 +0,0 @@
1
- import type { ProgressProps } from './progress.tsx'
2
-
3
- export function measured(props: ProgressProps): Readonly<Record<string, string | number>> {
4
- const target = props.target ?? 0
5
- const part = target <= 0 ? 0 : Math.min(1, Math.max(0, (props.value ?? 0) / target))
6
- const whole = Math.round(part * 100)
7
- return { fraction: whole, span: `${String(whole)}%` }
8
- }
@@ -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
-
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 { shown, option, uses } from './radio-group.works.ts'
11
-
12
-
13
- export interface RadioGroupProps {
14
- readonly name: string
15
- readonly labelKey: MessageKey
16
- readonly options: readonly Row[]
17
- readonly chosen?: string
18
- readonly onValue?: (said: string) => void
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 RadioGroup: Element<'RadioGroup', RadioGroupProps> = element(
26
- 'RadioGroup',
27
- (props: RadioGroupProps, 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="radiogroup" aria-label={surface.text(props.labelKey)}>
37
- <Each rows={props.options} by={'value'}>
38
- {(each0, at) => (
39
- <label>
40
- <input {...option(props, kept, each0, at)} type="radio" disabled={surface.marked('disabled')} />
41
- <span>{shown(props, each0, surface, kept).caption}</span>
42
- </label>
43
- )}
44
- </Each>
45
- </div>
46
- )
47
- },
48
- 'e-radio-group',
49
- {"choice":"thread-choice"},
50
- )
@@ -1,33 +0,0 @@
1
- import { useRef } from 'react'
2
-
3
- import { ITEM_ATTR, useRovingFocus } from '../roving-focus.ts'
4
- import type { Row } from '../charts.tsx'
5
- import type { Surface } from '../door.ts'
6
- import type { RadioGroupProps } from './radio-group.tsx'
7
-
8
- export function uses(props: RadioGroupProps): {
9
- readonly holds?: readonly string[]
10
- readonly animates?: boolean
11
- readonly on: Readonly<Record<string, unknown>>
12
- } {
13
- const group = useRef<HTMLDivElement>(null)
14
- useRovingFocus(group, { orientation: 'vertical' })
15
- return {
16
- ...(props.disabled === true ? { holds: ['disabled'] as const } : {}),
17
- on: { ref: group },
18
- }
19
- }
20
-
21
- export function option(props: RadioGroupProps, _kept: unknown, row: Row, _at: number): Readonly<Record<string, unknown>> {
22
- return {
23
- ...{ [ITEM_ATTR]: '' },
24
- name: props.name,
25
- value: String(row['value']),
26
- checked: props.chosen === row['value'],
27
- onChange: (): void => props.onValue?.(String(row['value'])),
28
- }
29
- }
30
-
31
- export function shown(_props: RadioGroupProps, row: Row, surface: Surface, _kept?: unknown): Readonly<Record<string, string>> {
32
- return { caption: surface.text(String(row['labelKey'])) }
33
- }
@@ -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 RowProps {
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 Row: Element<'Row', RowProps> = element(
17
- 'Row',
18
- (props: RowProps, 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-row',
28
- {},
29
- )