@refraction-ui/astro 0.12.0 → 0.14.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-audience-feature-card/AudienceFeatureCard.astro +53 -0
- package/dist/astro-audience-feature-card/index.ts +1 -0
- package/dist/astro-audio-room/AudioRoom.astro +81 -0
- package/dist/astro-audio-room/index.ts +1 -0
- package/dist/astro-brand-network-cell/BrandNetworkCell.astro +77 -0
- package/dist/astro-brand-network-cell/index.ts +1 -0
- package/dist/astro-browser-chrome-mock/BrowserChromeMock.astro +62 -0
- package/dist/astro-browser-chrome-mock/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-marquee-strip/MarqueeStrip.astro +63 -0
- package/dist/astro-marquee-strip/index.ts +1 -0
- package/dist/astro-mastery-bar/MasteryBar.astro +56 -0
- package/dist/astro-mastery-bar/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-numbered-steps/NumberedSteps.astro +47 -0
- package/dist/astro-numbered-steps/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-pricing-card/PricingCard.astro +114 -0
- package/dist/astro-pricing-card/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-section-head/SectionHead.astro +44 -0
- package/dist/astro-section-head/index.ts +1 -0
- package/dist/astro-stat-grid/StatGrid.astro +46 -0
- package/dist/astro-stat-grid/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/audience-feature-card/audience-feature-card.styles.ts +23 -0
- package/dist/audience-feature-card/audience-feature-card.ts +39 -0
- package/dist/audience-feature-card/index.ts +13 -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/brand-network-cell/brand-network-cell.styles.ts +46 -0
- package/dist/brand-network-cell/brand-network-cell.ts +35 -0
- package/dist/brand-network-cell/index.ts +14 -0
- package/dist/browser-chrome-mock/browser-chrome-mock.styles.ts +43 -0
- package/dist/browser-chrome-mock/browser-chrome-mock.ts +52 -0
- package/dist/browser-chrome-mock/index.ts +17 -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 +32 -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/marquee-strip/index.ts +14 -0
- package/dist/marquee-strip/marquee-strip.styles.ts +61 -0
- package/dist/marquee-strip/marquee-strip.ts +35 -0
- package/dist/mastery-bar/index.ts +15 -0
- package/dist/mastery-bar/mastery-bar.styles.ts +45 -0
- package/dist/mastery-bar/mastery-bar.ts +55 -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/numbered-steps/index.ts +16 -0
- package/dist/numbered-steps/numbered-steps.styles.ts +36 -0
- package/dist/numbered-steps/numbered-steps.ts +64 -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/pricing-card/index.ts +19 -0
- package/dist/pricing-card/pricing-card.styles.ts +71 -0
- package/dist/pricing-card/pricing-card.ts +37 -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/section-head/index.ts +13 -0
- package/dist/section-head/section-head.styles.ts +30 -0
- package/dist/section-head/section-head.ts +28 -0
- package/dist/stat-grid/index.ts +13 -0
- package/dist/stat-grid/stat-grid.styles.ts +25 -0
- package/dist/stat-grid/stat-grid.ts +41 -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 +33 -1
|
@@ -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 {}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createWizard,
|
|
4
|
+
getStepState,
|
|
5
|
+
wizardVariants,
|
|
6
|
+
wizardRailVariants,
|
|
7
|
+
wizardStepItemVariants,
|
|
8
|
+
wizardStepIndicatorVariants,
|
|
9
|
+
wizardStepLabelVariants,
|
|
10
|
+
wizardStepOptionalClass,
|
|
11
|
+
wizardConnectorVariants,
|
|
12
|
+
wizardContentClass,
|
|
13
|
+
wizardFooterClass,
|
|
14
|
+
wizardPrimaryButtonVariants,
|
|
15
|
+
wizardSecondaryButtonVariants,
|
|
16
|
+
type WizardStep,
|
|
17
|
+
type WizardOrientation,
|
|
18
|
+
} from '../wizard/index.ts'
|
|
19
|
+
import { cn } from '../shared/index.ts'
|
|
20
|
+
|
|
21
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
22
|
+
/** Step definitions for the rail. */
|
|
23
|
+
steps: WizardStep[]
|
|
24
|
+
/** Current step index (0-based). */
|
|
25
|
+
currentStep?: number
|
|
26
|
+
/** Orientation of the step rail. */
|
|
27
|
+
orientation?: WizardOrientation
|
|
28
|
+
/** Label for the Next button. */
|
|
29
|
+
nextLabel?: string
|
|
30
|
+
/** Label for the Back button. */
|
|
31
|
+
backLabel?: string
|
|
32
|
+
/** Label for the button on the final step. */
|
|
33
|
+
completeLabel?: string
|
|
34
|
+
/** Label for the Skip button (shown on optional steps only). */
|
|
35
|
+
skipLabel?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
steps,
|
|
40
|
+
currentStep = 0,
|
|
41
|
+
orientation = 'vertical',
|
|
42
|
+
nextLabel = 'Next',
|
|
43
|
+
backLabel = 'Back',
|
|
44
|
+
completeLabel = 'Complete',
|
|
45
|
+
skipLabel = 'Skip',
|
|
46
|
+
class: className,
|
|
47
|
+
...rest
|
|
48
|
+
} = Astro.props
|
|
49
|
+
|
|
50
|
+
const stepStates = getStepState(steps, currentStep)
|
|
51
|
+
const isLastStep = currentStep === steps.length - 1
|
|
52
|
+
const isOptional = steps[currentStep]?.optional ?? false
|
|
53
|
+
const { ariaProps, dataAttributes } = createWizard({
|
|
54
|
+
currentIndex: currentStep,
|
|
55
|
+
count: steps.length,
|
|
56
|
+
orientation,
|
|
57
|
+
})
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
<div class={cn(wizardVariants({ orientation }), className)} {...ariaProps} {...dataAttributes} {...rest}>
|
|
61
|
+
<!-- Step Rail -->
|
|
62
|
+
<nav aria-label="Progress" class={wizardRailVariants({ orientation })}>
|
|
63
|
+
{
|
|
64
|
+
stepStates.map((state, i) => {
|
|
65
|
+
const isLast = i === stepStates.length - 1
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<div class={wizardStepItemVariants({ orientation, status: state.status })}>
|
|
69
|
+
<span
|
|
70
|
+
aria-current={state.status === 'current' ? 'step' : undefined}
|
|
71
|
+
class={wizardStepIndicatorVariants({ status: state.status })}
|
|
72
|
+
>
|
|
73
|
+
{state.status === 'complete' ? (
|
|
74
|
+
<svg
|
|
75
|
+
aria-hidden="true"
|
|
76
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
77
|
+
viewBox="0 0 24 24"
|
|
78
|
+
fill="none"
|
|
79
|
+
stroke="currentColor"
|
|
80
|
+
stroke-width="2.5"
|
|
81
|
+
stroke-linecap="round"
|
|
82
|
+
stroke-linejoin="round"
|
|
83
|
+
class="size-4"
|
|
84
|
+
>
|
|
85
|
+
<polyline points="20 6 9 17 4 12" />
|
|
86
|
+
</svg>
|
|
87
|
+
) : (
|
|
88
|
+
<span>{state.index + 1}</span>
|
|
89
|
+
)}
|
|
90
|
+
</span>
|
|
91
|
+
<div class="flex flex-col">
|
|
92
|
+
<span class={wizardStepLabelVariants({ status: state.status })}>
|
|
93
|
+
{state.step.label}
|
|
94
|
+
</span>
|
|
95
|
+
{state.step.optional && (
|
|
96
|
+
<span class={wizardStepOptionalClass}>(Optional)</span>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
{!isLast && (
|
|
101
|
+
<div
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
class={wizardConnectorVariants({
|
|
104
|
+
orientation,
|
|
105
|
+
complete: state.status === 'complete' ? 'true' : 'false',
|
|
106
|
+
})}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
109
|
+
</>
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
</nav>
|
|
114
|
+
|
|
115
|
+
<!-- Content + Footer -->
|
|
116
|
+
<div class={wizardContentClass}>
|
|
117
|
+
<div class="flex-1">
|
|
118
|
+
<slot />
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class={wizardFooterClass}>
|
|
122
|
+
<button type="button" disabled={currentStep === 0} class={wizardSecondaryButtonVariants()}>
|
|
123
|
+
{backLabel}
|
|
124
|
+
</button>
|
|
125
|
+
|
|
126
|
+
{isOptional && (
|
|
127
|
+
<button type="button" class={wizardSecondaryButtonVariants()}>
|
|
128
|
+
{skipLabel}
|
|
129
|
+
</button>
|
|
130
|
+
)}
|
|
131
|
+
|
|
132
|
+
<button type="button" class={wizardPrimaryButtonVariants()}>
|
|
133
|
+
{isLastStep ? completeLabel : nextLabel}
|
|
134
|
+
</button>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** Container — full-height flex column so footers align across a row. */
|
|
4
|
+
export const audienceFeatureCardVariants = cva({
|
|
5
|
+
base: 'flex h-full flex-col rounded-xl border border-border bg-card p-5',
|
|
6
|
+
variants: {},
|
|
7
|
+
defaultVariants: {},
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
/** Kicker text above the title. */
|
|
11
|
+
export const audienceFeatureCardKickerClass =
|
|
12
|
+
'text-[10px] uppercase tracking-widest text-primary font-semibold'
|
|
13
|
+
|
|
14
|
+
/** Main title. */
|
|
15
|
+
export const audienceFeatureCardTitleClass =
|
|
16
|
+
'mt-2 text-lg font-bold text-foreground'
|
|
17
|
+
|
|
18
|
+
/** Body copy. */
|
|
19
|
+
export const audienceFeatureCardBodyClass =
|
|
20
|
+
'mt-1 text-sm text-muted-foreground leading-relaxed'
|
|
21
|
+
|
|
22
|
+
/** Footer area — pushed to bottom via mt-auto. */
|
|
23
|
+
export const audienceFeatureCardFooterClass = 'mt-auto pt-4 text-xs'
|