@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.
- package/CHANGELOG.md +366 -0
- package/dist/ui/attach.js +6 -7
- package/dist/ui/compositions.d.ts +0 -2
- package/dist/ui/compositions.js +2 -112
- package/dist/ui/generators/routes.js +1 -0
- package/dist/ui/variant.d.ts +1 -3
- package/package.json +2 -2
- package/dist/ui/animations/anm_appear.ts +0 -13
- package/dist/ui/animations/anm_blur_in.ts +0 -19
- package/dist/ui/animations/anm_blur_out.ts +0 -18
- package/dist/ui/animations/anm_dismiss.ts +0 -18
- package/dist/ui/animations/anm_drop.ts +0 -19
- package/dist/ui/animations/anm_fall.ts +0 -18
- package/dist/ui/animations/anm_flash.ts +0 -18
- package/dist/ui/animations/anm_glow.ts +0 -16
- package/dist/ui/animations/anm_lift.ts +0 -16
- package/dist/ui/animations/anm_nudge.ts +0 -16
- package/dist/ui/animations/anm_pop_in.ts +0 -23
- package/dist/ui/animations/anm_pop_out.ts +0 -22
- package/dist/ui/animations/anm_press.ts +0 -12
- package/dist/ui/animations/anm_pulse.ts +0 -13
- package/dist/ui/animations/anm_rise.ts +0 -19
- package/dist/ui/animations/anm_shake.ts +0 -13
- package/dist/ui/animations/anm_slide_in_left.ts +0 -19
- package/dist/ui/animations/anm_slide_in_right.ts +0 -19
- package/dist/ui/animations/anm_slide_out_left.ts +0 -18
- package/dist/ui/animations/anm_slide_out_right.ts +0 -18
- package/dist/ui/animations/anm_spin.ts +0 -12
- package/dist/ui/animations/anm_swell.ts +0 -16
- package/dist/ui/animations/anm_wobble.ts +0 -24
- package/dist/ui/animations/anm_zoom_in.ts +0 -19
- package/dist/ui/animations/anm_zoom_out.ts +0 -18
- package/dist/ui/animations.ts +0 -308
- package/dist/ui/attach.ts +0 -553
- package/dist/ui/charts.tsx +0 -69
- package/dist/ui/checked.ts +0 -32
- package/dist/ui/collector.ts +0 -140
- package/dist/ui/compositions.ts +0 -555
- package/dist/ui/consent.ts +0 -55
- package/dist/ui/content.tsx +0 -27
- package/dist/ui/contrast.ts +0 -27
- package/dist/ui/controls.tsx +0 -25
- package/dist/ui/disclosure.tsx +0 -20
- package/dist/ui/dismiss-layer.ts +0 -87
- package/dist/ui/door.ts +0 -140
- package/dist/ui/drag.ts +0 -316
- package/dist/ui/elements/accordion.tsx +0 -47
- package/dist/ui/elements/accordion.works.ts +0 -66
- package/dist/ui/elements/aspect-ratio.tsx +0 -31
- package/dist/ui/elements/aspect-ratio.works.ts +0 -5
- package/dist/ui/elements/avatar.tsx +0 -34
- package/dist/ui/elements/avatar.works.ts +0 -16
- package/dist/ui/elements/badge.tsx +0 -33
- package/dist/ui/elements/badge.works.ts +0 -9
- package/dist/ui/elements/bars.tsx +0 -50
- package/dist/ui/elements/bars.works.ts +0 -29
- package/dist/ui/elements/box.tsx +0 -36
- package/dist/ui/elements/breadcrumb.tsx +0 -38
- package/dist/ui/elements/breadcrumb.works.ts +0 -24
- package/dist/ui/elements/button.tsx +0 -46
- package/dist/ui/elements/button.works.tsx +0 -52
- package/dist/ui/elements/column.tsx +0 -29
- package/dist/ui/elements/consent.tsx +0 -71
- package/dist/ui/elements/consent.works.tsx +0 -132
- package/dist/ui/elements/donut.tsx +0 -45
- package/dist/ui/elements/donut.works.ts +0 -42
- package/dist/ui/elements/each.tsx +0 -34
- package/dist/ui/elements/each.works.tsx +0 -82
- package/dist/ui/elements/empty.tsx +0 -33
- package/dist/ui/elements/field.tsx +0 -45
- package/dist/ui/elements/field.works.ts +0 -65
- package/dist/ui/elements/funnel.tsx +0 -50
- package/dist/ui/elements/funnel.works.ts +0 -37
- package/dist/ui/elements/gauge.tsx +0 -40
- package/dist/ui/elements/gauge.works.ts +0 -42
- package/dist/ui/elements/glyph.tsx +0 -30
- package/dist/ui/elements/glyph.works.tsx +0 -42
- package/dist/ui/elements/grid.tsx +0 -29
- package/dist/ui/elements/heatmap.tsx +0 -48
- package/dist/ui/elements/heatmap.works.ts +0 -24
- package/dist/ui/elements/hit-box.tsx +0 -47
- package/dist/ui/elements/hit-box.works.tsx +0 -576
- package/dist/ui/elements/image.tsx +0 -29
- package/dist/ui/elements/line-chart.tsx +0 -45
- package/dist/ui/elements/line-chart.works.ts +0 -41
- package/dist/ui/elements/link.tsx +0 -38
- package/dist/ui/elements/link.works.ts +0 -25
- package/dist/ui/elements/media.tsx +0 -57
- package/dist/ui/elements/menu.tsx +0 -51
- package/dist/ui/elements/menu.works.tsx +0 -154
- package/dist/ui/elements/money.tsx +0 -36
- package/dist/ui/elements/money.works.ts +0 -9
- package/dist/ui/elements/pagination.tsx +0 -39
- package/dist/ui/elements/pagination.works.ts +0 -20
- package/dist/ui/elements/progress.tsx +0 -34
- package/dist/ui/elements/progress.works.ts +0 -8
- package/dist/ui/elements/radio-group.tsx +0 -50
- package/dist/ui/elements/radio-group.works.ts +0 -33
- package/dist/ui/elements/row.tsx +0 -29
- package/dist/ui/elements/scatter.tsx +0 -42
- package/dist/ui/elements/scatter.works.ts +0 -38
- package/dist/ui/elements/select.tsx +0 -43
- package/dist/ui/elements/select.works.ts +0 -5
- package/dist/ui/elements/separator.tsx +0 -31
- package/dist/ui/elements/skeleton.tsx +0 -31
- package/dist/ui/elements/slider.tsx +0 -34
- package/dist/ui/elements/slider.works.ts +0 -5
- package/dist/ui/elements/sparkline.tsx +0 -37
- package/dist/ui/elements/sparkline.works.ts +0 -32
- package/dist/ui/elements/spinner.tsx +0 -33
- package/dist/ui/elements/stack.tsx +0 -29
- package/dist/ui/elements/stat.tsx +0 -36
- package/dist/ui/elements/stat.works.ts +0 -14
- package/dist/ui/elements/surface.tsx +0 -36
- package/dist/ui/elements/surface.works.ts +0 -9
- package/dist/ui/elements/table.tsx +0 -62
- package/dist/ui/elements/table.works.ts +0 -30
- package/dist/ui/elements/tabs.tsx +0 -47
- package/dist/ui/elements/tabs.works.ts +0 -84
- package/dist/ui/elements/text.tsx +0 -33
- package/dist/ui/elements/textarea.tsx +0 -38
- package/dist/ui/elements/textarea.works.ts +0 -5
- package/dist/ui/elements/time.tsx +0 -40
- package/dist/ui/elements/time.works.ts +0 -29
- package/dist/ui/elements/toggle.tsx +0 -34
- package/dist/ui/elements/toggle.works.ts +0 -5
- package/dist/ui/elements/tooltip.tsx +0 -40
- package/dist/ui/elements/tooltip.works.ts +0 -77
- package/dist/ui/elements/value.tsx +0 -38
- package/dist/ui/elements/value.works.ts +0 -9
- package/dist/ui/elements.ts +0 -127
- package/dist/ui/focus-scope.ts +0 -121
- package/dist/ui/fonts.ts +0 -355
- package/dist/ui/generators/dev.d.ts +0 -1
- package/dist/ui/generators/dev.js +0 -61
- package/dist/ui/generators/dev.ts +0 -74
- package/dist/ui/generators/elements.ts +0 -796
- package/dist/ui/generators/i18n.ts +0 -283
- package/dist/ui/generators/icons.ts +0 -207
- package/dist/ui/generators/react.ts +0 -267
- package/dist/ui/generators/routes.ts +0 -847
- package/dist/ui/generators/show.ts +0 -261
- package/dist/ui/generators/styles.ts +0 -677
- package/dist/ui/generators/variants.ts +0 -196
- package/dist/ui/i18n/catalogs/en.ts +0 -191
- package/dist/ui/i18n/format.ts +0 -221
- package/dist/ui/i18n/locales.ts +0 -33
- package/dist/ui/i18n/parse.ts +0 -315
- package/dist/ui/i18n/translator.ts +0 -31
- package/dist/ui/i18n/types.ts +0 -96
- package/dist/ui/icons.ts +0 -331
- package/dist/ui/islands/map.ts +0 -58
- package/dist/ui/localizations.ts +0 -75
- package/dist/ui/material.ts +0 -236
- package/dist/ui/materials/mat_bordered.ts +0 -13
- package/dist/ui/materials/mat_ceramic.ts +0 -13
- package/dist/ui/materials/mat_clay.ts +0 -13
- package/dist/ui/materials/mat_elevated.ts +0 -24
- package/dist/ui/materials/mat_flat.ts +0 -34
- package/dist/ui/materials/mat_glass.ts +0 -20
- package/dist/ui/materials/mat_lit.ts +0 -13
- package/dist/ui/materials/mat_moulded.ts +0 -11
- package/dist/ui/materials.ts +0 -197
- package/dist/ui/modes.ts +0 -44
- package/dist/ui/navigation.tsx +0 -29
- package/dist/ui/positioning.ts +0 -90
- package/dist/ui/presented.tsx +0 -119
- package/dist/ui/provider.tsx +0 -184
- package/dist/ui/render-loop.ts +0 -133
- package/dist/ui/roving-focus.ts +0 -161
- package/dist/ui/screens.ts +0 -141
- package/dist/ui/scroll-lock.ts +0 -28
- package/dist/ui/series.ts +0 -97
- package/dist/ui/signals.ts +0 -386
- package/dist/ui/styleguide.ts +0 -231
- package/dist/ui/styleguides.ts +0 -374
- package/dist/ui/themes.ts +0 -22
- package/dist/ui/token.ts +0 -78
- package/dist/ui/tokens/shadow.ts +0 -25
- package/dist/ui/tokens.ts +0 -234
- package/dist/ui/validators.ts +0 -232
- package/dist/ui/variant.ts +0 -352
- package/dist/ui/variants/var_avatar-round.ts +0 -14
- package/dist/ui/variants/var_bubble-me.ts +0 -14
- package/dist/ui/variants/var_bubble-them.ts +0 -13
- package/dist/ui/variants/var_button-border.ts +0 -76
- package/dist/ui/variants/var_button-cta.ts +0 -76
- package/dist/ui/variants/var_button-destructive-ghost.ts +0 -72
- package/dist/ui/variants/var_button-destructive.ts +0 -76
- package/dist/ui/variants/var_button-fill.ts +0 -76
- package/dist/ui/variants/var_button-ghost.ts +0 -72
- package/dist/ui/variants/var_button-glass.ts +0 -55
- package/dist/ui/variants/var_button-normal.ts +0 -76
- package/dist/ui/variants/var_button-quiet.ts +0 -74
- package/dist/ui/variants/var_card.ts +0 -17
- package/dist/ui/variants/var_chip.ts +0 -13
- package/dist/ui/variants/var_choice.ts +0 -15
- package/dist/ui/variants/var_framed.ts +0 -10
- package/dist/ui/variants/var_glyph-plain.ts +0 -11
- package/dist/ui/variants/var_input.ts +0 -43
- package/dist/ui/variants/var_link-inline.ts +0 -29
- package/dist/ui/variants/var_list-row.ts +0 -34
- package/dist/ui/variants/var_rule.ts +0 -10
- package/dist/ui/variants/var_skeleton-block.ts +0 -12
- package/dist/ui/variants/var_spinner-turning.ts +0 -12
- package/dist/ui/variants/var_stat-tile.ts +0 -13
- package/dist/ui/variants/var_tab-bar.ts +0 -15
- package/dist/ui/variants/var_tap-target.ts +0 -25
- package/dist/ui/variants.ts +0 -64
- 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
|
-
}
|
package/dist/ui/elements/row.tsx
DELETED
|
@@ -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
|
-
)
|