@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.
Files changed (117) hide show
  1. package/dist/astro-audio-room/AudioRoom.astro +81 -0
  2. package/dist/astro-audio-room/index.ts +1 -0
  3. package/dist/astro-call-controls/CallControls.astro +57 -0
  4. package/dist/astro-call-controls/index.ts +1 -0
  5. package/dist/astro-checklist/Checklist.astro +76 -0
  6. package/dist/astro-checklist/index.ts +1 -0
  7. package/dist/astro-editor-status-bar/EditorStatusBar.astro +92 -0
  8. package/dist/astro-editor-status-bar/index.ts +1 -0
  9. package/dist/astro-editor-tabs/EditorTabs.astro +74 -0
  10. package/dist/astro-editor-tabs/index.ts +1 -0
  11. package/dist/astro-floating-reactions/FloatingReactions.astro +56 -0
  12. package/dist/astro-floating-reactions/index.ts +1 -0
  13. package/dist/astro-flow-editor/FlowEditor.astro +99 -0
  14. package/dist/astro-flow-editor/index.ts +3 -0
  15. package/dist/astro-graph-view/GraphView.astro +109 -0
  16. package/dist/astro-graph-view/index.ts +1 -0
  17. package/dist/astro-infinite-canvas/InfiniteCanvas.astro +43 -0
  18. package/dist/astro-infinite-canvas/index.ts +1 -0
  19. package/dist/astro-live-captions/LiveCaptions.astro +54 -0
  20. package/dist/astro-live-captions/index.ts +1 -0
  21. package/dist/astro-live-cursors/LiveCursors.astro +55 -0
  22. package/dist/astro-live-cursors/index.ts +1 -0
  23. package/dist/astro-live-transcript/LiveTranscript.astro +55 -0
  24. package/dist/astro-live-transcript/index.ts +1 -0
  25. package/dist/astro-mini-map/MiniMap.astro +74 -0
  26. package/dist/astro-mini-map/index.ts +1 -0
  27. package/dist/astro-pre-call-lobby/PreCallLobby.astro +202 -0
  28. package/dist/astro-pre-call-lobby/index.ts +1 -0
  29. package/dist/astro-radial-gauge/RadialGauge.astro +144 -0
  30. package/dist/astro-radial-gauge/index.ts +1 -0
  31. package/dist/astro-rating-scale/RatingScale.astro +73 -0
  32. package/dist/astro-rating-scale/index.ts +1 -0
  33. package/dist/astro-sticky-note/StickyNote.astro +55 -0
  34. package/dist/astro-sticky-note/index.ts +1 -0
  35. package/dist/astro-terminal/Terminal.astro +67 -0
  36. package/dist/astro-terminal/index.ts +1 -0
  37. package/dist/astro-test-results/TestResults.astro +76 -0
  38. package/dist/astro-test-results/index.ts +1 -0
  39. package/dist/astro-timeline/Timeline.astro +59 -0
  40. package/dist/astro-timeline/index.ts +1 -0
  41. package/dist/astro-video-grid/VideoGrid.astro +182 -0
  42. package/dist/astro-video-grid/index.ts +1 -0
  43. package/dist/astro-video-tile/VideoTile.astro +104 -0
  44. package/dist/astro-video-tile/index.ts +1 -0
  45. package/dist/astro-wizard/Wizard.astro +137 -0
  46. package/dist/astro-wizard/index.ts +1 -0
  47. package/dist/audio-room/audio-room.styles.ts +88 -0
  48. package/dist/audio-room/audio-room.ts +70 -0
  49. package/dist/audio-room/index.ts +16 -0
  50. package/dist/call-controls/call-controls.styles.ts +63 -0
  51. package/dist/call-controls/call-controls.ts +79 -0
  52. package/dist/call-controls/index.ts +15 -0
  53. package/dist/checklist/checklist.styles.ts +76 -0
  54. package/dist/checklist/checklist.ts +62 -0
  55. package/dist/checklist/index.ts +18 -0
  56. package/dist/editor-status-bar/editor-status-bar.styles.ts +40 -0
  57. package/dist/editor-status-bar/editor-status-bar.ts +78 -0
  58. package/dist/editor-status-bar/index.ts +15 -0
  59. package/dist/editor-tabs/editor-tabs.styles.ts +52 -0
  60. package/dist/editor-tabs/editor-tabs.ts +82 -0
  61. package/dist/editor-tabs/index.ts +15 -0
  62. package/dist/floating-reactions/floating-reactions.styles.ts +54 -0
  63. package/dist/floating-reactions/floating-reactions.ts +85 -0
  64. package/dist/floating-reactions/index.ts +14 -0
  65. package/dist/flow-editor/flow-editor.styles.ts +56 -0
  66. package/dist/flow-editor/flow-editor.ts +126 -0
  67. package/dist/flow-editor/index.ts +18 -0
  68. package/dist/graph-view/graph-view.styles.ts +66 -0
  69. package/dist/graph-view/graph-view.ts +130 -0
  70. package/dist/graph-view/index.ts +21 -0
  71. package/dist/index.ts +23 -0
  72. package/dist/infinite-canvas/index.ts +17 -0
  73. package/dist/infinite-canvas/infinite-canvas.styles.ts +49 -0
  74. package/dist/infinite-canvas/infinite-canvas.ts +97 -0
  75. package/dist/live-captions/index.ts +13 -0
  76. package/dist/live-captions/live-captions.styles.ts +45 -0
  77. package/dist/live-captions/live-captions.ts +57 -0
  78. package/dist/live-cursors/index.ts +14 -0
  79. package/dist/live-cursors/live-cursors.styles.ts +41 -0
  80. package/dist/live-cursors/live-cursors.ts +73 -0
  81. package/dist/live-transcript/index.ts +16 -0
  82. package/dist/live-transcript/live-transcript.styles.ts +48 -0
  83. package/dist/live-transcript/live-transcript.ts +82 -0
  84. package/dist/mini-map/index.ts +16 -0
  85. package/dist/mini-map/mini-map.styles.ts +32 -0
  86. package/dist/mini-map/mini-map.ts +122 -0
  87. package/dist/pre-call-lobby/index.ts +22 -0
  88. package/dist/pre-call-lobby/pre-call-lobby.styles.ts +108 -0
  89. package/dist/pre-call-lobby/pre-call-lobby.ts +61 -0
  90. package/dist/radial-gauge/index.ts +26 -0
  91. package/dist/radial-gauge/radial-gauge.styles.ts +75 -0
  92. package/dist/radial-gauge/radial-gauge.ts +183 -0
  93. package/dist/rating-scale/index.ts +16 -0
  94. package/dist/rating-scale/rating-scale.styles.ts +48 -0
  95. package/dist/rating-scale/rating-scale.ts +101 -0
  96. package/dist/sticky-note/index.ts +14 -0
  97. package/dist/sticky-note/sticky-note.styles.ts +37 -0
  98. package/dist/sticky-note/sticky-note.ts +47 -0
  99. package/dist/terminal/index.ts +13 -0
  100. package/dist/terminal/terminal.styles.ts +56 -0
  101. package/dist/terminal/terminal.ts +60 -0
  102. package/dist/test-results/index.ts +19 -0
  103. package/dist/test-results/test-results.styles.ts +93 -0
  104. package/dist/test-results/test-results.ts +83 -0
  105. package/dist/timeline/index.ts +22 -0
  106. package/dist/timeline/timeline.styles.ts +98 -0
  107. package/dist/timeline/timeline.ts +86 -0
  108. package/dist/video-grid/index.ts +16 -0
  109. package/dist/video-grid/video-grid.styles.ts +38 -0
  110. package/dist/video-grid/video-grid.ts +93 -0
  111. package/dist/video-tile/index.ts +17 -0
  112. package/dist/video-tile/video-tile.styles.ts +59 -0
  113. package/dist/video-tile/video-tile.ts +74 -0
  114. package/dist/wizard/index.ts +28 -0
  115. package/dist/wizard/wizard.styles.ts +147 -0
  116. package/dist/wizard/wizard.ts +128 -0
  117. 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'