@refraction-ui/astro 0.11.1 → 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 (148) 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-empty-state/EmptyState.astro +57 -0
  12. package/dist/astro-empty-state/index.ts +1 -0
  13. package/dist/astro-floating-reactions/FloatingReactions.astro +56 -0
  14. package/dist/astro-floating-reactions/index.ts +1 -0
  15. package/dist/astro-flow-editor/FlowEditor.astro +99 -0
  16. package/dist/astro-flow-editor/index.ts +3 -0
  17. package/dist/astro-graph-view/GraphView.astro +109 -0
  18. package/dist/astro-graph-view/index.ts +1 -0
  19. package/dist/astro-infinite-canvas/InfiniteCanvas.astro +43 -0
  20. package/dist/astro-infinite-canvas/index.ts +1 -0
  21. package/dist/astro-input/Input.astro +5 -1
  22. package/dist/astro-live-captions/LiveCaptions.astro +54 -0
  23. package/dist/astro-live-captions/index.ts +1 -0
  24. package/dist/astro-live-cursors/LiveCursors.astro +55 -0
  25. package/dist/astro-live-cursors/index.ts +1 -0
  26. package/dist/astro-live-transcript/LiveTranscript.astro +55 -0
  27. package/dist/astro-live-transcript/index.ts +1 -0
  28. package/dist/astro-mini-map/MiniMap.astro +74 -0
  29. package/dist/astro-mini-map/index.ts +1 -0
  30. package/dist/astro-password-input/PasswordInput.astro +102 -0
  31. package/dist/astro-password-input/index.ts +1 -0
  32. package/dist/astro-pre-call-lobby/PreCallLobby.astro +202 -0
  33. package/dist/astro-pre-call-lobby/index.ts +1 -0
  34. package/dist/astro-radial-gauge/RadialGauge.astro +144 -0
  35. package/dist/astro-radial-gauge/index.ts +1 -0
  36. package/dist/astro-rating-scale/RatingScale.astro +73 -0
  37. package/dist/astro-rating-scale/index.ts +1 -0
  38. package/dist/astro-segmented-control/SegmentedControl.astro +106 -0
  39. package/dist/astro-segmented-control/SegmentedControlItem.astro +32 -0
  40. package/dist/astro-segmented-control/index.ts +1 -0
  41. package/dist/astro-separator/Separator.astro +49 -0
  42. package/dist/astro-separator/index.ts +1 -0
  43. package/dist/astro-social-auth-button/SocialAuthButton.astro +110 -0
  44. package/dist/astro-social-auth-button/SocialAuthRow.astro +13 -0
  45. package/dist/astro-social-auth-button/index.ts +1 -0
  46. package/dist/astro-sticky-note/StickyNote.astro +55 -0
  47. package/dist/astro-sticky-note/index.ts +1 -0
  48. package/dist/astro-terminal/Terminal.astro +67 -0
  49. package/dist/astro-terminal/index.ts +1 -0
  50. package/dist/astro-test-results/TestResults.astro +76 -0
  51. package/dist/astro-test-results/index.ts +1 -0
  52. package/dist/astro-timeline/Timeline.astro +59 -0
  53. package/dist/astro-timeline/index.ts +1 -0
  54. package/dist/astro-video-grid/VideoGrid.astro +182 -0
  55. package/dist/astro-video-grid/index.ts +1 -0
  56. package/dist/astro-video-tile/VideoTile.astro +104 -0
  57. package/dist/astro-video-tile/index.ts +1 -0
  58. package/dist/astro-wizard/Wizard.astro +137 -0
  59. package/dist/astro-wizard/index.ts +1 -0
  60. package/dist/audio-room/audio-room.styles.ts +88 -0
  61. package/dist/audio-room/audio-room.ts +70 -0
  62. package/dist/audio-room/index.ts +16 -0
  63. package/dist/call-controls/call-controls.styles.ts +63 -0
  64. package/dist/call-controls/call-controls.ts +79 -0
  65. package/dist/call-controls/index.ts +15 -0
  66. package/dist/checklist/checklist.styles.ts +76 -0
  67. package/dist/checklist/checklist.ts +62 -0
  68. package/dist/checklist/index.ts +18 -0
  69. package/dist/editor-status-bar/editor-status-bar.styles.ts +40 -0
  70. package/dist/editor-status-bar/editor-status-bar.ts +78 -0
  71. package/dist/editor-status-bar/index.ts +15 -0
  72. package/dist/editor-tabs/editor-tabs.styles.ts +52 -0
  73. package/dist/editor-tabs/editor-tabs.ts +82 -0
  74. package/dist/editor-tabs/index.ts +15 -0
  75. package/dist/empty-state/empty-state.styles.ts +42 -0
  76. package/dist/empty-state/empty-state.ts +27 -0
  77. package/dist/empty-state/index.ts +14 -0
  78. package/dist/floating-reactions/floating-reactions.styles.ts +54 -0
  79. package/dist/floating-reactions/floating-reactions.ts +85 -0
  80. package/dist/floating-reactions/index.ts +14 -0
  81. package/dist/flow-editor/flow-editor.styles.ts +56 -0
  82. package/dist/flow-editor/flow-editor.ts +126 -0
  83. package/dist/flow-editor/index.ts +18 -0
  84. package/dist/graph-view/graph-view.styles.ts +66 -0
  85. package/dist/graph-view/graph-view.ts +130 -0
  86. package/dist/graph-view/index.ts +21 -0
  87. package/dist/index.ts +45 -17
  88. package/dist/infinite-canvas/index.ts +17 -0
  89. package/dist/infinite-canvas/infinite-canvas.styles.ts +49 -0
  90. package/dist/infinite-canvas/infinite-canvas.ts +97 -0
  91. package/dist/input/index.ts +2 -0
  92. package/dist/input/input.styles.ts +4 -0
  93. package/dist/input/input.ts +39 -3
  94. package/dist/live-captions/index.ts +13 -0
  95. package/dist/live-captions/live-captions.styles.ts +45 -0
  96. package/dist/live-captions/live-captions.ts +57 -0
  97. package/dist/live-cursors/index.ts +14 -0
  98. package/dist/live-cursors/live-cursors.styles.ts +41 -0
  99. package/dist/live-cursors/live-cursors.ts +73 -0
  100. package/dist/live-transcript/index.ts +16 -0
  101. package/dist/live-transcript/live-transcript.styles.ts +48 -0
  102. package/dist/live-transcript/live-transcript.ts +82 -0
  103. package/dist/mini-map/index.ts +16 -0
  104. package/dist/mini-map/mini-map.styles.ts +32 -0
  105. package/dist/mini-map/mini-map.ts +122 -0
  106. package/dist/password-input/index.ts +9 -0
  107. package/dist/password-input/password-input.styles.ts +10 -0
  108. package/dist/password-input/password-input.ts +45 -0
  109. package/dist/pre-call-lobby/index.ts +22 -0
  110. package/dist/pre-call-lobby/pre-call-lobby.styles.ts +108 -0
  111. package/dist/pre-call-lobby/pre-call-lobby.ts +61 -0
  112. package/dist/radial-gauge/index.ts +26 -0
  113. package/dist/radial-gauge/radial-gauge.styles.ts +75 -0
  114. package/dist/radial-gauge/radial-gauge.ts +183 -0
  115. package/dist/rating-scale/index.ts +16 -0
  116. package/dist/rating-scale/rating-scale.styles.ts +48 -0
  117. package/dist/rating-scale/rating-scale.ts +101 -0
  118. package/dist/segmented-control/index.ts +13 -0
  119. package/dist/segmented-control/segmented-control.styles.ts +42 -0
  120. package/dist/segmented-control/segmented-control.ts +80 -0
  121. package/dist/separator/index.ts +12 -0
  122. package/dist/separator/separator.styles.ts +34 -0
  123. package/dist/separator/separator.ts +53 -0
  124. package/dist/social-auth-button/index.ts +14 -0
  125. package/dist/social-auth-button/social-auth-button.styles.ts +16 -0
  126. package/dist/social-auth-button/social-auth-button.ts +51 -0
  127. package/dist/sticky-note/index.ts +14 -0
  128. package/dist/sticky-note/sticky-note.styles.ts +37 -0
  129. package/dist/sticky-note/sticky-note.ts +47 -0
  130. package/dist/terminal/index.ts +13 -0
  131. package/dist/terminal/terminal.styles.ts +56 -0
  132. package/dist/terminal/terminal.ts +60 -0
  133. package/dist/test-results/index.ts +19 -0
  134. package/dist/test-results/test-results.styles.ts +93 -0
  135. package/dist/test-results/test-results.ts +83 -0
  136. package/dist/timeline/index.ts +22 -0
  137. package/dist/timeline/timeline.styles.ts +98 -0
  138. package/dist/timeline/timeline.ts +86 -0
  139. package/dist/video-grid/index.ts +16 -0
  140. package/dist/video-grid/video-grid.styles.ts +38 -0
  141. package/dist/video-grid/video-grid.ts +93 -0
  142. package/dist/video-tile/index.ts +17 -0
  143. package/dist/video-tile/video-tile.styles.ts +59 -0
  144. package/dist/video-tile/video-tile.ts +74 -0
  145. package/dist/wizard/index.ts +28 -0
  146. package/dist/wizard/wizard.styles.ts +147 -0
  147. package/dist/wizard/wizard.ts +128 -0
  148. package/package.json +47 -19
@@ -0,0 +1,110 @@
1
+ ---
2
+ import {
3
+ createSocialAuthButton,
4
+ socialAuthButtonVariants,
5
+ lastUsedBadgeVariants,
6
+ type SocialProvider,
7
+ } from '../social-auth-button/index.ts'
8
+ import { cn } from '../shared/index.ts'
9
+
10
+ interface Props extends astroHTML.JSX.HTMLAttributes {
11
+ provider: SocialProvider
12
+ lastUsed?: boolean
13
+ loading?: boolean
14
+ disabled?: boolean
15
+ }
16
+
17
+ const {
18
+ provider,
19
+ lastUsed,
20
+ loading,
21
+ disabled,
22
+ class: className,
23
+ ...props
24
+ } = Astro.props
25
+
26
+ const { label, mono, dataAttributes } = createSocialAuthButton({ provider })
27
+
28
+ // Mirrors the react-button outline variant base + outline classes so the Astro
29
+ // and React buttons render identically.
30
+ const buttonClasses = cn(
31
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
32
+ 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
33
+ 'w-full justify-center gap-2',
34
+ className,
35
+ )
36
+
37
+ const iconFill = (color: string) => (mono ? 'currentColor' : color)
38
+ ---
39
+
40
+ <div class={socialAuthButtonVariants()}>
41
+ <button
42
+ type="button"
43
+ class={buttonClasses}
44
+ disabled={disabled || loading}
45
+ {...dataAttributes}
46
+ {...props}
47
+ >
48
+ {loading ? (
49
+ <svg
50
+ class="animate-spin h-4 w-4"
51
+ xmlns="http://www.w3.org/2000/svg"
52
+ fill="none"
53
+ viewBox="0 0 24 24"
54
+ aria-hidden="true"
55
+ >
56
+ <circle
57
+ class="opacity-25"
58
+ cx="12"
59
+ cy="12"
60
+ r="10"
61
+ stroke="currentColor"
62
+ stroke-width="4"
63
+ />
64
+ <path
65
+ class="opacity-75"
66
+ fill="currentColor"
67
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
68
+ />
69
+ </svg>
70
+ ) : (
71
+ <svg
72
+ class="shrink-0"
73
+ width="18"
74
+ height="18"
75
+ viewBox="0 0 24 24"
76
+ xmlns="http://www.w3.org/2000/svg"
77
+ aria-hidden="true"
78
+ >
79
+ {provider === 'google' && (
80
+ <path
81
+ fill={iconFill('#4285F4')}
82
+ d="M21.35 11.1H12v2.92h5.35c-.23 1.4-1.65 4.1-5.35 4.1a5.99 5.99 0 0 1 0-11.98c1.71 0 2.86.73 3.52 1.36l2.4-2.31C16.46 3.7 14.43 2.8 12 2.8a9.2 9.2 0 1 0 0 18.4c5.31 0 8.82-3.73 8.82-8.99 0-.6-.07-1.06-.17-1.51Z"
83
+ />
84
+ )}
85
+ {provider === 'github' && (
86
+ <path
87
+ fill="currentColor"
88
+ d="M12 2a10 10 0 0 0-3.16 19.49c.5.09.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.45-1.16-1.11-1.46-1.11-1.46-.91-.62.07-.61.07-.61 1 .07 1.53 1.03 1.53 1.03.9 1.53 2.36 1.09 2.94.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.09.39-1.98 1.03-2.68-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02a9.6 9.6 0 0 1 5 0c1.91-1.29 2.75-1.02 2.75-1.02.55 1.38.2 2.4.1 2.65.64.7 1.03 1.59 1.03 2.68 0 3.84-2.34 4.69-4.57 4.94.36.31.68.92.68 1.85l-.01 2.74c0 .27.18.58.69.48A10 10 0 0 0 12 2Z"
89
+ />
90
+ )}
91
+ {provider === 'microsoft' && (
92
+ <Fragment>
93
+ <path fill={iconFill('#F25022')} d="M3 3h8v8H3z" />
94
+ <path fill={iconFill('#7FBA00')} d="M13 3h8v8h-8z" />
95
+ <path fill={iconFill('#00A4EF')} d="M3 13h8v8H3z" />
96
+ <path fill={iconFill('#FFB900')} d="M13 13h8v8h-8z" />
97
+ </Fragment>
98
+ )}
99
+ {provider === 'apple' && (
100
+ <path
101
+ fill="currentColor"
102
+ d="M16.37 12.78c-.03-2.62 2.14-3.88 2.24-3.94-1.22-1.79-3.12-2.03-3.79-2.06-1.61-.16-3.15.95-3.97.95-.82 0-2.08-.93-3.42-.9-1.76.03-3.39 1.02-4.29 2.6-1.83 3.18-.47 7.88 1.31 10.46.87 1.26 1.91 2.67 3.27 2.62 1.31-.05 1.81-.85 3.4-.85 1.58 0 2.03.85 3.42.82 1.41-.02 2.31-1.28 3.17-2.55.99-1.46 1.4-2.88 1.42-2.95-.03-.01-2.73-1.05-2.76-4.15M13.9 5.1c.72-.88 1.21-2.09 1.08-3.3-1.04.04-2.3.69-3.04 1.56-.66.77-1.24 2.01-1.09 3.19 1.16.09 2.34-.59 3.05-1.45"
103
+ />
104
+ )}
105
+ </svg>
106
+ )}
107
+ {label}
108
+ </button>
109
+ {lastUsed && <span class={lastUsedBadgeVariants()}>Last used</span>}
110
+ </div>
@@ -0,0 +1,13 @@
1
+ ---
2
+ import { socialAuthRowVariants } from '../social-auth-button/index.ts'
3
+ import { cn } from '../shared/index.ts'
4
+
5
+ interface Props extends astroHTML.JSX.HTMLAttributes {}
6
+
7
+ const { class: className, ...props } = Astro.props
8
+ const classes = cn(socialAuthRowVariants(), className)
9
+ ---
10
+
11
+ <div class={classes} {...props}>
12
+ <slot />
13
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,55 @@
1
+ ---
2
+ import {
3
+ createStickyNote,
4
+ stickyNoteVariants,
5
+ stickyNoteTextClass,
6
+ stickyNoteAuthorClass,
7
+ type StickyNoteColor,
8
+ } from '../sticky-note/index.ts'
9
+ import { cn } from '../shared/index.ts'
10
+
11
+ interface Props extends astroHTML.JSX.HTMLAttributes {
12
+ /** Color of the sticky note. */
13
+ color?: StickyNoteColor
14
+ /** Text content of the note. */
15
+ text?: string
16
+ /** Optional author name shown in the note footer. */
17
+ author?: string
18
+ /** Absolute x position in pixels (for board/canvas placement). */
19
+ x?: number
20
+ /** Absolute y position in pixels (for board/canvas placement). */
21
+ y?: number
22
+ }
23
+
24
+ const {
25
+ color = 'yellow',
26
+ text,
27
+ author,
28
+ x,
29
+ y,
30
+ class: className,
31
+ ...rest
32
+ } = Astro.props
33
+
34
+ const { ariaProps, dataAttributes } = createStickyNote({ color })
35
+
36
+ const positionStyle =
37
+ x !== undefined && y !== undefined
38
+ ? `position:absolute;left:${x}px;top:${y}px;`
39
+ : ''
40
+ ---
41
+
42
+ <div
43
+ class={cn(stickyNoteVariants({ color }), className)}
44
+ style={positionStyle || undefined}
45
+ {...ariaProps}
46
+ {...dataAttributes}
47
+ {...rest}
48
+ >
49
+ {text ? (
50
+ <p class={`${stickyNoteTextClass} whitespace-pre-wrap`}>{text}</p>
51
+ ) : (
52
+ <p class={`${stickyNoteTextClass} whitespace-pre-wrap`}><slot /></p>
53
+ )}
54
+ {author && <span class={stickyNoteAuthorClass}>{author}</span>}
55
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,67 @@
1
+ ---
2
+ import {
3
+ createTerminal,
4
+ formatPrompt,
5
+ terminalVariants,
6
+ terminalLineVariants,
7
+ terminalPromptClass,
8
+ type TerminalLine,
9
+ } from '../terminal/index.ts'
10
+ import { cn } from '../shared/index.ts'
11
+
12
+ type TerminalMaxHeight = 'sm' | 'md' | 'lg' | 'none'
13
+
14
+ interface Props extends astroHTML.JSX.HTMLAttributes {
15
+ /** Lines to render in order. Each line's `kind` determines its styling. */
16
+ lines: TerminalLine[]
17
+ /**
18
+ * Prompt symbol prepended to `command` lines.
19
+ * @default '$'
20
+ */
21
+ promptSymbol?: string
22
+ /**
23
+ * Maximum height of the scroll region before it becomes scrollable.
24
+ * @default 'md'
25
+ */
26
+ maxHeight?: TerminalMaxHeight
27
+ /** Accessible label for the live region. */
28
+ 'aria-label'?: string
29
+ }
30
+
31
+ const {
32
+ lines,
33
+ promptSymbol = '$',
34
+ maxHeight = 'md',
35
+ 'aria-label': ariaLabel,
36
+ class: className,
37
+ ...rest
38
+ } = Astro.props
39
+
40
+ const { ariaProps, dataAttributes } = createTerminal({ label: ariaLabel })
41
+ ---
42
+
43
+ <div
44
+ class={cn(terminalVariants({ maxHeight }), className)}
45
+ {...ariaProps}
46
+ {...dataAttributes}
47
+ {...rest}
48
+ >
49
+ {
50
+ lines.map((line, index) => {
51
+ const lineClass = terminalLineVariants({ kind: line.kind })
52
+ if (line.kind === 'command') {
53
+ return (
54
+ <span key={line.id ?? index} class={lineClass}>
55
+ <span class={terminalPromptClass}>{promptSymbol}</span>
56
+ {line.text}
57
+ </span>
58
+ )
59
+ }
60
+ return (
61
+ <span key={line.id ?? index} class={lineClass}>
62
+ {line.text}
63
+ </span>
64
+ )
65
+ })
66
+ }
67
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,76 @@
1
+ ---
2
+ import {
3
+ summarizeTests,
4
+ createTestResults,
5
+ testResultsContainerVariants,
6
+ testResultsSummaryVariants,
7
+ testRowVariants,
8
+ testStatusBadgeVariants,
9
+ testNameClass,
10
+ testDurationClass,
11
+ testDiffBlockVariants,
12
+ testMessageClass,
13
+ type TestResultData,
14
+ } from '../test-results/index.ts'
15
+ import { cn } from '../shared/index.ts'
16
+
17
+ interface Props extends astroHTML.JSX.HTMLAttributes {
18
+ /** Array of test case results to display. */
19
+ results: TestResultData[]
20
+ /** When true, renders a summary bar showing "{passed}/{total} passed". */
21
+ showSummary?: boolean
22
+ }
23
+
24
+ const { results, showSummary = true, class: className, ...rest } = Astro.props
25
+
26
+ const summary = summarizeTests(results)
27
+ const { ariaProps, dataAttributes } = createTestResults()
28
+ const summaryOutcome = summary.failed > 0 ? 'fail' : summary.skipped > 0 ? 'skip' : 'pass'
29
+ ---
30
+
31
+ <div class={cn(testResultsContainerVariants(), className)} {...dataAttributes} {...rest}>
32
+ {showSummary && results.length > 0 && (
33
+ <div class={testResultsSummaryVariants({ outcome: summaryOutcome })} aria-live="polite">
34
+ <span>{summary.passed}/{summary.total} passed</span>
35
+ {summary.failed > 0 && <span>· {summary.failed} failed</span>}
36
+ {summary.skipped > 0 && <span>· {summary.skipped} skipped</span>}
37
+ </div>
38
+ )}
39
+
40
+ <ul {...ariaProps} class="flex flex-col gap-2">
41
+ {
42
+ results.map((result) => (
43
+ <li role="listitem" class={testRowVariants({ status: result.status })}>
44
+ <div class="flex items-center justify-between gap-2">
45
+ <div class="flex items-center gap-2 min-w-0">
46
+ <span class={testStatusBadgeVariants({ status: result.status })}>
47
+ {result.status}
48
+ </span>
49
+ <span class={cn(testNameClass, 'truncate')}>{result.name}</span>
50
+ </div>
51
+ {result.durationMs !== undefined && (
52
+ <span class={testDurationClass}>{result.durationMs} ms</span>
53
+ )}
54
+ </div>
55
+
56
+ {result.status === 'fail' && result.expected !== undefined && (
57
+ <div class="flex flex-col gap-1 mt-1">
58
+ <div class={testDiffBlockVariants({ side: 'expected' })}>
59
+ <span class="opacity-60 mr-1 select-none">expected</span>
60
+ {result.expected}
61
+ </div>
62
+ {result.actual !== undefined && (
63
+ <div class={testDiffBlockVariants({ side: 'actual' })}>
64
+ <span class="opacity-60 mr-1 select-none">actual</span>
65
+ {result.actual}
66
+ </div>
67
+ )}
68
+ </div>
69
+ )}
70
+
71
+ {result.message && <p class={testMessageClass}>{result.message}</p>}
72
+ </li>
73
+ ))
74
+ }
75
+ </ul>
76
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,59 @@
1
+ ---
2
+ import {
3
+ createTimeline,
4
+ resolveTimelineItems,
5
+ timelineVariants,
6
+ timelineItemVariants,
7
+ timelineMarkerVariants,
8
+ timelineConnectorVariants,
9
+ timelineGutterClass,
10
+ timelineTimeClass,
11
+ timelineTitleClass,
12
+ timelineDescriptionClass,
13
+ timelineContentClass,
14
+ type TimelineOrientation,
15
+ type TimelineItemData,
16
+ } from '../timeline/index.ts'
17
+ import { cn } from '../shared/index.ts'
18
+
19
+ interface Props extends astroHTML.JSX.HTMLAttributes {
20
+ /** Ordered list of events to render. */
21
+ items: TimelineItemData[]
22
+ /** Layout direction. Defaults to `'vertical'`. */
23
+ orientation?: TimelineOrientation
24
+ }
25
+
26
+ const {
27
+ items,
28
+ orientation = 'vertical',
29
+ class: className,
30
+ ...rest
31
+ } = Astro.props
32
+
33
+ const resolved = resolveTimelineItems(items)
34
+ const { ariaProps, dataAttributes } = createTimeline({ orientation })
35
+ ---
36
+
37
+ <ul class={cn(timelineVariants({ orientation }), className)} {...ariaProps} {...dataAttributes} {...rest}>
38
+ {
39
+ resolved.map((item, index) => {
40
+ const isLast = index === resolved.length - 1
41
+ return (
42
+ <li role="listitem" class={timelineItemVariants({ orientation })} data-status={item.status}>
43
+ {/* Gutter: marker + connector */}
44
+ <div class={timelineGutterClass}>
45
+ <div class={timelineMarkerVariants({ status: item.status })} />
46
+ {!isLast && <div class={timelineConnectorVariants({ orientation })} />}
47
+ </div>
48
+
49
+ {/* Content */}
50
+ <div class={timelineContentClass}>
51
+ {item.time && <span class={timelineTimeClass}>{item.time}</span>}
52
+ <span class={timelineTitleClass}>{item.title}</span>
53
+ {item.description && <p class={timelineDescriptionClass}>{item.description}</p>}
54
+ </div>
55
+ </li>
56
+ )
57
+ })
58
+ }
59
+ </ul>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,182 @@
1
+ ---
2
+ import {
3
+ createVideoGrid,
4
+ computeGridColumns,
5
+ videoGridVariants,
6
+ videoGridSpotlightClass,
7
+ videoGridFilmstripClass,
8
+ videoGridFilmstripTileClass,
9
+ type VideoGridLayout,
10
+ } from '../video-grid/index.ts'
11
+ import { getInitials, type VideoTileData } from '../video-tile/index.ts'
12
+ import { cn } from '../shared/index.ts'
13
+
14
+ interface Props extends astroHTML.JSX.HTMLAttributes {
15
+ /** List of participant data to render as tiles. */
16
+ participants: VideoTileData[]
17
+ /**
18
+ * Grid layout mode.
19
+ * - 'auto' — grid unless ≤1 participant (falls back to speaker)
20
+ * - 'grid' — always uniform CSS grid
21
+ * - 'speaker' — spotlight for `spotlightId` (or first participant) + filmstrip
22
+ */
23
+ layout?: VideoGridLayout
24
+ /**
25
+ * Participant `id` to spotlight in speaker view.
26
+ * Falls back to the first participant when omitted.
27
+ */
28
+ spotlightId?: string
29
+ }
30
+
31
+ const {
32
+ participants,
33
+ layout = 'auto',
34
+ spotlightId,
35
+ class: className,
36
+ style,
37
+ ...rest
38
+ } = Astro.props
39
+
40
+ const count = participants.length
41
+
42
+ // Resolve effective layout: 'auto' with ≤1 participant shows speaker view.
43
+ const effectiveLayout: VideoGridLayout =
44
+ layout === 'auto' ? (count <= 1 ? 'speaker' : 'grid') : layout
45
+
46
+ const { ariaProps, dataAttributes } = createVideoGrid({ layout: effectiveLayout })
47
+ const columns = computeGridColumns(count)
48
+
49
+ const gridStyle =
50
+ effectiveLayout !== 'speaker'
51
+ ? `grid-template-columns: repeat(${columns}, 1fr);${style ? ` ${style}` : ''}`
52
+ : (style as string | undefined)
53
+
54
+ const spotlightParticipant =
55
+ effectiveLayout === 'speaker'
56
+ ? (participants.find((p) => p.id === spotlightId) ?? participants[0])
57
+ : undefined
58
+
59
+ const filmstrip =
60
+ effectiveLayout === 'speaker'
61
+ ? participants.filter((p) => p.id !== spotlightParticipant?.id)
62
+ : []
63
+
64
+ // Inline tile helper — renders VideoTile markup without importing the .astro
65
+ // file (which may not be built yet in a parallel-build context). When
66
+ // astro-video-tile ships its own VideoTile.astro, replace this with:
67
+ // import VideoTile from '../astro-video-tile/src/VideoTile.astro.ts'
68
+ function renderTileAttrs(p: VideoTileData): Record<string, string> {
69
+ const speaking = p.speaking ?? false
70
+ const pinned = p.pinned ?? false
71
+ return {
72
+ role: 'group',
73
+ 'data-speaking': speaking ? 'true' : 'false',
74
+ 'data-pinned': pinned ? 'true' : 'false',
75
+ }
76
+ }
77
+ ---
78
+
79
+ {effectiveLayout === 'speaker' ? (
80
+ <div
81
+ class={cn(videoGridVariants({ layout: 'speaker' }), className)}
82
+ style={style as string | undefined}
83
+ {...ariaProps}
84
+ {...dataAttributes}
85
+ {...rest}
86
+ >
87
+ {spotlightParticipant && (
88
+ <div class={videoGridSpotlightClass}>
89
+ {/* Inline VideoTile markup for the spotlight participant */}
90
+ <div
91
+ {...renderTileAttrs(spotlightParticipant)}
92
+ class="relative flex h-full w-full flex-col items-center justify-center bg-muted rounded-xl overflow-hidden"
93
+ aria-label={spotlightParticipant.name}
94
+ >
95
+ {spotlightParticipant.avatarUrl ? (
96
+ <img
97
+ src={spotlightParticipant.avatarUrl}
98
+ alt={spotlightParticipant.name}
99
+ class="h-full w-full object-cover"
100
+ />
101
+ ) : (
102
+ <span class="flex h-20 w-20 items-center justify-center rounded-full bg-primary text-3xl font-semibold text-primary-foreground">
103
+ {getInitials(spotlightParticipant.name)}
104
+ </span>
105
+ )}
106
+ <div class="absolute bottom-2 left-2 flex items-center gap-1 rounded-md bg-black/60 px-2 py-0.5 text-xs text-white">
107
+ {(spotlightParticipant.micState ?? 'on') === 'muted' && (
108
+ <span aria-label="Muted" role="img">🔇</span>
109
+ )}
110
+ <span>{spotlightParticipant.name}</span>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ )}
115
+
116
+ {filmstrip.length > 0 && (
117
+ <div class={videoGridFilmstripClass}>
118
+ {filmstrip.map((p) => (
119
+ <div class={videoGridFilmstripTileClass}>
120
+ <div
121
+ {...renderTileAttrs(p)}
122
+ class="relative flex h-24 flex-col items-center justify-center bg-muted rounded-lg overflow-hidden"
123
+ aria-label={p.name}
124
+ >
125
+ {p.avatarUrl ? (
126
+ <img
127
+ src={p.avatarUrl}
128
+ alt={p.name}
129
+ class="h-full w-full object-cover"
130
+ />
131
+ ) : (
132
+ <span class="flex h-10 w-10 items-center justify-center rounded-full bg-primary text-sm font-semibold text-primary-foreground">
133
+ {getInitials(p.name)}
134
+ </span>
135
+ )}
136
+ <div class="absolute bottom-1 left-1 flex items-center gap-0.5 rounded bg-black/60 px-1 py-0.5 text-[10px] text-white">
137
+ {(p.micState ?? 'on') === 'muted' && (
138
+ <span aria-label="Muted" role="img">🔇</span>
139
+ )}
140
+ <span>{p.name}</span>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ ))}
145
+ </div>
146
+ )}
147
+ </div>
148
+ ) : (
149
+ <div
150
+ class={cn(videoGridVariants({ layout: effectiveLayout }), className)}
151
+ style={gridStyle}
152
+ {...ariaProps}
153
+ {...dataAttributes}
154
+ {...rest}
155
+ >
156
+ {participants.map((p) => (
157
+ <div
158
+ {...renderTileAttrs(p)}
159
+ class="relative flex flex-col items-center justify-center bg-muted rounded-xl overflow-hidden aspect-video"
160
+ aria-label={p.name}
161
+ >
162
+ {p.avatarUrl ? (
163
+ <img
164
+ src={p.avatarUrl}
165
+ alt={p.name}
166
+ class="h-full w-full object-cover"
167
+ />
168
+ ) : (
169
+ <span class="flex h-14 w-14 items-center justify-center rounded-full bg-primary text-xl font-semibold text-primary-foreground">
170
+ {getInitials(p.name)}
171
+ </span>
172
+ )}
173
+ <div class="absolute bottom-2 left-2 flex items-center gap-1 rounded-md bg-black/60 px-2 py-0.5 text-xs text-white">
174
+ {(p.micState ?? 'on') === 'muted' && (
175
+ <span aria-label="Muted" role="img">🔇</span>
176
+ )}
177
+ <span>{p.name}</span>
178
+ </div>
179
+ </div>
180
+ ))}
181
+ </div>
182
+ )}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,104 @@
1
+ ---
2
+ import {
3
+ createVideoTile,
4
+ getInitials,
5
+ videoTileVariants,
6
+ videoTileNameChipClass,
7
+ videoTileMicIconClass,
8
+ videoTileAvatarFallbackClass,
9
+ videoTileAvatarCircleClass,
10
+ videoTileReactionBadgeClass,
11
+ type VideoTileMicState,
12
+ } from '../video-tile/index.ts'
13
+ import { cn } from '../shared/index.ts'
14
+
15
+ interface Props extends astroHTML.JSX.HTMLAttributes {
16
+ /** Participant display name — shown in the name chip and used for initials. */
17
+ name: string
18
+ /** Current microphone state. Defaults to 'on'. */
19
+ micState?: VideoTileMicState
20
+ /** Whether the participant is actively speaking. Adds an emerald ring. */
21
+ speaking?: boolean
22
+ /** Whether the tile is pinned in the grid. Adds a primary ring. */
23
+ pinned?: boolean
24
+ /** URL of the participant's avatar image, used in the fallback circle. */
25
+ avatarUrl?: string
26
+ }
27
+
28
+ const {
29
+ name,
30
+ micState = 'on',
31
+ speaking = false,
32
+ pinned = false,
33
+ avatarUrl,
34
+ class: className,
35
+ ...rest
36
+ } = Astro.props
37
+
38
+ const { ariaProps, dataAttributes } = createVideoTile({ speaking, pinned })
39
+ const initials = getInitials(name)
40
+ const isMuted = micState === 'muted'
41
+ const hasDefaultSlot = Astro.slots.has('default')
42
+ ---
43
+
44
+ <div
45
+ class={cn(
46
+ videoTileVariants({
47
+ speaking: speaking ? 'true' : 'false',
48
+ pinned: pinned ? 'true' : 'false',
49
+ }),
50
+ className,
51
+ )}
52
+ aria-label={name}
53
+ {...ariaProps}
54
+ {...dataAttributes}
55
+ {...rest}
56
+ >
57
+ {hasDefaultSlot ? (
58
+ <!-- Media slot fills the tile -->
59
+ <div class="absolute inset-0 h-full w-full">
60
+ <slot />
61
+ </div>
62
+ ) : avatarUrl ? (
63
+ <!-- Avatar image fallback -->
64
+ <div class={videoTileAvatarFallbackClass}>
65
+ <img src={avatarUrl} alt={name} class={videoTileAvatarCircleClass} />
66
+ </div>
67
+ ) : (
68
+ <!-- Initials fallback -->
69
+ <div class={videoTileAvatarFallbackClass}>
70
+ <div class={videoTileAvatarCircleClass} aria-hidden="true">{initials}</div>
71
+ </div>
72
+ )}
73
+
74
+ {/* Reaction badge (top-right) — via named slot */}
75
+ <slot name="reaction" />
76
+
77
+ <!-- Name chip (bottom-left) -->
78
+ <div class={videoTileNameChipClass}>
79
+ <span>{name}</span>
80
+ {isMuted && (
81
+ <span class={videoTileMicIconClass} aria-label="Microphone muted">
82
+ <svg
83
+ xmlns="http://www.w3.org/2000/svg"
84
+ width="12"
85
+ height="12"
86
+ viewBox="0 0 24 24"
87
+ fill="none"
88
+ stroke="currentColor"
89
+ stroke-width="2"
90
+ stroke-linecap="round"
91
+ stroke-linejoin="round"
92
+ aria-hidden="true"
93
+ >
94
+ <line x1="2" y1="2" x2="22" y2="22" />
95
+ <path d="M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" />
96
+ <path d="M5 10v2a7 7 0 0 0 12 5" />
97
+ <path d="M15 9.34V5a3 3 0 0 0-5.68-1.33" />
98
+ <path d="M9 9v3a3 3 0 0 0 5.12 2.12" />
99
+ <line x1="12" y1="19" x2="12" y2="22" />
100
+ </svg>
101
+ </span>
102
+ )}
103
+ </div>
104
+ </div>
@@ -0,0 +1 @@
1
+ export {}