@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,122 @@
|
|
|
1
|
+
/** A rectangle in 2-D space. */
|
|
2
|
+
export interface Rect {
|
|
3
|
+
x: number
|
|
4
|
+
y: number
|
|
5
|
+
width: number
|
|
6
|
+
height: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** A single item to be represented as a dot on the minimap. */
|
|
10
|
+
export interface MiniMapItem {
|
|
11
|
+
id: string
|
|
12
|
+
x: number
|
|
13
|
+
y: number
|
|
14
|
+
width?: number
|
|
15
|
+
height?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MiniMapAPI {
|
|
19
|
+
/** ARIA attributes to spread on the minimap container element. */
|
|
20
|
+
ariaProps: { role: 'img'; 'aria-label': string }
|
|
21
|
+
/** Data attributes for styling hooks. */
|
|
22
|
+
dataAttributes: Record<string, string>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Compute the bounding box that encloses all items (using their x/y + optional
|
|
27
|
+
* width/height). Returns a zero-size rect at the origin when `items` is empty.
|
|
28
|
+
*/
|
|
29
|
+
export function contentBounds(items: MiniMapItem[]): Rect {
|
|
30
|
+
if (items.length === 0) return { x: 0, y: 0, width: 0, height: 0 }
|
|
31
|
+
|
|
32
|
+
let minX = Infinity
|
|
33
|
+
let minY = Infinity
|
|
34
|
+
let maxX = -Infinity
|
|
35
|
+
let maxY = -Infinity
|
|
36
|
+
|
|
37
|
+
for (const item of items) {
|
|
38
|
+
const right = item.x + (item.width ?? 0)
|
|
39
|
+
const bottom = item.y + (item.height ?? 0)
|
|
40
|
+
if (item.x < minX) minX = item.x
|
|
41
|
+
if (item.y < minY) minY = item.y
|
|
42
|
+
if (right > maxX) maxX = right
|
|
43
|
+
if (bottom > maxY) maxY = bottom
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compute the uniform scale factor that fits `contentRect` into the minimap
|
|
51
|
+
* box (after subtracting `padding` on each side). Returns 1 when the content
|
|
52
|
+
* has zero area (nothing to scale).
|
|
53
|
+
*/
|
|
54
|
+
export function miniScale(
|
|
55
|
+
contentRect: Rect,
|
|
56
|
+
miniW: number,
|
|
57
|
+
miniH: number,
|
|
58
|
+
padding = 8,
|
|
59
|
+
): number {
|
|
60
|
+
const availW = miniW - padding * 2
|
|
61
|
+
const availH = miniH - padding * 2
|
|
62
|
+
if (availW <= 0 || availH <= 0) return 1
|
|
63
|
+
if (contentRect.width === 0 || contentRect.height === 0) return 1
|
|
64
|
+
return Math.min(availW / contentRect.width, availH / contentRect.height)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Map a world-space point `{ x, y }` to minimap pixel coordinates.
|
|
69
|
+
*
|
|
70
|
+
* @param point World coordinate
|
|
71
|
+
* @param contentRect Bounding box of all content (from `contentBounds`)
|
|
72
|
+
* @param scale Scale factor (from `miniScale`)
|
|
73
|
+
* @param padding Padding inset in px (must match the value passed to `miniScale`)
|
|
74
|
+
*/
|
|
75
|
+
export function worldToMini(
|
|
76
|
+
point: { x: number; y: number },
|
|
77
|
+
contentRect: Rect,
|
|
78
|
+
scale: number,
|
|
79
|
+
padding = 8,
|
|
80
|
+
): { x: number; y: number } {
|
|
81
|
+
return {
|
|
82
|
+
x: (point.x - contentRect.x) * scale + padding,
|
|
83
|
+
y: (point.y - contentRect.y) * scale + padding,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Compute the position and size of the viewport indicator rectangle in
|
|
89
|
+
* minimap coordinates, ready to be applied as CSS `left/top/width/height`.
|
|
90
|
+
*/
|
|
91
|
+
export function viewportRectInMini(
|
|
92
|
+
viewport: Rect,
|
|
93
|
+
contentRect: Rect,
|
|
94
|
+
scale: number,
|
|
95
|
+
padding = 8,
|
|
96
|
+
): Rect {
|
|
97
|
+
const { x, y } = worldToMini(
|
|
98
|
+
{ x: viewport.x, y: viewport.y },
|
|
99
|
+
contentRect,
|
|
100
|
+
scale,
|
|
101
|
+
padding,
|
|
102
|
+
)
|
|
103
|
+
return {
|
|
104
|
+
x,
|
|
105
|
+
y,
|
|
106
|
+
width: viewport.width * scale,
|
|
107
|
+
height: viewport.height * scale,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Build the framework-agnostic ARIA props for the minimap container.
|
|
113
|
+
*
|
|
114
|
+
* Every adapter spreads `ariaProps` onto its outermost element; `dataAttributes`
|
|
115
|
+
* are available for CSS hooks.
|
|
116
|
+
*/
|
|
117
|
+
export function createMiniMap(): MiniMapAPI {
|
|
118
|
+
return {
|
|
119
|
+
ariaProps: { role: 'img', 'aria-label': 'Minimap' },
|
|
120
|
+
dataAttributes: { 'data-component': 'mini-map' },
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createNumberedSteps,
|
|
3
|
+
padOrdinal,
|
|
4
|
+
stepColumns,
|
|
5
|
+
type NumberedStepItem,
|
|
6
|
+
type NumberedStepsProps,
|
|
7
|
+
type NumberedStepsAPI,
|
|
8
|
+
} from './numbered-steps.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
numberedStepsVariants,
|
|
12
|
+
numberedStepsItemClass,
|
|
13
|
+
numberedStepsOrdinalClass,
|
|
14
|
+
numberedStepsTitleClass,
|
|
15
|
+
numberedStepsBodyClass,
|
|
16
|
+
} from './numbered-steps.styles.js'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Container grid for the numbered steps. The `columns` variant maps to a
|
|
5
|
+
* fixed Tailwind grid-cols class; adapters that need a dynamic column count
|
|
6
|
+
* use `gridTemplateColumns` inline style via {@link stepColumns} instead and
|
|
7
|
+
* omit this variant.
|
|
8
|
+
*/
|
|
9
|
+
export const numberedStepsVariants = cva({
|
|
10
|
+
base: 'grid gap-6',
|
|
11
|
+
variants: {
|
|
12
|
+
columns: {
|
|
13
|
+
'2': 'grid-cols-2',
|
|
14
|
+
'3': 'grid-cols-3',
|
|
15
|
+
'4': 'grid-cols-4',
|
|
16
|
+
'5': 'grid-cols-5',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
columns: '3',
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
/** Individual step card. */
|
|
25
|
+
export const numberedStepsItemClass =
|
|
26
|
+
'rounded-xl border border-border bg-card p-5'
|
|
27
|
+
|
|
28
|
+
/** Zero-padded ordinal badge above each step title. */
|
|
29
|
+
export const numberedStepsOrdinalClass = 'font-mono text-sm text-primary'
|
|
30
|
+
|
|
31
|
+
/** Step title. */
|
|
32
|
+
export const numberedStepsTitleClass = 'mt-2 text-sm font-bold text-foreground'
|
|
33
|
+
|
|
34
|
+
/** Step body copy. */
|
|
35
|
+
export const numberedStepsBodyClass =
|
|
36
|
+
'mt-1 text-xs text-muted-foreground leading-relaxed'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** A single step item in the numbered steps grid. */
|
|
2
|
+
export type NumberedStepItem = {
|
|
3
|
+
/** Step title displayed prominently below the ordinal badge. */
|
|
4
|
+
title: string
|
|
5
|
+
/** Step body copy displayed below the title. */
|
|
6
|
+
body: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface NumberedStepsProps {
|
|
10
|
+
/** The step items to display. */
|
|
11
|
+
items: NumberedStepItem[]
|
|
12
|
+
/**
|
|
13
|
+
* Number of grid columns. When omitted, defaults to clamping the item count
|
|
14
|
+
* to the 2–5 range via {@link stepColumns}.
|
|
15
|
+
*/
|
|
16
|
+
columns?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface NumberedStepsAPI {
|
|
20
|
+
/** ARIA attributes to spread on the container element (`role="list"`). */
|
|
21
|
+
ariaProps: { role: 'list' }
|
|
22
|
+
/** Data attributes for styling hooks. */
|
|
23
|
+
dataAttributes: Record<string, string>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Zero-pad a 1-indexed ordinal to at least two digits.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* padOrdinal(1) // '01'
|
|
31
|
+
* padOrdinal(10) // '10'
|
|
32
|
+
*/
|
|
33
|
+
export function padOrdinal(n: number): string {
|
|
34
|
+
return String(n).padStart(2, '0')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Derive the number of grid columns from the item count.
|
|
39
|
+
* Clamps the count to the 2–5 range.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* stepColumns(3) // 3
|
|
43
|
+
* stepColumns(1) // 2
|
|
44
|
+
* stepColumns(6) // 5
|
|
45
|
+
*/
|
|
46
|
+
export function stepColumns(count: number): number {
|
|
47
|
+
return Math.min(5, Math.max(2, count))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build the framework-agnostic container props for a numbered-steps grid.
|
|
52
|
+
*
|
|
53
|
+
* Returns `role="list"` plus data attributes; adapters spread these onto their
|
|
54
|
+
* container and render each step as a `role="listitem"`.
|
|
55
|
+
*/
|
|
56
|
+
export function createNumberedSteps(): NumberedStepsAPI {
|
|
57
|
+
const ariaProps = { role: 'list' } as const
|
|
58
|
+
|
|
59
|
+
const dataAttributes: Record<string, string> = {
|
|
60
|
+
'data-component': 'numbered-steps',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { ariaProps, dataAttributes }
|
|
64
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createPreCallLobby,
|
|
3
|
+
micLevelToBars,
|
|
4
|
+
type MediaDeviceOption,
|
|
5
|
+
type DeviceKind,
|
|
6
|
+
type PreCallLobbyProps,
|
|
7
|
+
type PreCallLobbyAPI,
|
|
8
|
+
} from './pre-call-lobby.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
preCallLobbyVariants,
|
|
12
|
+
preCallCameraVariants,
|
|
13
|
+
preCallCameraPlaceholderClass,
|
|
14
|
+
preCallDeviceRowClass,
|
|
15
|
+
preCallDeviceLabelClass,
|
|
16
|
+
preCallDeviceSelectClass,
|
|
17
|
+
preCallMicMeterClass,
|
|
18
|
+
preCallMicBarVariants,
|
|
19
|
+
preCallControlsRowClass,
|
|
20
|
+
preCallToggleVariants,
|
|
21
|
+
preCallJoinButtonClass,
|
|
22
|
+
} from './pre-call-lobby.styles.js'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Outer lobby card — a centered panel that holds all preflight controls.
|
|
5
|
+
*/
|
|
6
|
+
export const preCallLobbyVariants = cva({
|
|
7
|
+
base: [
|
|
8
|
+
'flex flex-col gap-6 rounded-2xl border border-border bg-card p-6 shadow-sm',
|
|
9
|
+
'w-full max-w-lg mx-auto',
|
|
10
|
+
].join(' '),
|
|
11
|
+
variants: {},
|
|
12
|
+
defaultVariants: {},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Camera preview area. The `camera` variant switches between the live preview
|
|
17
|
+
* slot and an avatar / placeholder shown when the camera is off.
|
|
18
|
+
*/
|
|
19
|
+
export const preCallCameraVariants = cva({
|
|
20
|
+
base: [
|
|
21
|
+
'relative flex items-center justify-center overflow-hidden rounded-xl',
|
|
22
|
+
'aspect-video w-full bg-muted',
|
|
23
|
+
].join(' '),
|
|
24
|
+
variants: {
|
|
25
|
+
camera: {
|
|
26
|
+
on: '',
|
|
27
|
+
off: 'bg-muted',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
camera: 'on',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
/** Placeholder shown inside the camera area when the camera is off. */
|
|
36
|
+
export const preCallCameraPlaceholderClass = [
|
|
37
|
+
'flex flex-col items-center justify-center gap-2',
|
|
38
|
+
'text-muted-foreground select-none',
|
|
39
|
+
].join(' ')
|
|
40
|
+
|
|
41
|
+
/** Row that holds the label and the <select> for a device picker. */
|
|
42
|
+
export const preCallDeviceRowClass = 'flex flex-col gap-1'
|
|
43
|
+
|
|
44
|
+
/** Label text above a device picker. */
|
|
45
|
+
export const preCallDeviceLabelClass = 'text-xs font-medium text-muted-foreground'
|
|
46
|
+
|
|
47
|
+
/** The <select> element used for device selection. */
|
|
48
|
+
export const preCallDeviceSelectClass = [
|
|
49
|
+
'flex h-9 w-full rounded-md border border-input bg-background px-3 py-1',
|
|
50
|
+
'text-sm text-foreground shadow-sm',
|
|
51
|
+
'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
52
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
53
|
+
].join(' ')
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Mic level meter container. Renders a row of bars.
|
|
57
|
+
*/
|
|
58
|
+
export const preCallMicMeterClass = 'flex items-end gap-0.5 h-5'
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* An individual bar in the mic level meter.
|
|
62
|
+
* The `lit` variant distinguishes active (lit) from inactive bars.
|
|
63
|
+
*/
|
|
64
|
+
export const preCallMicBarVariants = cva({
|
|
65
|
+
base: 'w-1.5 rounded-sm transition-colors',
|
|
66
|
+
variants: {
|
|
67
|
+
lit: {
|
|
68
|
+
true: 'bg-primary',
|
|
69
|
+
false: 'bg-muted-foreground/30',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
defaultVariants: {
|
|
73
|
+
lit: 'false',
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
/** Row holding the camera-toggle and mic-toggle buttons. */
|
|
78
|
+
export const preCallControlsRowClass = 'flex items-center gap-3'
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Toggle button for camera or microphone.
|
|
82
|
+
* The `active` variant styles the enabled vs disabled states.
|
|
83
|
+
*/
|
|
84
|
+
export const preCallToggleVariants = cva({
|
|
85
|
+
base: [
|
|
86
|
+
'inline-flex items-center justify-center rounded-full p-2.5',
|
|
87
|
+
'transition-colors focus-visible:outline-none focus-visible:ring-2',
|
|
88
|
+
'focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
89
|
+
].join(' '),
|
|
90
|
+
variants: {
|
|
91
|
+
active: {
|
|
92
|
+
true: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
93
|
+
false: 'bg-destructive/10 text-destructive hover:bg-destructive/20',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
defaultVariants: {
|
|
97
|
+
active: 'false',
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
/** Primary "Join" button. */
|
|
102
|
+
export const preCallJoinButtonClass = [
|
|
103
|
+
'inline-flex w-full items-center justify-center rounded-lg px-4 py-2.5',
|
|
104
|
+
'bg-primary text-primary-foreground text-sm font-semibold',
|
|
105
|
+
'shadow transition-colors hover:bg-primary/90',
|
|
106
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
107
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
108
|
+
].join(' ')
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** A single selectable media device (camera, microphone, or speaker). */
|
|
2
|
+
export interface MediaDeviceOption {
|
|
3
|
+
id: string
|
|
4
|
+
label: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** The kind of media device. */
|
|
8
|
+
export type DeviceKind = 'camera' | 'microphone' | 'speaker'
|
|
9
|
+
|
|
10
|
+
export interface PreCallLobbyProps {
|
|
11
|
+
/** Whether the camera is currently enabled. */
|
|
12
|
+
cameraOn: boolean
|
|
13
|
+
/** Whether the microphone is currently enabled. */
|
|
14
|
+
micOn: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PreCallLobbyAPI {
|
|
18
|
+
/** ARIA attributes to spread on the group element. */
|
|
19
|
+
ariaProps: { role: 'group'; 'aria-label': string }
|
|
20
|
+
/** Data attributes for styling hooks. */
|
|
21
|
+
dataAttributes: { 'data-camera': string; 'data-mic': string }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build the framework-agnostic group props for a pre-call lobby / device-check panel.
|
|
26
|
+
*
|
|
27
|
+
* Returns `role="group"` plus data attributes reflecting camera and mic state.
|
|
28
|
+
* Adapters spread these onto their container and render the device pickers,
|
|
29
|
+
* preview, and join button themselves.
|
|
30
|
+
*/
|
|
31
|
+
export function createPreCallLobby(props: PreCallLobbyProps): PreCallLobbyAPI {
|
|
32
|
+
const { cameraOn, micOn } = props
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
ariaProps: {
|
|
36
|
+
role: 'group',
|
|
37
|
+
'aria-label': 'Device setup',
|
|
38
|
+
},
|
|
39
|
+
dataAttributes: {
|
|
40
|
+
'data-camera': cameraOn ? 'on' : 'off',
|
|
41
|
+
'data-mic': micOn ? 'on' : 'off',
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convert a microphone level (0..1) to a count of lit bars.
|
|
48
|
+
*
|
|
49
|
+
* Pure function — no side effects, no JSX. Adapters use this to render
|
|
50
|
+
* the mic level meter by iterating `0..barCount` and lighting bars up to
|
|
51
|
+
* the returned value.
|
|
52
|
+
*
|
|
53
|
+
* @param level Current mic level in the range [0, 1].
|
|
54
|
+
* @param barCount Total number of bars in the meter.
|
|
55
|
+
* @returns Number of bars that should appear "lit" (0..barCount).
|
|
56
|
+
*/
|
|
57
|
+
export function micLevelToBars(level: number, barCount: number): number {
|
|
58
|
+
if (barCount <= 0) return 0
|
|
59
|
+
const clamped = Math.min(1, Math.max(0, level))
|
|
60
|
+
return Math.round(clamped * barCount)
|
|
61
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createPricingCard,
|
|
3
|
+
type PricingCtaVariant,
|
|
4
|
+
type PricingCardOptions,
|
|
5
|
+
type PricingCardAPI,
|
|
6
|
+
} from './pricing-card.js'
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
pricingCardVariants,
|
|
10
|
+
pricingCardBadgeClass,
|
|
11
|
+
pricingCardNameClass,
|
|
12
|
+
pricingCardPriceClass,
|
|
13
|
+
pricingCardPeriodClass,
|
|
14
|
+
pricingCardDescriptionClass,
|
|
15
|
+
pricingCardFeatureListClass,
|
|
16
|
+
pricingCardFeatureRowClass,
|
|
17
|
+
pricingCardFeatureTickClass,
|
|
18
|
+
pricingCtaVariants,
|
|
19
|
+
} from './pricing-card.styles.js'
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Root pricing card container. The `featured` variant adds a primary-coloured
|
|
5
|
+
* ring to draw the eye to the recommended plan.
|
|
6
|
+
*/
|
|
7
|
+
export const pricingCardVariants = cva({
|
|
8
|
+
base: [
|
|
9
|
+
'relative flex flex-col rounded-2xl border border-border bg-card p-6',
|
|
10
|
+
'transition-shadow',
|
|
11
|
+
].join(' '),
|
|
12
|
+
variants: {
|
|
13
|
+
featured: {
|
|
14
|
+
true: 'ring-1 ring-primary',
|
|
15
|
+
false: '',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
featured: 'false',
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/** "Best value" / "Most popular" badge above the plan name. */
|
|
24
|
+
export const pricingCardBadgeClass =
|
|
25
|
+
'mb-3 inline-flex items-center rounded-full bg-primary/10 px-2.5 py-0.5 text-xs font-medium text-primary'
|
|
26
|
+
|
|
27
|
+
/** Plan name (e.g. "Pro", "Starter"). */
|
|
28
|
+
export const pricingCardNameClass = 'text-lg font-bold text-foreground'
|
|
29
|
+
|
|
30
|
+
/** Main price figure (e.g. "$29", "Free"). */
|
|
31
|
+
export const pricingCardPriceClass = 'text-3xl font-bold text-foreground'
|
|
32
|
+
|
|
33
|
+
/** Billing period or qualifier (e.g. "/ month", "forever"). */
|
|
34
|
+
export const pricingCardPeriodClass = 'text-sm text-muted-foreground'
|
|
35
|
+
|
|
36
|
+
/** Short marketing description beneath the price. */
|
|
37
|
+
export const pricingCardDescriptionClass = 'text-sm text-muted-foreground'
|
|
38
|
+
|
|
39
|
+
/** Unordered list of included features. */
|
|
40
|
+
export const pricingCardFeatureListClass = 'flex flex-col gap-2'
|
|
41
|
+
|
|
42
|
+
/** A single feature row — tick icon + text. */
|
|
43
|
+
export const pricingCardFeatureRowClass = 'flex items-start gap-2 text-sm text-foreground'
|
|
44
|
+
|
|
45
|
+
/** Inline check SVG rendered before each feature string. */
|
|
46
|
+
export const pricingCardFeatureTickClass = 'mt-0.5 size-4 shrink-0 text-primary'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* CTA button/link that appears at the bottom of the card.
|
|
50
|
+
*
|
|
51
|
+
* `default` — filled primary style (call to action on featured plans).
|
|
52
|
+
* `outline` — bordered secondary style (for non-featured plans).
|
|
53
|
+
*/
|
|
54
|
+
export const pricingCtaVariants = cva({
|
|
55
|
+
base: [
|
|
56
|
+
'mt-auto inline-flex w-full items-center justify-center rounded-lg px-4 py-2',
|
|
57
|
+
'text-sm font-medium transition-colors',
|
|
58
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
59
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
60
|
+
].join(' '),
|
|
61
|
+
variants: {
|
|
62
|
+
variant: {
|
|
63
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
64
|
+
outline:
|
|
65
|
+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
defaultVariants: {
|
|
69
|
+
variant: 'default',
|
|
70
|
+
},
|
|
71
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** CTA button/link visual variant. */
|
|
4
|
+
export type PricingCtaVariant = 'default' | 'outline'
|
|
5
|
+
|
|
6
|
+
export interface PricingCardOptions {
|
|
7
|
+
/** Whether this card is highlighted as the featured / recommended plan. */
|
|
8
|
+
featured?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PricingCardAPI {
|
|
12
|
+
/** ARIA props to spread on the card container. */
|
|
13
|
+
ariaProps: Partial<AccessibilityProps>
|
|
14
|
+
/** Data attributes for styling hooks and CSS selectors. */
|
|
15
|
+
dataAttributes: Record<string, string>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build the framework-agnostic props for a pricing plan card.
|
|
20
|
+
*
|
|
21
|
+
* Returns `role="group"` (semantically groups the plan's content without
|
|
22
|
+
* imposing listitem semantics on a standalone card) and a `data-featured`
|
|
23
|
+
* attribute that adapters can use for conditional ring/highlight styling.
|
|
24
|
+
*/
|
|
25
|
+
export function createPricingCard(options: PricingCardOptions = {}): PricingCardAPI {
|
|
26
|
+
const { featured = false } = options
|
|
27
|
+
|
|
28
|
+
const ariaProps: Partial<AccessibilityProps> = {
|
|
29
|
+
role: 'group',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const dataAttributes: Record<string, string> = {
|
|
33
|
+
'data-featured': String(featured),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { ariaProps, dataAttributes }
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export {
|
|
2
|
+
clampValue,
|
|
3
|
+
valueToFraction,
|
|
4
|
+
polarToCartesian,
|
|
5
|
+
describeArc,
|
|
6
|
+
gaugeStrokeDashoffset,
|
|
7
|
+
resolveZoneTone,
|
|
8
|
+
createRadialGauge,
|
|
9
|
+
GAUGE_SIZE_PX,
|
|
10
|
+
type GaugeSize,
|
|
11
|
+
type GaugeTone,
|
|
12
|
+
type GaugeZone,
|
|
13
|
+
type RadialGaugeProps,
|
|
14
|
+
type RadialGaugeAPI,
|
|
15
|
+
} from './radial-gauge.js'
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
radialGaugeRootVariants,
|
|
19
|
+
gaugeTrackClass,
|
|
20
|
+
gaugeArcClass,
|
|
21
|
+
gaugeArcToneVariants,
|
|
22
|
+
gaugeLabelClass,
|
|
23
|
+
gaugeSublabelClass,
|
|
24
|
+
GAUGE_LABEL_FONT_SIZE,
|
|
25
|
+
GAUGE_SUBLABEL_FONT_SIZE,
|
|
26
|
+
} from './radial-gauge.styles.js'
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Root SVG element variants — controls the rendered size via an inline
|
|
5
|
+
* width/height (adapters use GAUGE_SIZE_PX) and sets up the color context.
|
|
6
|
+
*/
|
|
7
|
+
export const radialGaugeRootVariants = cva({
|
|
8
|
+
base: 'inline-block shrink-0',
|
|
9
|
+
variants: {
|
|
10
|
+
size: {
|
|
11
|
+
sm: '',
|
|
12
|
+
md: '',
|
|
13
|
+
lg: '',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
size: 'md',
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Track (background) circle stroke class.
|
|
23
|
+
* Uses a muted foreground so it sits behind the value arc without competing.
|
|
24
|
+
*/
|
|
25
|
+
export const gaugeTrackClass = 'fill-none stroke-muted-foreground/20'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Value arc stroke class — resolved per tone via {@link gaugeArcToneVariants}.
|
|
29
|
+
* `currentColor` stroke picks up the Tailwind text-* class applied by the
|
|
30
|
+
* tone variant so we only need one class-generating helper.
|
|
31
|
+
*/
|
|
32
|
+
export const gaugeArcClass = 'fill-none stroke-current transition-[stroke-dashoffset] duration-300 ease-out'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Tone variants for the value arc. Adapters combine this with `gaugeArcClass`.
|
|
36
|
+
*/
|
|
37
|
+
export const gaugeArcToneVariants = cva({
|
|
38
|
+
base: '',
|
|
39
|
+
variants: {
|
|
40
|
+
tone: {
|
|
41
|
+
default: 'text-foreground',
|
|
42
|
+
success: 'text-emerald-500',
|
|
43
|
+
warning: 'text-amber-500',
|
|
44
|
+
danger: 'text-destructive',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
defaultVariants: {
|
|
48
|
+
tone: 'default',
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
/** Center label (primary text) classes. */
|
|
53
|
+
export const gaugeLabelClass = 'fill-foreground text-center font-semibold'
|
|
54
|
+
|
|
55
|
+
/** Center sub-label (secondary text) classes. */
|
|
56
|
+
export const gaugeSublabelClass = 'fill-muted-foreground'
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Font size for the primary label by size variant.
|
|
60
|
+
* Expressed as SVG `font-size` values (px) for use in the `<text>` element.
|
|
61
|
+
*/
|
|
62
|
+
export const GAUGE_LABEL_FONT_SIZE: Record<'sm' | 'md' | 'lg', number> = {
|
|
63
|
+
sm: 14,
|
|
64
|
+
md: 20,
|
|
65
|
+
lg: 26,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Font size for the sub-label by size variant.
|
|
70
|
+
*/
|
|
71
|
+
export const GAUGE_SUBLABEL_FONT_SIZE: Record<'sm' | 'md' | 'lg', number> = {
|
|
72
|
+
sm: 9,
|
|
73
|
+
md: 11,
|
|
74
|
+
lg: 13,
|
|
75
|
+
}
|