@refraction-ui/astro 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/astro-audio-room/AudioRoom.astro +81 -0
- package/dist/astro-audio-room/index.ts +1 -0
- package/dist/astro-call-controls/CallControls.astro +57 -0
- package/dist/astro-call-controls/index.ts +1 -0
- package/dist/astro-checklist/Checklist.astro +76 -0
- package/dist/astro-checklist/index.ts +1 -0
- package/dist/astro-editor-status-bar/EditorStatusBar.astro +92 -0
- package/dist/astro-editor-status-bar/index.ts +1 -0
- package/dist/astro-editor-tabs/EditorTabs.astro +74 -0
- package/dist/astro-editor-tabs/index.ts +1 -0
- package/dist/astro-floating-reactions/FloatingReactions.astro +56 -0
- package/dist/astro-floating-reactions/index.ts +1 -0
- package/dist/astro-flow-editor/FlowEditor.astro +99 -0
- package/dist/astro-flow-editor/index.ts +3 -0
- package/dist/astro-graph-view/GraphView.astro +109 -0
- package/dist/astro-graph-view/index.ts +1 -0
- package/dist/astro-infinite-canvas/InfiniteCanvas.astro +43 -0
- package/dist/astro-infinite-canvas/index.ts +1 -0
- package/dist/astro-live-captions/LiveCaptions.astro +54 -0
- package/dist/astro-live-captions/index.ts +1 -0
- package/dist/astro-live-cursors/LiveCursors.astro +55 -0
- package/dist/astro-live-cursors/index.ts +1 -0
- package/dist/astro-live-transcript/LiveTranscript.astro +55 -0
- package/dist/astro-live-transcript/index.ts +1 -0
- package/dist/astro-mini-map/MiniMap.astro +74 -0
- package/dist/astro-mini-map/index.ts +1 -0
- package/dist/astro-pre-call-lobby/PreCallLobby.astro +202 -0
- package/dist/astro-pre-call-lobby/index.ts +1 -0
- package/dist/astro-radial-gauge/RadialGauge.astro +144 -0
- package/dist/astro-radial-gauge/index.ts +1 -0
- package/dist/astro-rating-scale/RatingScale.astro +73 -0
- package/dist/astro-rating-scale/index.ts +1 -0
- package/dist/astro-sticky-note/StickyNote.astro +55 -0
- package/dist/astro-sticky-note/index.ts +1 -0
- package/dist/astro-terminal/Terminal.astro +67 -0
- package/dist/astro-terminal/index.ts +1 -0
- package/dist/astro-test-results/TestResults.astro +76 -0
- package/dist/astro-test-results/index.ts +1 -0
- package/dist/astro-timeline/Timeline.astro +59 -0
- package/dist/astro-timeline/index.ts +1 -0
- package/dist/astro-video-grid/VideoGrid.astro +182 -0
- package/dist/astro-video-grid/index.ts +1 -0
- package/dist/astro-video-tile/VideoTile.astro +104 -0
- package/dist/astro-video-tile/index.ts +1 -0
- package/dist/astro-wizard/Wizard.astro +137 -0
- package/dist/astro-wizard/index.ts +1 -0
- package/dist/audio-room/audio-room.styles.ts +88 -0
- package/dist/audio-room/audio-room.ts +70 -0
- package/dist/audio-room/index.ts +16 -0
- package/dist/call-controls/call-controls.styles.ts +63 -0
- package/dist/call-controls/call-controls.ts +79 -0
- package/dist/call-controls/index.ts +15 -0
- package/dist/checklist/checklist.styles.ts +76 -0
- package/dist/checklist/checklist.ts +62 -0
- package/dist/checklist/index.ts +18 -0
- package/dist/editor-status-bar/editor-status-bar.styles.ts +40 -0
- package/dist/editor-status-bar/editor-status-bar.ts +78 -0
- package/dist/editor-status-bar/index.ts +15 -0
- package/dist/editor-tabs/editor-tabs.styles.ts +52 -0
- package/dist/editor-tabs/editor-tabs.ts +82 -0
- package/dist/editor-tabs/index.ts +15 -0
- package/dist/floating-reactions/floating-reactions.styles.ts +54 -0
- package/dist/floating-reactions/floating-reactions.ts +85 -0
- package/dist/floating-reactions/index.ts +14 -0
- package/dist/flow-editor/flow-editor.styles.ts +56 -0
- package/dist/flow-editor/flow-editor.ts +126 -0
- package/dist/flow-editor/index.ts +18 -0
- package/dist/graph-view/graph-view.styles.ts +66 -0
- package/dist/graph-view/graph-view.ts +130 -0
- package/dist/graph-view/index.ts +21 -0
- package/dist/index.ts +23 -0
- package/dist/infinite-canvas/index.ts +17 -0
- package/dist/infinite-canvas/infinite-canvas.styles.ts +49 -0
- package/dist/infinite-canvas/infinite-canvas.ts +97 -0
- package/dist/live-captions/index.ts +13 -0
- package/dist/live-captions/live-captions.styles.ts +45 -0
- package/dist/live-captions/live-captions.ts +57 -0
- package/dist/live-cursors/index.ts +14 -0
- package/dist/live-cursors/live-cursors.styles.ts +41 -0
- package/dist/live-cursors/live-cursors.ts +73 -0
- package/dist/live-transcript/index.ts +16 -0
- package/dist/live-transcript/live-transcript.styles.ts +48 -0
- package/dist/live-transcript/live-transcript.ts +82 -0
- package/dist/mini-map/index.ts +16 -0
- package/dist/mini-map/mini-map.styles.ts +32 -0
- package/dist/mini-map/mini-map.ts +122 -0
- package/dist/pre-call-lobby/index.ts +22 -0
- package/dist/pre-call-lobby/pre-call-lobby.styles.ts +108 -0
- package/dist/pre-call-lobby/pre-call-lobby.ts +61 -0
- package/dist/radial-gauge/index.ts +26 -0
- package/dist/radial-gauge/radial-gauge.styles.ts +75 -0
- package/dist/radial-gauge/radial-gauge.ts +183 -0
- package/dist/rating-scale/index.ts +16 -0
- package/dist/rating-scale/rating-scale.styles.ts +48 -0
- package/dist/rating-scale/rating-scale.ts +101 -0
- package/dist/sticky-note/index.ts +14 -0
- package/dist/sticky-note/sticky-note.styles.ts +37 -0
- package/dist/sticky-note/sticky-note.ts +47 -0
- package/dist/terminal/index.ts +13 -0
- package/dist/terminal/terminal.styles.ts +56 -0
- package/dist/terminal/terminal.ts +60 -0
- package/dist/test-results/index.ts +19 -0
- package/dist/test-results/test-results.styles.ts +93 -0
- package/dist/test-results/test-results.ts +83 -0
- package/dist/timeline/index.ts +22 -0
- package/dist/timeline/timeline.styles.ts +98 -0
- package/dist/timeline/timeline.ts +86 -0
- package/dist/video-grid/index.ts +16 -0
- package/dist/video-grid/video-grid.styles.ts +38 -0
- package/dist/video-grid/video-grid.ts +93 -0
- package/dist/video-tile/index.ts +17 -0
- package/dist/video-tile/video-tile.styles.ts +59 -0
- package/dist/video-tile/video-tile.ts +74 -0
- package/dist/wizard/index.ts +28 -0
- package/dist/wizard/wizard.styles.ts +147 -0
- package/dist/wizard/wizard.ts +128 -0
- package/package.json +24 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** The execution status of a single test case. */
|
|
4
|
+
export type TestStatus = 'pass' | 'fail' | 'skip'
|
|
5
|
+
|
|
6
|
+
/** Data for a single test case result. */
|
|
7
|
+
export interface TestResultData {
|
|
8
|
+
/** Unique identifier for the test. */
|
|
9
|
+
id: string
|
|
10
|
+
/** Human-readable test name. */
|
|
11
|
+
name: string
|
|
12
|
+
/** Execution status. */
|
|
13
|
+
status: TestStatus
|
|
14
|
+
/** How long the test took to run, in milliseconds. */
|
|
15
|
+
durationMs?: number
|
|
16
|
+
/** Expected value (shown in diff when the test fails). */
|
|
17
|
+
expected?: string
|
|
18
|
+
/** Actual value received (shown in diff when the test fails). */
|
|
19
|
+
actual?: string
|
|
20
|
+
/** Optional message — error message on failure, skip reason, etc. */
|
|
21
|
+
message?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Aggregate counts derived from a list of test results. */
|
|
25
|
+
export interface TestSummary {
|
|
26
|
+
total: number
|
|
27
|
+
passed: number
|
|
28
|
+
failed: number
|
|
29
|
+
skipped: number
|
|
30
|
+
/** True when every test passed (failed === 0 and skipped === 0). */
|
|
31
|
+
allPassed: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TestResultsAPI {
|
|
35
|
+
/** ARIA attributes to spread on the list container (`role="list"`). */
|
|
36
|
+
ariaProps: Partial<AccessibilityProps>
|
|
37
|
+
/** Data attributes for styling hooks. */
|
|
38
|
+
dataAttributes: Record<string, string>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Compute aggregate summary counts from a list of {@link TestResultData}.
|
|
43
|
+
*
|
|
44
|
+
* Pure function — no side effects, safe to call during SSR.
|
|
45
|
+
*/
|
|
46
|
+
export function summarizeTests(results: TestResultData[]): TestSummary {
|
|
47
|
+
let passed = 0
|
|
48
|
+
let failed = 0
|
|
49
|
+
let skipped = 0
|
|
50
|
+
|
|
51
|
+
for (const r of results) {
|
|
52
|
+
if (r.status === 'pass') passed++
|
|
53
|
+
else if (r.status === 'fail') failed++
|
|
54
|
+
else skipped++
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
total: results.length,
|
|
59
|
+
passed,
|
|
60
|
+
failed,
|
|
61
|
+
skipped,
|
|
62
|
+
allPassed: failed === 0 && skipped === 0 && results.length > 0,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build the framework-agnostic container props for a test results list.
|
|
68
|
+
*
|
|
69
|
+
* Returns `role="list"` plus data attributes that adapters spread onto their
|
|
70
|
+
* container element. The adapter is responsible for rendering individual items
|
|
71
|
+
* with `role="listitem"`.
|
|
72
|
+
*/
|
|
73
|
+
export function createTestResults(): TestResultsAPI {
|
|
74
|
+
const ariaProps: Partial<AccessibilityProps> = {
|
|
75
|
+
role: 'list',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const dataAttributes: Record<string, string> = {
|
|
79
|
+
'data-component': 'test-results',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { ariaProps, dataAttributes }
|
|
83
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createTimeline,
|
|
3
|
+
getTimelineItemAria,
|
|
4
|
+
resolveTimelineItems,
|
|
5
|
+
type TimelineOrientation,
|
|
6
|
+
type TimelineItemStatus,
|
|
7
|
+
type TimelineItemData,
|
|
8
|
+
type TimelineProps,
|
|
9
|
+
type TimelineAPI,
|
|
10
|
+
} from './timeline.js'
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
timelineVariants,
|
|
14
|
+
timelineItemVariants,
|
|
15
|
+
timelineMarkerVariants,
|
|
16
|
+
timelineConnectorVariants,
|
|
17
|
+
timelineGutterClass,
|
|
18
|
+
timelineTimeClass,
|
|
19
|
+
timelineTitleClass,
|
|
20
|
+
timelineDescriptionClass,
|
|
21
|
+
timelineContentClass,
|
|
22
|
+
} from './timeline.styles.js'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Outer rail / list container.
|
|
5
|
+
*
|
|
6
|
+
* Vertical: a single column; horizontal: a row so items sit side-by-side.
|
|
7
|
+
*/
|
|
8
|
+
export const timelineVariants = cva({
|
|
9
|
+
base: 'relative',
|
|
10
|
+
variants: {
|
|
11
|
+
orientation: {
|
|
12
|
+
vertical: 'flex flex-col',
|
|
13
|
+
horizontal: 'flex flex-row items-start overflow-x-auto',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
orientation: 'vertical',
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A single event row / column.
|
|
23
|
+
*
|
|
24
|
+
* In vertical mode items stack with a left gutter for the marker + connector.
|
|
25
|
+
* In horizontal mode each item takes equal width and the connector runs above.
|
|
26
|
+
*/
|
|
27
|
+
export const timelineItemVariants = cva({
|
|
28
|
+
base: 'relative flex gap-4',
|
|
29
|
+
variants: {
|
|
30
|
+
orientation: {
|
|
31
|
+
vertical: 'flex-row pb-8 last:pb-0',
|
|
32
|
+
horizontal: 'flex-col items-center flex-1 px-2',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
orientation: 'vertical',
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The marker / dot that represents the event on the rail.
|
|
42
|
+
*
|
|
43
|
+
* Status variants map directly to semantic token colours so theming is
|
|
44
|
+
* handled at the design-token level, not inline.
|
|
45
|
+
*/
|
|
46
|
+
export const timelineMarkerVariants = cva({
|
|
47
|
+
base: [
|
|
48
|
+
'relative z-10 flex shrink-0 items-center justify-center',
|
|
49
|
+
'size-3 rounded-full border-2',
|
|
50
|
+
'transition-colors',
|
|
51
|
+
].join(' '),
|
|
52
|
+
variants: {
|
|
53
|
+
status: {
|
|
54
|
+
done: 'bg-primary border-primary',
|
|
55
|
+
current: 'bg-background border-primary ring-2 ring-primary ring-offset-2 ring-offset-background',
|
|
56
|
+
upcoming: 'bg-muted border-muted-foreground/30',
|
|
57
|
+
default: 'bg-background border-border',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
defaultVariants: {
|
|
61
|
+
status: 'default',
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The connector line drawn between adjacent markers.
|
|
67
|
+
*
|
|
68
|
+
* Vertical: a thin vertical stripe anchored to the left gutter.
|
|
69
|
+
* Horizontal: a thin horizontal stripe anchored above the content row.
|
|
70
|
+
* The last item hides its connector so the rail terminates cleanly.
|
|
71
|
+
*/
|
|
72
|
+
export const timelineConnectorVariants = cva({
|
|
73
|
+
base: 'absolute bg-border',
|
|
74
|
+
variants: {
|
|
75
|
+
orientation: {
|
|
76
|
+
vertical: 'left-[5px] top-3 w-0.5 h-full',
|
|
77
|
+
horizontal: 'top-[5px] left-1/2 w-full h-0.5',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
defaultVariants: {
|
|
81
|
+
orientation: 'vertical',
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
/** Wrapper that holds the marker + connector in the gutter column/row. */
|
|
86
|
+
export const timelineGutterClass = 'relative flex flex-col items-center'
|
|
87
|
+
|
|
88
|
+
/** Time / date label rendered near the marker. */
|
|
89
|
+
export const timelineTimeClass = 'text-xs text-muted-foreground tabular-nums'
|
|
90
|
+
|
|
91
|
+
/** Primary event title. */
|
|
92
|
+
export const timelineTitleClass = 'text-sm font-medium text-foreground leading-snug'
|
|
93
|
+
|
|
94
|
+
/** Secondary description text. */
|
|
95
|
+
export const timelineDescriptionClass = 'mt-0.5 text-xs text-muted-foreground leading-relaxed'
|
|
96
|
+
|
|
97
|
+
/** Content block (title + description) next to or below the marker. */
|
|
98
|
+
export const timelineContentClass = 'flex flex-col min-w-0'
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** Layout orientation of the timeline. */
|
|
4
|
+
export type TimelineOrientation = 'vertical' | 'horizontal'
|
|
5
|
+
|
|
6
|
+
/** Visual status of a single timeline item. */
|
|
7
|
+
export type TimelineItemStatus = 'done' | 'current' | 'upcoming' | 'default'
|
|
8
|
+
|
|
9
|
+
/** Data describing a single event in the timeline. */
|
|
10
|
+
export interface TimelineItemData {
|
|
11
|
+
/** Unique identifier for the item. */
|
|
12
|
+
id: string
|
|
13
|
+
/** Primary label shown beside the marker. */
|
|
14
|
+
title: string
|
|
15
|
+
/** Optional timestamp / date string shown near the marker. */
|
|
16
|
+
time?: string
|
|
17
|
+
/** Additional detail text rendered below the title. */
|
|
18
|
+
description?: string
|
|
19
|
+
/** Visual status of this item. Defaults to `'default'` when omitted. */
|
|
20
|
+
status?: TimelineItemStatus
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TimelineProps {
|
|
24
|
+
/** Ordered list of events to display. */
|
|
25
|
+
items: TimelineItemData[]
|
|
26
|
+
/** Layout direction. */
|
|
27
|
+
orientation?: TimelineOrientation
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TimelineAPI {
|
|
31
|
+
/** ARIA attributes to spread on the list container (`role="list"`). */
|
|
32
|
+
ariaProps: Partial<AccessibilityProps>
|
|
33
|
+
/** Data attributes for styling hooks. */
|
|
34
|
+
dataAttributes: Record<string, string>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build the framework-agnostic container props for a timeline.
|
|
39
|
+
*
|
|
40
|
+
* Returns `role="list"` (the items carry `role="listitem"`) plus a
|
|
41
|
+
* `data-orientation` attribute. The function is JSX-free so it can run in
|
|
42
|
+
* any environment including server-side Node and Astro SSR.
|
|
43
|
+
*/
|
|
44
|
+
export function createTimeline(
|
|
45
|
+
props: Pick<TimelineProps, 'orientation'> = {},
|
|
46
|
+
): TimelineAPI {
|
|
47
|
+
const { orientation = 'vertical' } = props
|
|
48
|
+
|
|
49
|
+
const ariaProps: Partial<AccessibilityProps> = {
|
|
50
|
+
role: 'list',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const dataAttributes: Record<string, string> = {
|
|
54
|
+
'data-orientation': orientation,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { ariaProps, dataAttributes }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Accessibility props for a single timeline item.
|
|
62
|
+
*
|
|
63
|
+
* Every item gets `role="listitem"` regardless of status so screen readers
|
|
64
|
+
* announce the full list correctly.
|
|
65
|
+
*/
|
|
66
|
+
export function getTimelineItemAria(): Partial<AccessibilityProps> {
|
|
67
|
+
return { role: 'listitem' }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Normalise a raw list of timeline items so every entry has an explicit
|
|
72
|
+
* `status`. Items without a status receive `'default'`.
|
|
73
|
+
*
|
|
74
|
+
* This is the single source of truth shared by every adapter — call it once
|
|
75
|
+
* at render time and iterate over the result.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveTimelineItems(
|
|
78
|
+
items: TimelineItemData[],
|
|
79
|
+
): Required<TimelineItemData>[] {
|
|
80
|
+
return items.map((item) => ({
|
|
81
|
+
...item,
|
|
82
|
+
status: item.status ?? 'default',
|
|
83
|
+
time: item.time ?? '',
|
|
84
|
+
description: item.description ?? '',
|
|
85
|
+
}))
|
|
86
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createVideoGrid,
|
|
3
|
+
computeGridColumns,
|
|
4
|
+
bucketByCount,
|
|
5
|
+
type VideoGridLayout,
|
|
6
|
+
type VideoGridBucket,
|
|
7
|
+
type VideoGridHeadlessProps,
|
|
8
|
+
type VideoGridAPI,
|
|
9
|
+
} from './video-grid.js'
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
videoGridVariants,
|
|
13
|
+
videoGridSpotlightClass,
|
|
14
|
+
videoGridFilmstripClass,
|
|
15
|
+
videoGridFilmstripTileClass,
|
|
16
|
+
} from './video-grid.styles.js'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Grid container variants.
|
|
5
|
+
*
|
|
6
|
+
* - `layout: 'grid'` — uniform CSS grid; column count is set inline by the
|
|
7
|
+
* adapter using `computeGridColumns` (gridTemplateColumns).
|
|
8
|
+
* - `layout: 'speaker'` — two-row layout: large spotlight on top, filmstrip row
|
|
9
|
+
* below. The adapter renders the spotlight as the first child and a horizontal
|
|
10
|
+
* overflow row for remaining tiles.
|
|
11
|
+
*/
|
|
12
|
+
export const videoGridVariants = cva({
|
|
13
|
+
base: 'w-full h-full gap-2',
|
|
14
|
+
variants: {
|
|
15
|
+
layout: {
|
|
16
|
+
grid: 'grid',
|
|
17
|
+
speaker: 'flex flex-col',
|
|
18
|
+
auto: 'grid',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
layout: 'auto',
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
/** Full-bleed spotlight area in speaker view (takes up remaining vertical space). */
|
|
27
|
+
export const videoGridSpotlightClass = 'relative flex-1 min-h-0 rounded-xl overflow-hidden bg-muted'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Horizontal filmstrip row used in speaker view.
|
|
31
|
+
* Each tile in this row is constrained to a fixed width so the row scrolls
|
|
32
|
+
* rather than wraps on large calls.
|
|
33
|
+
*/
|
|
34
|
+
export const videoGridFilmstripClass =
|
|
35
|
+
'flex flex-row gap-2 overflow-x-auto py-1 shrink-0'
|
|
36
|
+
|
|
37
|
+
/** Individual filmstrip tile wrapper — fixed width so they don't collapse. */
|
|
38
|
+
export const videoGridFilmstripTileClass = 'w-36 shrink-0'
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout mode for a VideoGrid.
|
|
3
|
+
*
|
|
4
|
+
* - 'auto' — choose grid or speaker view based on participant count (≤1 forces speaker)
|
|
5
|
+
* - 'grid' — always render an even CSS grid of tiles
|
|
6
|
+
* - 'speaker' — render one large spotlight tile + a filmstrip row for the rest
|
|
7
|
+
*/
|
|
8
|
+
export type VideoGridLayout = 'auto' | 'grid' | 'speaker'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Bucket label that describes the rough meeting size.
|
|
12
|
+
* Useful for analytics and adaptive UI decisions.
|
|
13
|
+
*/
|
|
14
|
+
export type VideoGridBucket =
|
|
15
|
+
| 'solo'
|
|
16
|
+
| 'pair'
|
|
17
|
+
| 'small'
|
|
18
|
+
| 'medium'
|
|
19
|
+
| 'large'
|
|
20
|
+
| 'townhall'
|
|
21
|
+
|
|
22
|
+
export interface VideoGridHeadlessProps {
|
|
23
|
+
/** Desired layout mode. */
|
|
24
|
+
layout?: VideoGridLayout
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface VideoGridAPI {
|
|
28
|
+
/** ARIA attributes to spread on the grid container. */
|
|
29
|
+
ariaProps: {
|
|
30
|
+
role: 'group'
|
|
31
|
+
}
|
|
32
|
+
/** Data attributes for CSS styling hooks. */
|
|
33
|
+
dataAttributes: {
|
|
34
|
+
'data-layout': VideoGridLayout
|
|
35
|
+
'data-count'?: string
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compute the number of CSS grid columns for N participants.
|
|
41
|
+
*
|
|
42
|
+
* The responsive rule mirrors easyloops meeting layouts:
|
|
43
|
+
*
|
|
44
|
+
* | count | columns |
|
|
45
|
+
* |-------|---------|
|
|
46
|
+
* | 1 | 1 |
|
|
47
|
+
* | 2 | 2 |
|
|
48
|
+
* | 3–4 | 2 |
|
|
49
|
+
* | 5–9 | 3 |
|
|
50
|
+
* | 10–16 | 4 |
|
|
51
|
+
* | 17–25 | 5 |
|
|
52
|
+
* | 26+ | 6 |
|
|
53
|
+
*/
|
|
54
|
+
export function computeGridColumns(count: number): number {
|
|
55
|
+
if (count <= 1) return 1
|
|
56
|
+
if (count <= 4) return 2
|
|
57
|
+
if (count <= 9) return 3
|
|
58
|
+
if (count <= 16) return 4
|
|
59
|
+
if (count <= 25) return 5
|
|
60
|
+
return 6
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Label the meeting size as a human-readable bucket.
|
|
65
|
+
*
|
|
66
|
+
* Useful for analytics events and adaptive UI decisions without
|
|
67
|
+
* coupling to the exact column math.
|
|
68
|
+
*/
|
|
69
|
+
export function bucketByCount(count: number): VideoGridBucket {
|
|
70
|
+
if (count <= 1) return 'solo'
|
|
71
|
+
if (count <= 2) return 'pair'
|
|
72
|
+
if (count <= 9) return 'small'
|
|
73
|
+
if (count <= 16) return 'medium'
|
|
74
|
+
if (count <= 25) return 'large'
|
|
75
|
+
return 'townhall'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build the framework-agnostic ARIA and data attributes for a VideoGrid
|
|
80
|
+
* container. Adapters spread these onto their host element.
|
|
81
|
+
*/
|
|
82
|
+
export function createVideoGrid(props: VideoGridHeadlessProps = {}): VideoGridAPI {
|
|
83
|
+
const { layout = 'auto' } = props
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
ariaProps: {
|
|
87
|
+
role: 'group',
|
|
88
|
+
},
|
|
89
|
+
dataAttributes: {
|
|
90
|
+
'data-layout': layout,
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createVideoTile,
|
|
3
|
+
getInitials,
|
|
4
|
+
type VideoTileMicState,
|
|
5
|
+
type VideoTileData,
|
|
6
|
+
type VideoTileHeadlessProps,
|
|
7
|
+
type VideoTileAPI,
|
|
8
|
+
} from './video-tile.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
videoTileVariants,
|
|
12
|
+
videoTileNameChipClass,
|
|
13
|
+
videoTileMicIconClass,
|
|
14
|
+
videoTileAvatarFallbackClass,
|
|
15
|
+
videoTileAvatarCircleClass,
|
|
16
|
+
videoTileReactionBadgeClass,
|
|
17
|
+
} from './video-tile.styles.js'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Root tile container — aspect-video, rounded corners, clipped media.
|
|
5
|
+
*
|
|
6
|
+
* `speaking` variant adds a visible ring to indicate active audio.
|
|
7
|
+
* `pinned` variant adds a stronger ring to mark a pinned participant.
|
|
8
|
+
*
|
|
9
|
+
* cva boolean-like variants use string values ('true'/'false') per the
|
|
10
|
+
* refraction-ui convention — callers must pass strings, not raw booleans.
|
|
11
|
+
*/
|
|
12
|
+
export const videoTileVariants = cva({
|
|
13
|
+
base: [
|
|
14
|
+
'relative aspect-video overflow-hidden rounded-lg bg-muted',
|
|
15
|
+
'transition-shadow duration-200',
|
|
16
|
+
].join(' '),
|
|
17
|
+
variants: {
|
|
18
|
+
speaking: {
|
|
19
|
+
'true': 'ring-2 ring-emerald-500 dark:ring-emerald-400 ring-offset-2 ring-offset-background',
|
|
20
|
+
'false': '',
|
|
21
|
+
},
|
|
22
|
+
pinned: {
|
|
23
|
+
'true': 'ring-2 ring-primary ring-offset-2 ring-offset-background',
|
|
24
|
+
'false': '',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
speaking: 'false',
|
|
29
|
+
pinned: 'false',
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Name chip anchored to the bottom-left of the tile.
|
|
35
|
+
* Semi-transparent bg keeps it legible over any media content.
|
|
36
|
+
*/
|
|
37
|
+
export const videoTileNameChipClass =
|
|
38
|
+
'absolute bottom-2 left-2 flex items-center gap-1.5 rounded-md bg-background/70 px-2 py-0.5 text-xs font-medium text-foreground backdrop-blur-sm'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Mic-muted icon container — sits inside the name chip at the trailing edge.
|
|
42
|
+
*/
|
|
43
|
+
export const videoTileMicIconClass = 'text-destructive'
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Avatar fallback circle — centred in the tile when no media stream is available.
|
|
47
|
+
*/
|
|
48
|
+
export const videoTileAvatarFallbackClass =
|
|
49
|
+
'absolute inset-0 flex items-center justify-center'
|
|
50
|
+
|
|
51
|
+
/** Inner circle with initials. */
|
|
52
|
+
export const videoTileAvatarCircleClass =
|
|
53
|
+
'flex size-16 items-center justify-center rounded-full bg-card text-foreground text-xl font-semibold select-none'
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reaction badge slot — anchored to the top-right corner.
|
|
57
|
+
*/
|
|
58
|
+
export const videoTileReactionBadgeClass =
|
|
59
|
+
'absolute right-2 top-2 flex items-center justify-center rounded-full bg-background/70 px-1.5 py-0.5 text-sm backdrop-blur-sm'
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Microphone state for a video participant. */
|
|
2
|
+
export type VideoTileMicState = 'on' | 'muted'
|
|
3
|
+
|
|
4
|
+
/** Data describing a video meeting participant tile. */
|
|
5
|
+
export interface VideoTileData {
|
|
6
|
+
/** Unique participant identifier. */
|
|
7
|
+
id: string
|
|
8
|
+
/** Display name shown in the name chip. */
|
|
9
|
+
name: string
|
|
10
|
+
/** Current microphone state. Defaults to 'on'. */
|
|
11
|
+
micState?: VideoTileMicState
|
|
12
|
+
/** Whether the participant is currently speaking (active audio). */
|
|
13
|
+
speaking?: boolean
|
|
14
|
+
/** Whether the tile is pinned in the grid. */
|
|
15
|
+
pinned?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface VideoTileHeadlessProps {
|
|
19
|
+
/** Whether the participant is actively speaking. */
|
|
20
|
+
speaking?: boolean
|
|
21
|
+
/** Whether the tile is pinned in the grid. */
|
|
22
|
+
pinned?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface VideoTileAPI {
|
|
26
|
+
/** Data attributes for styling hooks — spread onto the tile container. */
|
|
27
|
+
dataAttributes: {
|
|
28
|
+
'data-speaking': string
|
|
29
|
+
'data-pinned': string
|
|
30
|
+
}
|
|
31
|
+
/** ARIA props — spread onto the tile container. */
|
|
32
|
+
ariaProps: {
|
|
33
|
+
role: 'group'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Derive framework-agnostic data attributes and ARIA props for a VideoTile.
|
|
39
|
+
*
|
|
40
|
+
* Adapters spread `dataAttributes` and `ariaProps` onto the tile container so
|
|
41
|
+
* CSS selectors and assistive technology can reflect the current participant
|
|
42
|
+
* state without coupling to a specific framework.
|
|
43
|
+
*/
|
|
44
|
+
export function createVideoTile(props: VideoTileHeadlessProps = {}): VideoTileAPI {
|
|
45
|
+
const { speaking = false, pinned = false } = props
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
dataAttributes: {
|
|
49
|
+
'data-speaking': speaking ? 'true' : 'false',
|
|
50
|
+
'data-pinned': pinned ? 'true' : 'false',
|
|
51
|
+
},
|
|
52
|
+
ariaProps: {
|
|
53
|
+
role: 'group',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Derive up to two uppercase initials from a display name.
|
|
60
|
+
*
|
|
61
|
+
* Splits on whitespace and takes the first character of the first two words.
|
|
62
|
+
* Single-word names produce a single initial.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* getInitials('Maya Goldberg') // 'MG'
|
|
66
|
+
* getInitials('Alice') // 'A'
|
|
67
|
+
*/
|
|
68
|
+
export function getInitials(name: string): string {
|
|
69
|
+
const words = name.trim().split(/\s+/).filter(Boolean)
|
|
70
|
+
return words
|
|
71
|
+
.slice(0, 2)
|
|
72
|
+
.map((w) => w[0].toUpperCase())
|
|
73
|
+
.join('')
|
|
74
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createWizard,
|
|
3
|
+
getStepState,
|
|
4
|
+
getNextIndex,
|
|
5
|
+
getPrevIndex,
|
|
6
|
+
canAdvance,
|
|
7
|
+
wizardProgress,
|
|
8
|
+
type WizardStep,
|
|
9
|
+
type WizardStepStatus,
|
|
10
|
+
type WizardStepState,
|
|
11
|
+
type WizardOrientation,
|
|
12
|
+
type WizardProps,
|
|
13
|
+
type WizardAPI,
|
|
14
|
+
} from './wizard.js'
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
wizardVariants,
|
|
18
|
+
wizardRailVariants,
|
|
19
|
+
wizardStepItemVariants,
|
|
20
|
+
wizardStepIndicatorVariants,
|
|
21
|
+
wizardStepLabelVariants,
|
|
22
|
+
wizardStepOptionalClass,
|
|
23
|
+
wizardConnectorVariants,
|
|
24
|
+
wizardContentClass,
|
|
25
|
+
wizardFooterClass,
|
|
26
|
+
wizardPrimaryButtonVariants,
|
|
27
|
+
wizardSecondaryButtonVariants,
|
|
28
|
+
} from './wizard.styles.js'
|