@refraction-ui/astro 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/astro-audio-room/AudioRoom.astro +81 -0
  2. package/dist/astro-audio-room/index.ts +1 -0
  3. package/dist/astro-call-controls/CallControls.astro +57 -0
  4. package/dist/astro-call-controls/index.ts +1 -0
  5. package/dist/astro-checklist/Checklist.astro +76 -0
  6. package/dist/astro-checklist/index.ts +1 -0
  7. package/dist/astro-editor-status-bar/EditorStatusBar.astro +92 -0
  8. package/dist/astro-editor-status-bar/index.ts +1 -0
  9. package/dist/astro-editor-tabs/EditorTabs.astro +74 -0
  10. package/dist/astro-editor-tabs/index.ts +1 -0
  11. package/dist/astro-floating-reactions/FloatingReactions.astro +56 -0
  12. package/dist/astro-floating-reactions/index.ts +1 -0
  13. package/dist/astro-flow-editor/FlowEditor.astro +99 -0
  14. package/dist/astro-flow-editor/index.ts +3 -0
  15. package/dist/astro-graph-view/GraphView.astro +109 -0
  16. package/dist/astro-graph-view/index.ts +1 -0
  17. package/dist/astro-infinite-canvas/InfiniteCanvas.astro +43 -0
  18. package/dist/astro-infinite-canvas/index.ts +1 -0
  19. package/dist/astro-live-captions/LiveCaptions.astro +54 -0
  20. package/dist/astro-live-captions/index.ts +1 -0
  21. package/dist/astro-live-cursors/LiveCursors.astro +55 -0
  22. package/dist/astro-live-cursors/index.ts +1 -0
  23. package/dist/astro-live-transcript/LiveTranscript.astro +55 -0
  24. package/dist/astro-live-transcript/index.ts +1 -0
  25. package/dist/astro-mini-map/MiniMap.astro +74 -0
  26. package/dist/astro-mini-map/index.ts +1 -0
  27. package/dist/astro-pre-call-lobby/PreCallLobby.astro +202 -0
  28. package/dist/astro-pre-call-lobby/index.ts +1 -0
  29. package/dist/astro-radial-gauge/RadialGauge.astro +144 -0
  30. package/dist/astro-radial-gauge/index.ts +1 -0
  31. package/dist/astro-rating-scale/RatingScale.astro +73 -0
  32. package/dist/astro-rating-scale/index.ts +1 -0
  33. package/dist/astro-sticky-note/StickyNote.astro +55 -0
  34. package/dist/astro-sticky-note/index.ts +1 -0
  35. package/dist/astro-terminal/Terminal.astro +67 -0
  36. package/dist/astro-terminal/index.ts +1 -0
  37. package/dist/astro-test-results/TestResults.astro +76 -0
  38. package/dist/astro-test-results/index.ts +1 -0
  39. package/dist/astro-timeline/Timeline.astro +59 -0
  40. package/dist/astro-timeline/index.ts +1 -0
  41. package/dist/astro-video-grid/VideoGrid.astro +182 -0
  42. package/dist/astro-video-grid/index.ts +1 -0
  43. package/dist/astro-video-tile/VideoTile.astro +104 -0
  44. package/dist/astro-video-tile/index.ts +1 -0
  45. package/dist/astro-wizard/Wizard.astro +137 -0
  46. package/dist/astro-wizard/index.ts +1 -0
  47. package/dist/audio-room/audio-room.styles.ts +88 -0
  48. package/dist/audio-room/audio-room.ts +70 -0
  49. package/dist/audio-room/index.ts +16 -0
  50. package/dist/call-controls/call-controls.styles.ts +63 -0
  51. package/dist/call-controls/call-controls.ts +79 -0
  52. package/dist/call-controls/index.ts +15 -0
  53. package/dist/checklist/checklist.styles.ts +76 -0
  54. package/dist/checklist/checklist.ts +62 -0
  55. package/dist/checklist/index.ts +18 -0
  56. package/dist/editor-status-bar/editor-status-bar.styles.ts +40 -0
  57. package/dist/editor-status-bar/editor-status-bar.ts +78 -0
  58. package/dist/editor-status-bar/index.ts +15 -0
  59. package/dist/editor-tabs/editor-tabs.styles.ts +52 -0
  60. package/dist/editor-tabs/editor-tabs.ts +82 -0
  61. package/dist/editor-tabs/index.ts +15 -0
  62. package/dist/floating-reactions/floating-reactions.styles.ts +54 -0
  63. package/dist/floating-reactions/floating-reactions.ts +85 -0
  64. package/dist/floating-reactions/index.ts +14 -0
  65. package/dist/flow-editor/flow-editor.styles.ts +56 -0
  66. package/dist/flow-editor/flow-editor.ts +126 -0
  67. package/dist/flow-editor/index.ts +18 -0
  68. package/dist/graph-view/graph-view.styles.ts +66 -0
  69. package/dist/graph-view/graph-view.ts +130 -0
  70. package/dist/graph-view/index.ts +21 -0
  71. package/dist/index.ts +23 -0
  72. package/dist/infinite-canvas/index.ts +17 -0
  73. package/dist/infinite-canvas/infinite-canvas.styles.ts +49 -0
  74. package/dist/infinite-canvas/infinite-canvas.ts +97 -0
  75. package/dist/live-captions/index.ts +13 -0
  76. package/dist/live-captions/live-captions.styles.ts +45 -0
  77. package/dist/live-captions/live-captions.ts +57 -0
  78. package/dist/live-cursors/index.ts +14 -0
  79. package/dist/live-cursors/live-cursors.styles.ts +41 -0
  80. package/dist/live-cursors/live-cursors.ts +73 -0
  81. package/dist/live-transcript/index.ts +16 -0
  82. package/dist/live-transcript/live-transcript.styles.ts +48 -0
  83. package/dist/live-transcript/live-transcript.ts +82 -0
  84. package/dist/mini-map/index.ts +16 -0
  85. package/dist/mini-map/mini-map.styles.ts +32 -0
  86. package/dist/mini-map/mini-map.ts +122 -0
  87. package/dist/pre-call-lobby/index.ts +22 -0
  88. package/dist/pre-call-lobby/pre-call-lobby.styles.ts +108 -0
  89. package/dist/pre-call-lobby/pre-call-lobby.ts +61 -0
  90. package/dist/radial-gauge/index.ts +26 -0
  91. package/dist/radial-gauge/radial-gauge.styles.ts +75 -0
  92. package/dist/radial-gauge/radial-gauge.ts +183 -0
  93. package/dist/rating-scale/index.ts +16 -0
  94. package/dist/rating-scale/rating-scale.styles.ts +48 -0
  95. package/dist/rating-scale/rating-scale.ts +101 -0
  96. package/dist/sticky-note/index.ts +14 -0
  97. package/dist/sticky-note/sticky-note.styles.ts +37 -0
  98. package/dist/sticky-note/sticky-note.ts +47 -0
  99. package/dist/terminal/index.ts +13 -0
  100. package/dist/terminal/terminal.styles.ts +56 -0
  101. package/dist/terminal/terminal.ts +60 -0
  102. package/dist/test-results/index.ts +19 -0
  103. package/dist/test-results/test-results.styles.ts +93 -0
  104. package/dist/test-results/test-results.ts +83 -0
  105. package/dist/timeline/index.ts +22 -0
  106. package/dist/timeline/timeline.styles.ts +98 -0
  107. package/dist/timeline/timeline.ts +86 -0
  108. package/dist/video-grid/index.ts +16 -0
  109. package/dist/video-grid/video-grid.styles.ts +38 -0
  110. package/dist/video-grid/video-grid.ts +93 -0
  111. package/dist/video-tile/index.ts +17 -0
  112. package/dist/video-tile/video-tile.styles.ts +59 -0
  113. package/dist/video-tile/video-tile.ts +74 -0
  114. package/dist/wizard/index.ts +28 -0
  115. package/dist/wizard/wizard.styles.ts +147 -0
  116. package/dist/wizard/wizard.ts +128 -0
  117. package/package.json +24 -1
@@ -0,0 +1,43 @@
1
+ ---
2
+ import {
3
+ createInfiniteCanvas,
4
+ transformToCss,
5
+ infiniteCanvasVariants,
6
+ canvasContentClass,
7
+ } from '../infinite-canvas/index.ts'
8
+ import { cn } from '../shared/index.ts'
9
+
10
+ interface Props extends astroHTML.JSX.HTMLAttributes {
11
+ /** Zoom level (default 1). */
12
+ zoom?: number
13
+ /** Pan offset x (default 0). */
14
+ x?: number
15
+ /** Pan offset y (default 0). */
16
+ y?: number
17
+ /** Show a dot-grid background. */
18
+ showGrid?: boolean
19
+ }
20
+
21
+ const {
22
+ zoom = 1,
23
+ x = 0,
24
+ y = 0,
25
+ showGrid = false,
26
+ class: className,
27
+ ...rest
28
+ } = Astro.props
29
+
30
+ const { ariaProps, dataAttributes } = createInfiniteCanvas({ zoom })
31
+ const transform = transformToCss({ zoom, x, y })
32
+ ---
33
+
34
+ <div
35
+ class={cn(infiniteCanvasVariants({ grid: showGrid ? 'true' : 'false' }), className)}
36
+ {...ariaProps}
37
+ {...dataAttributes}
38
+ {...rest}
39
+ >
40
+ <div class={canvasContentClass} style={`transform:${transform};transform-origin:0 0`}>
41
+ <slot />
42
+ </div>
43
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,54 @@
1
+ ---
2
+ import {
3
+ visibleCues,
4
+ createLiveCaptions,
5
+ liveCaptionsVariants,
6
+ liveCaptionsCueVariants,
7
+ liveCaptionsSpeakerClass,
8
+ type CaptionCue,
9
+ } from '../live-captions/index.ts'
10
+ import { cn } from '../shared/index.ts'
11
+
12
+ interface Props extends astroHTML.JSX.HTMLAttributes {
13
+ /** The full ordered list of caption cues. */
14
+ cues: CaptionCue[]
15
+ /**
16
+ * Maximum number of cue lines to display at once.
17
+ * @default 2
18
+ */
19
+ maxLines?: number
20
+ /**
21
+ * How the container is positioned in the layout.
22
+ * @default 'static'
23
+ */
24
+ position?: 'static' | 'absolute'
25
+ }
26
+
27
+ const {
28
+ cues,
29
+ maxLines = 2,
30
+ position = 'static',
31
+ class: className,
32
+ ...rest
33
+ } = Astro.props
34
+
35
+ const shown = visibleCues(cues, maxLines)
36
+ const { ariaProps, dataAttributes } = createLiveCaptions()
37
+ ---
38
+
39
+ <div class={cn(liveCaptionsVariants({ position }), className)} {...ariaProps} {...dataAttributes} {...rest}>
40
+ {
41
+ shown.map((cue) => (
42
+ <p class={liveCaptionsCueVariants({ interim: cue.final === false ? 'true' : 'false' })}>
43
+ {cue.speaker ? (
44
+ <>
45
+ <span class={liveCaptionsSpeakerClass}>{cue.speaker}</span>
46
+ {`: ${cue.text}`}
47
+ </>
48
+ ) : (
49
+ cue.text
50
+ )}
51
+ </p>
52
+ ))
53
+ }
54
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,55 @@
1
+ ---
2
+ import {
3
+ createLiveCursors,
4
+ assignCursorColor,
5
+ liveCursorsVariants,
6
+ cursorWrapperClass,
7
+ cursorArrowClass,
8
+ cursorLabelClass,
9
+ type CursorData,
10
+ } from '../live-cursors/index.ts'
11
+ import { cn } from '../shared/index.ts'
12
+
13
+ interface Props extends astroHTML.JSX.HTMLAttributes {
14
+ /** Array of collaborator cursor data to render. */
15
+ cursors: CursorData[]
16
+ }
17
+
18
+ const { cursors, class: className, ...rest } = Astro.props
19
+
20
+ const { ariaProps, dataAttributes } = createLiveCursors()
21
+ ---
22
+
23
+ <div class={cn(liveCursorsVariants(), className)} {...ariaProps} {...dataAttributes} {...rest}>
24
+ {
25
+ cursors.map((cursor, index) => {
26
+ const color = cursor.color ?? assignCursorColor(cursor.id, index)
27
+ return (
28
+ <div
29
+ class={cursorWrapperClass}
30
+ style={`left:${cursor.x}px;top:${cursor.y}px`}
31
+ data-cursor-name={cursor.name}
32
+ >
33
+ <svg
34
+ class={cursorArrowClass}
35
+ viewBox="0 0 20 20"
36
+ fill="none"
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ aria-hidden="true"
39
+ style={`fill:${color}`}
40
+ >
41
+ <path
42
+ d="M3 2L17 9.5L10.5 11.5L8 18L3 2Z"
43
+ stroke="white"
44
+ stroke-width="1.5"
45
+ stroke-linejoin="round"
46
+ />
47
+ </svg>
48
+ <span class={cursorLabelClass} style={`background-color:${color}`}>
49
+ {cursor.name}
50
+ </span>
51
+ </div>
52
+ )
53
+ })
54
+ }
55
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,55 @@
1
+ ---
2
+ import {
3
+ createLiveTranscript,
4
+ groupConsecutiveBySpeaker,
5
+ liveTranscriptVariants,
6
+ transcriptBlockVariants,
7
+ transcriptSpeakerRowClass,
8
+ transcriptSpeakerNameClass,
9
+ transcriptTimestampClass,
10
+ transcriptTextLineClass,
11
+ type TranscriptEntry,
12
+ } from '../live-transcript/index.ts'
13
+ import { cn } from '../shared/index.ts'
14
+
15
+ interface Props extends astroHTML.JSX.HTMLAttributes {
16
+ /** The ordered list of transcript entries to display. */
17
+ entries: TranscriptEntry[]
18
+ /** Tightens spacing between speaker blocks. */
19
+ compact?: boolean
20
+ }
21
+
22
+ const { entries, compact = false, class: className, ...rest } = Astro.props
23
+
24
+ const compactVariant = compact ? 'true' : 'false'
25
+ const blocks = groupConsecutiveBySpeaker(entries)
26
+ const { ariaProps, dataAttributes } = createLiveTranscript()
27
+ ---
28
+
29
+ <div
30
+ class={cn(liveTranscriptVariants({ compact: compactVariant }), className)}
31
+ {...ariaProps}
32
+ {...dataAttributes}
33
+ {...rest}
34
+ >
35
+ {
36
+ blocks.map((block, blockIndex) => (
37
+ <div class={transcriptBlockVariants({ compact: compactVariant })}>
38
+ <div class={transcriptSpeakerRowClass}>
39
+ <span
40
+ class={transcriptSpeakerNameClass}
41
+ style={block.speakerColor ? `color:${block.speakerColor}` : undefined}
42
+ >
43
+ {block.speaker}
44
+ </span>
45
+ {block.timestamp && (
46
+ <span class={transcriptTimestampClass}>{block.timestamp}</span>
47
+ )}
48
+ </div>
49
+ {block.texts.map((text) => (
50
+ <p class={transcriptTextLineClass}>{text}</p>
51
+ ))}
52
+ </div>
53
+ ))
54
+ }
55
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,74 @@
1
+ ---
2
+ import {
3
+ contentBounds,
4
+ miniScale,
5
+ worldToMini,
6
+ viewportRectInMini,
7
+ createMiniMap,
8
+ miniMapVariants,
9
+ miniMapDotClass,
10
+ miniMapViewportClass,
11
+ type MiniMapItem,
12
+ type Rect,
13
+ } from '../mini-map/index.ts'
14
+ import { cn } from '../shared/index.ts'
15
+
16
+ interface Props extends astroHTML.JSX.HTMLAttributes {
17
+ /** Items to render as dots on the minimap. */
18
+ items: MiniMapItem[]
19
+ /** Current viewport in world coordinates. Renders a viewport indicator rect. */
20
+ viewport?: Rect
21
+ /** Width of the minimap box in px. */
22
+ width?: number
23
+ /** Height of the minimap box in px. */
24
+ height?: number
25
+ /** Padding inset in px (applied to all sides). */
26
+ padding?: number
27
+ }
28
+
29
+ const {
30
+ items,
31
+ viewport,
32
+ width = 200,
33
+ height = 140,
34
+ padding = 8,
35
+ class: className,
36
+ ...rest
37
+ } = Astro.props
38
+
39
+ const bounds = contentBounds(items)
40
+ const scale = miniScale(bounds, width, height, padding)
41
+ const { ariaProps, dataAttributes } = createMiniMap()
42
+
43
+ const viewportRect = viewport != null
44
+ ? viewportRectInMini(viewport, bounds, scale, padding)
45
+ : null
46
+ ---
47
+
48
+ <div
49
+ class={cn(miniMapVariants({ interactive: 'false' }), className)}
50
+ style={`width:${width}px;height:${height}px;position:relative`}
51
+ {...ariaProps}
52
+ {...dataAttributes}
53
+ {...rest}
54
+ >
55
+ {items.map((item) => {
56
+ const pos = worldToMini(item, bounds, scale, padding)
57
+ const w = Math.max(4, (item.width ?? 8) * scale)
58
+ const h = Math.max(4, (item.height ?? 8) * scale)
59
+ return (
60
+ <span
61
+ class={miniMapDotClass}
62
+ style={`position:absolute;left:${pos.x}px;top:${pos.y}px;width:${w}px;height:${h}px`}
63
+ aria-hidden="true"
64
+ />
65
+ )
66
+ })}
67
+ {viewportRect != null && (
68
+ <span
69
+ class={miniMapViewportClass}
70
+ style={`position:absolute;left:${viewportRect.x}px;top:${viewportRect.y}px;width:${viewportRect.width}px;height:${viewportRect.height}px`}
71
+ aria-hidden="true"
72
+ />
73
+ )}
74
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,202 @@
1
+ ---
2
+ import {
3
+ createPreCallLobby,
4
+ micLevelToBars,
5
+ preCallLobbyVariants,
6
+ preCallCameraVariants,
7
+ preCallCameraPlaceholderClass,
8
+ preCallDeviceRowClass,
9
+ preCallDeviceLabelClass,
10
+ preCallDeviceSelectClass,
11
+ preCallMicMeterClass,
12
+ preCallMicBarVariants,
13
+ preCallControlsRowClass,
14
+ preCallToggleVariants,
15
+ preCallJoinButtonClass,
16
+ type MediaDeviceOption,
17
+ } from '../pre-call-lobby/index.ts'
18
+ import { cn } from '../shared/index.ts'
19
+
20
+ const MIC_BAR_COUNT = 8
21
+
22
+ interface Props extends astroHTML.JSX.HTMLAttributes {
23
+ /** Whether the camera is on. */
24
+ cameraOn?: boolean
25
+ /** Whether the microphone is on. */
26
+ micOn?: boolean
27
+ /** Current microphone level (0..1). */
28
+ micLevel?: number
29
+ /** Available cameras. */
30
+ cameras?: MediaDeviceOption[]
31
+ /** Available microphones. */
32
+ microphones?: MediaDeviceOption[]
33
+ /** Available speakers. */
34
+ speakers?: MediaDeviceOption[]
35
+ /** Currently selected camera id. */
36
+ selectedCamera?: string
37
+ /** Currently selected microphone id. */
38
+ selectedMicrophone?: string
39
+ /** Currently selected speaker id. */
40
+ selectedSpeaker?: string
41
+ /** Label for the join button. */
42
+ joinLabel?: string
43
+ }
44
+
45
+ const {
46
+ cameraOn = true,
47
+ micOn = true,
48
+ micLevel = 0,
49
+ cameras = [],
50
+ microphones = [],
51
+ speakers = [],
52
+ selectedCamera,
53
+ selectedMicrophone,
54
+ selectedSpeaker,
55
+ joinLabel = 'Join',
56
+ class: className,
57
+ ...rest
58
+ } = Astro.props
59
+
60
+ const { ariaProps, dataAttributes } = createPreCallLobby({ cameraOn, micOn })
61
+ const litBars = micLevelToBars(micLevel, MIC_BAR_COUNT)
62
+ ---
63
+
64
+ <div class={cn(preCallLobbyVariants(), className)} {...ariaProps} {...dataAttributes} {...rest}>
65
+ <!-- Camera preview -->
66
+ <div class={preCallCameraVariants({ camera: cameraOn ? 'on' : 'off' })}>
67
+ {cameraOn ? (
68
+ <slot name="preview">
69
+ <div class={preCallCameraPlaceholderClass}>
70
+ <span class="text-xs">No preview</span>
71
+ </div>
72
+ </slot>
73
+ ) : (
74
+ <div class={preCallCameraPlaceholderClass}>
75
+ <svg
76
+ xmlns="http://www.w3.org/2000/svg"
77
+ width="40"
78
+ height="40"
79
+ viewBox="0 0 24 24"
80
+ fill="none"
81
+ stroke="currentColor"
82
+ stroke-width="1.5"
83
+ stroke-linecap="round"
84
+ stroke-linejoin="round"
85
+ aria-hidden="true"
86
+ >
87
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
88
+ <circle cx="9" cy="7" r="4" />
89
+ <line x1="2" y1="2" x2="22" y2="22" />
90
+ </svg>
91
+ <span class="text-xs">Camera is off</span>
92
+ </div>
93
+ )}
94
+ </div>
95
+
96
+ <!-- Mic level meter -->
97
+ <div class="flex items-center gap-2">
98
+ <span class="text-xs text-muted-foreground">Mic</span>
99
+ <div class={preCallMicMeterClass} aria-label={`Mic level: ${litBars} of ${MIC_BAR_COUNT}`}>
100
+ {Array.from({ length: MIC_BAR_COUNT }, (_, i) => {
101
+ const isLit = i < litBars
102
+ const heightClass = i < 2 ? 'h-2' : i < 4 ? 'h-3' : i < 6 ? 'h-4' : 'h-5'
103
+ return (
104
+ <div
105
+ class={`${preCallMicBarVariants({ lit: isLit ? 'true' : 'false' })} ${heightClass}`}
106
+ data-lit={isLit ? 'true' : 'false'}
107
+ />
108
+ )
109
+ })}
110
+ </div>
111
+ </div>
112
+
113
+ <!-- Device selectors -->
114
+ {cameras.length > 0 && (
115
+ <div class={preCallDeviceRowClass}>
116
+ <label for="pcl-camera-select" class={preCallDeviceLabelClass}>Camera</label>
117
+ <select id="pcl-camera-select" class={preCallDeviceSelectClass} value={selectedCamera}>
118
+ {cameras.map((d) => (
119
+ <option value={d.id}>{d.label}</option>
120
+ ))}
121
+ </select>
122
+ </div>
123
+ )}
124
+
125
+ {microphones.length > 0 && (
126
+ <div class={preCallDeviceRowClass}>
127
+ <label for="pcl-mic-select" class={preCallDeviceLabelClass}>Microphone</label>
128
+ <select id="pcl-mic-select" class={preCallDeviceSelectClass} value={selectedMicrophone}>
129
+ {microphones.map((d) => (
130
+ <option value={d.id}>{d.label}</option>
131
+ ))}
132
+ </select>
133
+ </div>
134
+ )}
135
+
136
+ {speakers.length > 0 && (
137
+ <div class={preCallDeviceRowClass}>
138
+ <label for="pcl-speaker-select" class={preCallDeviceLabelClass}>Speaker</label>
139
+ <select id="pcl-speaker-select" class={preCallDeviceSelectClass} value={selectedSpeaker}>
140
+ {speakers.map((d) => (
141
+ <option value={d.id}>{d.label}</option>
142
+ ))}
143
+ </select>
144
+ </div>
145
+ )}
146
+
147
+ <!-- Controls row -->
148
+ <div class={preCallControlsRowClass}>
149
+ <button
150
+ type="button"
151
+ aria-label={cameraOn ? 'Turn camera off' : 'Turn camera on'}
152
+ aria-pressed={cameraOn}
153
+ class={preCallToggleVariants({ active: cameraOn ? 'true' : 'false' })}
154
+ >
155
+ <svg
156
+ xmlns="http://www.w3.org/2000/svg"
157
+ width="18"
158
+ height="18"
159
+ viewBox="0 0 24 24"
160
+ fill="none"
161
+ stroke="currentColor"
162
+ stroke-width="2"
163
+ stroke-linecap="round"
164
+ stroke-linejoin="round"
165
+ aria-hidden="true"
166
+ >
167
+ <path d="M23 7l-7 5 7 5V7z" />
168
+ <rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
169
+ </svg>
170
+ </button>
171
+
172
+ <button
173
+ type="button"
174
+ aria-label={micOn ? 'Mute microphone' : 'Unmute microphone'}
175
+ aria-pressed={micOn}
176
+ class={preCallToggleVariants({ active: micOn ? 'true' : 'false' })}
177
+ >
178
+ <svg
179
+ xmlns="http://www.w3.org/2000/svg"
180
+ width="18"
181
+ height="18"
182
+ viewBox="0 0 24 24"
183
+ fill="none"
184
+ stroke="currentColor"
185
+ stroke-width="2"
186
+ stroke-linecap="round"
187
+ stroke-linejoin="round"
188
+ aria-hidden="true"
189
+ >
190
+ <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
191
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
192
+ <line x1="12" y1="19" x2="12" y2="23" />
193
+ <line x1="8" y1="23" x2="16" y2="23" />
194
+ </svg>
195
+ </button>
196
+ </div>
197
+
198
+ <!-- Join button -->
199
+ <button type="button" class={preCallJoinButtonClass}>
200
+ {joinLabel}
201
+ </button>
202
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,144 @@
1
+ ---
2
+ import {
3
+ clampValue,
4
+ valueToFraction,
5
+ gaugeStrokeDashoffset,
6
+ resolveZoneTone,
7
+ createRadialGauge,
8
+ radialGaugeRootVariants,
9
+ gaugeTrackClass,
10
+ gaugeArcClass,
11
+ gaugeArcToneVariants,
12
+ gaugeLabelClass,
13
+ gaugeSublabelClass,
14
+ GAUGE_SIZE_PX,
15
+ GAUGE_LABEL_FONT_SIZE,
16
+ GAUGE_SUBLABEL_FONT_SIZE,
17
+ type GaugeSize,
18
+ type GaugeZone,
19
+ } from '../radial-gauge/index.ts'
20
+ import { cn } from '../shared/index.ts'
21
+
22
+ interface Props extends astroHTML.JSX.SVGAttributes {
23
+ /** Current value. */
24
+ value: number
25
+ /** Minimum value. Defaults to 0. */
26
+ min?: number
27
+ /** Maximum value. Defaults to 100. */
28
+ max?: number
29
+ /** Visual size of the gauge. Defaults to 'md'. */
30
+ size?: GaugeSize
31
+ /** Stroke thickness in px. Defaults to 8. */
32
+ thickness?: number
33
+ /** Primary center label. When omitted and `showValue` is true, the numeric value is shown. */
34
+ label?: string
35
+ /** Secondary label shown below the primary label. */
36
+ sublabel?: string
37
+ /**
38
+ * Threshold zones that colour the arc based on the current value.
39
+ * List in ascending `upTo` order for clarity (internally sorted).
40
+ */
41
+ zones?: GaugeZone[]
42
+ /** Show the numeric value in the center when no explicit label is given. Defaults to true. */
43
+ showValue?: boolean
44
+ }
45
+
46
+ const {
47
+ value,
48
+ min = 0,
49
+ max = 100,
50
+ size = 'md',
51
+ thickness = 8,
52
+ label,
53
+ sublabel,
54
+ zones,
55
+ showValue = true,
56
+ class: className,
57
+ ...rest
58
+ } = Astro.props
59
+
60
+ const diameter = GAUGE_SIZE_PX[size]
61
+ const cx = diameter / 2
62
+ const cy = diameter / 2
63
+ const r = cx - thickness / 2
64
+
65
+ const clamped = clampValue(value, min, max)
66
+ const fraction = valueToFraction(clamped, min, max)
67
+
68
+ const circumference = 2 * Math.PI * r
69
+ const dashoffset = gaugeStrokeDashoffset(fraction, circumference)
70
+
71
+ const tone = zones && zones.length > 0 ? resolveZoneTone(clamped, zones, max) : 'default'
72
+
73
+ const { ariaProps, dataAttributes } = createRadialGauge({ value, min, max, size })
74
+
75
+ const centerLabel = label ?? (showValue ? String(clamped) : null)
76
+ const labelFontSize = GAUGE_LABEL_FONT_SIZE[size]
77
+ const sublabelFontSize = GAUGE_SUBLABEL_FONT_SIZE[size]
78
+ const labelDy = sublabel ? -sublabelFontSize * 0.8 : 0
79
+ ---
80
+
81
+ <svg
82
+ width={diameter}
83
+ height={diameter}
84
+ viewBox={`0 0 ${diameter} ${diameter}`}
85
+ role="meter"
86
+ aria-valuenow={ariaProps['aria-valuenow']}
87
+ aria-valuemin={ariaProps['aria-valuemin']}
88
+ aria-valuemax={ariaProps['aria-valuemax']}
89
+ class={cn(radialGaugeRootVariants({ size }), className)}
90
+ {...dataAttributes}
91
+ {...rest}
92
+ >
93
+ {/* Background track */}
94
+ <circle
95
+ cx={cx}
96
+ cy={cy}
97
+ r={r}
98
+ stroke-width={thickness}
99
+ class={gaugeTrackClass}
100
+ />
101
+
102
+ {/* Value arc */}
103
+ <circle
104
+ cx={cx}
105
+ cy={cy}
106
+ r={r}
107
+ stroke-width={thickness}
108
+ stroke-dasharray={`${circumference} ${circumference}`}
109
+ stroke-dashoffset={dashoffset}
110
+ stroke-linecap="round"
111
+ transform={`rotate(-90 ${cx} ${cy})`}
112
+ class={cn(gaugeArcClass, gaugeArcToneVariants({ tone }))}
113
+ />
114
+
115
+ {/* Center label */}
116
+ {centerLabel != null && (
117
+ <text
118
+ x={cx}
119
+ y={cy}
120
+ text-anchor="middle"
121
+ dominant-baseline="central"
122
+ font-size={labelFontSize}
123
+ dy={labelDy}
124
+ class={gaugeLabelClass}
125
+ >
126
+ {centerLabel}
127
+ </text>
128
+ )}
129
+
130
+ {/* Sub-label */}
131
+ {sublabel != null && (
132
+ <text
133
+ x={cx}
134
+ y={cy}
135
+ text-anchor="middle"
136
+ dominant-baseline="central"
137
+ font-size={sublabelFontSize}
138
+ dy={labelDy + labelFontSize * 1.1}
139
+ class={gaugeSublabelClass}
140
+ >
141
+ {sublabel}
142
+ </text>
143
+ )}
144
+ </svg>
@@ -0,0 +1 @@
1
+ export {}