@refraction-ui/astro 0.17.4 → 0.18.1

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 (40) hide show
  1. package/dist/analytics-sink-app-insights/app-insights-sink.ts +13 -3
  2. package/dist/analytics-sink-posthog/client-sdk-sink.ts +12 -1
  3. package/dist/analytics-sink-posthog/replay.ts +10 -1
  4. package/dist/astro-calendar/Calendar.astro +7 -2
  5. package/dist/astro-date-picker/DatePicker.astro +10 -2
  6. package/dist/astro-file-tree/FileTree.astro +86 -0
  7. package/dist/astro-file-tree/FileTree.stories.ts +29 -2
  8. package/dist/astro-file-tree/FileTreeItems.astro +77 -0
  9. package/dist/astro-file-tree/index.ts +2 -4
  10. package/dist/astro-pagination/Pagination.astro +100 -1
  11. package/dist/astro-pagination/Pagination.stories.ts +11 -1
  12. package/dist/astro-pagination/index.ts +1 -1
  13. package/dist/astro-skip-to-content/SkipToContent.astro +22 -1
  14. package/dist/astro-skip-to-content/SkipToContent.stories.ts +7 -1
  15. package/dist/astro-skip-to-content/index.ts +1 -1
  16. package/dist/astro-slider/Slider.astro +75 -0
  17. package/dist/astro-slider/Slider.stories.ts +24 -0
  18. package/dist/astro-slider/index.ts +1 -1
  19. package/dist/astro-steps/Step.astro +20 -0
  20. package/dist/astro-steps/StepContent.astro +17 -0
  21. package/dist/astro-steps/StepDescription.astro +17 -0
  22. package/dist/astro-steps/StepIndicator.astro +20 -0
  23. package/dist/astro-steps/StepTitle.astro +17 -0
  24. package/dist/astro-steps/Steps.astro +17 -1
  25. package/dist/astro-steps/Steps.stories.ts +7 -1
  26. package/dist/astro-steps/index.ts +6 -1
  27. package/dist/astro-toast/Toaster.astro +3 -3
  28. package/dist/avatar-group/avatar-group.styles.ts +5 -5
  29. package/dist/callout/callout.styles.ts +1 -1
  30. package/dist/empty-state/empty-state.styles.ts +2 -2
  31. package/dist/file-tree/file-tree.styles.ts +31 -0
  32. package/dist/file-tree/file-tree.ts +360 -0
  33. package/dist/file-tree/index.ts +15 -0
  34. package/dist/input/input.styles.ts +1 -1
  35. package/dist/presence-indicator/presence-indicator.styles.ts +5 -5
  36. package/dist/progress-display/progress-display.styles.ts +2 -2
  37. package/dist/status-indicator/status-indicator.styles.ts +12 -12
  38. package/dist/toast/toast.styles.ts +3 -6
  39. package/package.json +1 -1
  40. package/dist/astro-file-tree/astro-file-tree.astro +0 -4
@@ -0,0 +1,17 @@
1
+ ---
2
+ import { createStepContent } from '../steps/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 api = createStepContent()
9
+ ---
10
+
11
+ <div
12
+ class={cn('flex-1 pb-8', className)}
13
+ {...api.dataAttributes}
14
+ {...props}
15
+ >
16
+ <slot />
17
+ </div>
@@ -0,0 +1,17 @@
1
+ ---
2
+ import { createStepDescription, stepDescriptionVariants } from '../steps/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 api = createStepDescription()
9
+ ---
10
+
11
+ <p
12
+ class={cn(stepDescriptionVariants(), className)}
13
+ {...api.dataAttributes}
14
+ {...props}
15
+ >
16
+ <slot />
17
+ </p>
@@ -0,0 +1,20 @@
1
+ ---
2
+ import { createStepIndicator, stepIndicatorVariants } from '../steps/index.ts'
3
+ import { cn } from '../shared/index.ts'
4
+
5
+ interface Props extends astroHTML.JSX.HTMLAttributes {
6
+ status?: 'completed' | 'active' | 'upcoming'
7
+ }
8
+
9
+ const { status, class: className, ...props } = Astro.props
10
+ const api = createStepIndicator()
11
+ ---
12
+
13
+ <div
14
+ class={cn(stepIndicatorVariants({ status }), className)}
15
+ data-state={status ?? 'upcoming'}
16
+ {...api.dataAttributes}
17
+ {...props}
18
+ >
19
+ <slot />
20
+ </div>
@@ -0,0 +1,17 @@
1
+ ---
2
+ import { createStepTitle, stepTitleVariants } from '../steps/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 api = createStepTitle()
9
+ ---
10
+
11
+ <h4
12
+ class={cn(stepTitleVariants(), className)}
13
+ {...api.dataAttributes}
14
+ {...props}
15
+ >
16
+ <slot />
17
+ </h4>
@@ -1,3 +1,19 @@
1
1
  ---
2
+ import { createSteps, stepsVariants } from '../steps/index.ts'
3
+ import { cn } from '../shared/index.ts'
4
+
5
+ interface Props extends astroHTML.JSX.HTMLAttributes {
6
+ orientation?: 'vertical' | 'horizontal'
7
+ }
8
+
9
+ const { orientation, class: className, ...props } = Astro.props
10
+ const api = createSteps()
2
11
  ---
3
- <div></div>
12
+
13
+ <div
14
+ class={cn(stepsVariants({ orientation }), className)}
15
+ {...api.dataAttributes}
16
+ {...props}
17
+ >
18
+ <slot />
19
+ </div>
@@ -3,10 +3,16 @@ import Component from './Steps.astro'
3
3
  const meta = {
4
4
  title: 'Astro/Steps',
5
5
  component: Component,
6
+ argTypes: {
7
+ orientation: { control: 'select', options: ['vertical', 'horizontal'] },
8
+ },
6
9
  }
7
10
 
8
11
  export default meta
9
12
 
10
13
  export const Default = {
11
- args: {},
14
+ args: {
15
+ default: '<div data-slot="step" data-state="completed"><strong>1. Account</strong></div><div data-slot="step" data-state="active"><strong>2. Profile</strong></div><div data-slot="step" data-state="upcoming"><strong>3. Review</strong></div>',
16
+ orientation: 'vertical',
17
+ }
12
18
  }
@@ -1 +1,6 @@
1
- export { default as Steps } from './Steps.astro';
1
+ export { default as Steps } from './Steps.astro'
2
+ export { default as Step } from './Step.astro'
3
+ export { default as StepIndicator } from './StepIndicator.astro'
4
+ export { default as StepContent } from './StepContent.astro'
5
+ export { default as StepTitle } from './StepTitle.astro'
6
+ export { default as StepDescription } from './StepDescription.astro'
@@ -88,9 +88,9 @@ const { class: className, ...props } = Astro.props
88
88
 
89
89
  const VARIANT_CLASSES: Record<string, string> = {
90
90
  default: 'border bg-background text-foreground',
91
- success: 'border-l-4 border-green-500/50 bg-green-50 text-green-900 dark:bg-green-950 dark:text-green-100',
92
- error: 'border-l-4 border-red-500/50 bg-red-50 text-red-900 dark:bg-red-950 dark:text-red-100',
93
- warning: 'border-l-4 border-amber-500/50 bg-amber-50 text-amber-900 dark:bg-amber-950 dark:text-amber-100',
91
+ success: 'border-l-4 border-success/50 bg-success/10 text-success',
92
+ error: 'border-l-4 border-destructive/50 bg-destructive/10 text-destructive',
93
+ warning: 'border-l-4 border-warning/50 bg-warning/10 text-warning',
94
94
  }
95
95
 
96
96
  function createToastElement(options: ToastOptions): HTMLElement {
@@ -49,11 +49,11 @@ export const avatarPresenceDotVariants = cva({
49
49
  xl: 'h-4 w-4',
50
50
  },
51
51
  status: {
52
- online: 'bg-green-500',
53
- offline: 'bg-gray-400',
54
- away: 'bg-yellow-500',
55
- busy: 'bg-red-500',
56
- dnd: 'bg-red-500',
52
+ online: 'bg-success',
53
+ offline: 'bg-neutral',
54
+ away: 'bg-warning',
55
+ busy: 'bg-destructive',
56
+ dnd: 'bg-destructive',
57
57
  },
58
58
  },
59
59
  defaultVariants: {
@@ -8,7 +8,7 @@ export const calloutVariants = cva({
8
8
  destructive: 'bg-destructive/10 border-destructive/20 text-destructive',
9
9
  success: 'bg-success/10 border-success/20 text-success',
10
10
  warning: 'bg-warning/10 border-warning/20 text-warning',
11
- info: 'bg-blue-500/10 border-blue-500/20 text-blue-700 dark:text-blue-400',
11
+ info: 'bg-info/10 border-info/20 text-info',
12
12
  },
13
13
  },
14
14
  defaultVariants: {
@@ -26,8 +26,8 @@ export const emptyStateIconChipVariants = cva({
26
26
  variants: {
27
27
  tone: {
28
28
  neutral: 'bg-muted text-muted-foreground',
29
- success: 'bg-green-500/10 text-green-600',
30
- warning: 'bg-yellow-500/10 text-yellow-600',
29
+ success: 'bg-success/10 text-success',
30
+ warning: 'bg-warning/10 text-warning',
31
31
  danger: 'bg-destructive/10 text-destructive',
32
32
  },
33
33
  },
@@ -0,0 +1,31 @@
1
+ import { cva } from '../shared/index.ts'
2
+
3
+ /** Tree container. */
4
+ export const fileTreeVariants = cva({
5
+ base: 'w-full text-sm',
6
+ })
7
+
8
+ /**
9
+ * A single tree row (treeitem). The `selected` variant carries the
10
+ * selected-row treatment so adapters never inline these classes.
11
+ */
12
+ export const fileTreeItemVariants = cva({
13
+ base: [
14
+ 'flex w-full cursor-pointer select-none items-center gap-1.5 rounded-md px-2 py-1',
15
+ 'text-sm outline-none transition-colors',
16
+ 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background',
17
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
18
+ ].join(' '),
19
+ variants: {
20
+ selected: {
21
+ true: 'bg-accent text-accent-foreground',
22
+ false: 'text-foreground hover:bg-muted',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ selected: 'false',
27
+ },
28
+ })
29
+
30
+ /** Expand/collapse glyph or file marker preceding the label. */
31
+ export const fileTreeGlyphClass = 'w-4 shrink-0 text-center text-muted-foreground'
@@ -0,0 +1,360 @@
1
+ /**
2
+ * @refraction-ui/file-tree — headless tree view.
3
+ *
4
+ * Owns the *behavior* of a hierarchical tree: the node model, expand/collapse
5
+ * state, single selection, roving focus, and WAI-ARIA treeview keyboard
6
+ * semantics. It has NO UI opinion — adapters render the nested markup and
7
+ * spread the ARIA objects.
8
+ */
9
+
10
+ /** A node in the tree. Parents carry `children`; leaves do not. */
11
+ export interface FileTreeNode {
12
+ /** Unique, stable id (used for expansion/selection/focus). */
13
+ id: string
14
+ /** Display label. */
15
+ label: string
16
+ /** Child nodes — presence (even empty is not allowed: omit for leaves) marks a parent. */
17
+ children?: FileTreeNode[]
18
+ /** Disabled nodes cannot be selected, toggled, or focused via click. */
19
+ disabled?: boolean
20
+ }
21
+
22
+ /** Options for {@link createFileTree}. */
23
+ export interface FileTreeConfig {
24
+ /** Root-level nodes. */
25
+ nodes?: FileTreeNode[]
26
+ /** Controlled expanded node ids. Pair with `onExpandedChange`. */
27
+ expandedIds?: string[]
28
+ /** Uncontrolled initial expanded node ids. */
29
+ defaultExpandedIds?: string[]
30
+ /** Controlled selected node id (`null` = none). Pair with `onSelectionChange`. */
31
+ selectedId?: string | null
32
+ /** Uncontrolled initial selected node id. */
33
+ defaultSelectedId?: string | null
34
+ /** Called whenever the expanded set changes. */
35
+ onExpandedChange?: (ids: string[]) => void
36
+ /** Called whenever the selection changes. */
37
+ onSelectionChange?: (id: string | null) => void
38
+ /** Accessible label for the tree (default `'File tree'`). */
39
+ 'aria-label'?: string
40
+ }
41
+
42
+ /** Snapshot of the store. Returned by `getState()`; treat as immutable. */
43
+ export interface FileTreeState {
44
+ nodes: FileTreeNode[]
45
+ expandedIds: string[]
46
+ selectedId: string | null
47
+ /** Node holding the roving-tabindex focus (null = the first visible item). */
48
+ focusedId: string | null
49
+ }
50
+
51
+ /** A row of the flattened, currently-visible tree (DFS order). */
52
+ export interface VisibleTreeItem {
53
+ node: FileTreeNode
54
+ /** Id of the parent node, or null for root-level items. */
55
+ parentId: string | null
56
+ /** 1-based nesting depth (ARIA `aria-level`). */
57
+ depth: number
58
+ hasChildren: boolean
59
+ isExpanded: boolean
60
+ isSelected: boolean
61
+ isFocused: boolean
62
+ }
63
+
64
+ /** The framework-agnostic store. React/Astro adapters wrap this. */
65
+ export interface FileTreeAPI {
66
+ /** Current immutable snapshot */
67
+ getState(): FileTreeState
68
+ /** Subscribe to changes; returns an unsubscribe fn (suits useSyncExternalStore) */
69
+ subscribe(listener: () => void): () => void
70
+
71
+ /** Flattened visible rows in DFS order (children of collapsed parents omitted) */
72
+ getVisibleItems(): VisibleTreeItem[]
73
+ isExpanded(id: string): boolean
74
+ /** Toggle a parent's expanded state (no-op for leaves/disabled nodes) */
75
+ toggle(id: string): void
76
+ expand(id: string): void
77
+ collapse(id: string): void
78
+ /** Select a node (`null` clears). No-op for unknown/disabled nodes. */
79
+ select(id: string | null): void
80
+ /** Move the roving focus to a node */
81
+ focus(id: string | null): void
82
+ /**
83
+ * WAI-ARIA treeview keyboard semantics, applied to the focused item:
84
+ * ArrowDown/Up move, ArrowRight expands or descends, ArrowLeft collapses or
85
+ * ascends, Home/End jump, Enter/Space selects.
86
+ */
87
+ handleKey(key: string): void
88
+
89
+ /** Replace the node model (adapters sync the prop) */
90
+ setNodes(nodes: FileTreeNode[]): void
91
+ /** Sync a controlled expanded set */
92
+ setExpandedIds(ids: string[]): void
93
+ /** Sync a controlled selection */
94
+ setSelectedId(id: string | null): void
95
+ /** Update callbacks/options after creation */
96
+ setOptions(options: {
97
+ onExpandedChange?: (ids: string[]) => void
98
+ onSelectionChange?: (id: string | null) => void
99
+ 'aria-label'?: string
100
+ }): void
101
+
102
+ /** ARIA attributes for the tree container (`role="tree"`). */
103
+ getTreeAria(): Record<string, string | number | boolean>
104
+ /** ARIA attributes for one visible row (`role="treeitem"`). */
105
+ getItemAria(item: VisibleTreeItem): Record<string, string | number | boolean>
106
+ }
107
+
108
+ function arrayEquals(a: string[], b: string[]): boolean {
109
+ return a.length === b.length && a.every((v, i) => v === b[i])
110
+ }
111
+
112
+ /**
113
+ * createFileTree — headless tree store with expand/collapse, single
114
+ * selection, roving focus, and treeview keyboard semantics. Supports
115
+ * controlled (`expandedIds` / `selectedId`) and uncontrolled
116
+ * (`defaultExpandedIds` / `defaultSelectedId`) usage.
117
+ */
118
+ export function createFileTree(config: FileTreeConfig = {}): FileTreeAPI {
119
+ let nodes: FileTreeNode[] = config.nodes ?? []
120
+ let ariaLabel = config['aria-label'] ?? 'File tree'
121
+ let onExpandedChange = config.onExpandedChange
122
+ let onSelectionChange = config.onSelectionChange
123
+
124
+ let uncontrolledExpanded: string[] = config.defaultExpandedIds ?? []
125
+ let controlledExpanded: string[] | undefined = config.expandedIds
126
+ let uncontrolledSelected: string | null = config.defaultSelectedId ?? null
127
+ let controlledSelected: string | null | undefined = config.selectedId
128
+ let focusedId: string | null = null
129
+
130
+ // Indexes rebuilt whenever the node model changes.
131
+ let parentById = new Map<string, string | null>()
132
+ let nodeById = new Map<string, FileTreeNode>()
133
+
134
+ function reindex(): void {
135
+ parentById = new Map()
136
+ nodeById = new Map()
137
+ const walk = (list: FileTreeNode[], parentId: string | null) => {
138
+ for (const node of list) {
139
+ nodeById.set(node.id, node)
140
+ parentById.set(node.id, parentId)
141
+ if (node.children) walk(node.children, node.id)
142
+ }
143
+ }
144
+ walk(nodes, null)
145
+ }
146
+ reindex()
147
+
148
+ const listeners = new Set<() => void>()
149
+ let snapshot: FileTreeState = buildSnapshot()
150
+ let visibleItems: VisibleTreeItem[] = buildVisibleItems()
151
+
152
+ function expandedIds(): string[] {
153
+ return controlledExpanded !== undefined ? controlledExpanded : uncontrolledExpanded
154
+ }
155
+
156
+ function selectedId(): string | null {
157
+ return controlledSelected !== undefined ? controlledSelected : uncontrolledSelected
158
+ }
159
+
160
+ function buildSnapshot(): FileTreeState {
161
+ return {
162
+ nodes,
163
+ expandedIds: expandedIds(),
164
+ selectedId: selectedId(),
165
+ focusedId,
166
+ }
167
+ }
168
+
169
+ function buildVisibleItems(): VisibleTreeItem[] {
170
+ const expanded = new Set(expandedIds())
171
+ const selected = selectedId()
172
+ const items: VisibleTreeItem[] = []
173
+ const walk = (list: FileTreeNode[], parentId: string | null, depth: number) => {
174
+ for (const node of list) {
175
+ const hasChildren = Boolean(node.children && node.children.length > 0)
176
+ const isExpanded = hasChildren && expanded.has(node.id)
177
+ items.push({
178
+ node,
179
+ parentId,
180
+ depth,
181
+ hasChildren,
182
+ isExpanded,
183
+ isSelected: node.id === selected,
184
+ isFocused: node.id === focusedId,
185
+ })
186
+ if (hasChildren && isExpanded) walk(node.children!, node.id, depth + 1)
187
+ }
188
+ }
189
+ walk(nodes, null, 1)
190
+ return items
191
+ }
192
+
193
+ function emit(): void {
194
+ snapshot = buildSnapshot()
195
+ visibleItems = buildVisibleItems()
196
+ for (const l of listeners) l()
197
+ }
198
+
199
+ function setExpanded(ids: string[]): void {
200
+ if (controlledExpanded === undefined) {
201
+ if (arrayEquals(uncontrolledExpanded, ids)) return
202
+ uncontrolledExpanded = ids
203
+ }
204
+ onExpandedChange?.(ids)
205
+ emit()
206
+ }
207
+
208
+ function isExpandable(id: string): boolean {
209
+ const node = nodeById.get(id)
210
+ return Boolean(node && !node.disabled && node.children && node.children.length > 0)
211
+ }
212
+
213
+ const api: FileTreeAPI = {
214
+ getState() {
215
+ return snapshot
216
+ },
217
+
218
+ subscribe(listener) {
219
+ listeners.add(listener)
220
+ return () => listeners.delete(listener)
221
+ },
222
+
223
+ getVisibleItems() {
224
+ return visibleItems
225
+ },
226
+
227
+ isExpanded(id) {
228
+ return expandedIds().includes(id)
229
+ },
230
+
231
+ toggle(id) {
232
+ if (!isExpandable(id)) return
233
+ const current = expandedIds()
234
+ setExpanded(current.includes(id) ? current.filter((v) => v !== id) : [...current, id])
235
+ },
236
+
237
+ expand(id) {
238
+ if (!isExpandable(id)) return
239
+ const current = expandedIds()
240
+ if (current.includes(id)) return
241
+ setExpanded([...current, id])
242
+ },
243
+
244
+ collapse(id) {
245
+ const current = expandedIds()
246
+ if (!current.includes(id)) return
247
+ setExpanded(current.filter((v) => v !== id))
248
+ },
249
+
250
+ select(id) {
251
+ if (id !== null) {
252
+ const node = nodeById.get(id)
253
+ if (!node || node.disabled) return
254
+ }
255
+ if (controlledSelected === undefined) {
256
+ if (uncontrolledSelected === id) return
257
+ uncontrolledSelected = id
258
+ }
259
+ onSelectionChange?.(id)
260
+ emit()
261
+ },
262
+
263
+ focus(id) {
264
+ // Focus is only meaningful on rows that are actually rendered.
265
+ if (id !== null && !visibleItems.some((i) => i.node.id === id)) return
266
+ if (focusedId === id) return
267
+ focusedId = id
268
+ emit()
269
+ },
270
+
271
+ handleKey(key) {
272
+ const items = visibleItems
273
+ if (items.length === 0) return
274
+ const currentIndex = focusedId ? items.findIndex((i) => i.node.id === focusedId) : -1
275
+
276
+ switch (key) {
277
+ case 'ArrowDown':
278
+ api.focus(items[Math.min(currentIndex + 1, items.length - 1)]!.node.id)
279
+ return
280
+ case 'ArrowUp':
281
+ api.focus(items[Math.max(currentIndex - 1, 0)]!.node.id)
282
+ return
283
+ case 'Home':
284
+ api.focus(items[0]!.node.id)
285
+ return
286
+ case 'End':
287
+ api.focus(items[items.length - 1]!.node.id)
288
+ return
289
+ case 'ArrowRight': {
290
+ const item = currentIndex === -1 ? undefined : items[currentIndex]
291
+ if (!item) {
292
+ api.focus(items[0]!.node.id)
293
+ return
294
+ }
295
+ if (!item.hasChildren) return
296
+ if (!item.isExpanded) api.expand(item.node.id)
297
+ // Expanded: descend to the first child (DFS order puts it next).
298
+ else api.focus(items[currentIndex + 1]?.node.id ?? item.node.id)
299
+ return
300
+ }
301
+ case 'ArrowLeft': {
302
+ const item = currentIndex === -1 ? undefined : items[currentIndex]
303
+ if (!item) return
304
+ if (item.hasChildren && item.isExpanded) api.collapse(item.node.id)
305
+ else if (item.parentId) api.focus(item.parentId)
306
+ return
307
+ }
308
+ case 'Enter':
309
+ case ' ': {
310
+ const item = currentIndex === -1 ? undefined : items[currentIndex]
311
+ if (item) api.select(item.node.id)
312
+ return
313
+ }
314
+ default:
315
+ return
316
+ }
317
+ },
318
+
319
+ setNodes(nextNodes) {
320
+ nodes = nextNodes
321
+ reindex()
322
+ emit()
323
+ },
324
+
325
+ setExpandedIds(ids) {
326
+ if (controlledExpanded !== undefined && arrayEquals(controlledExpanded, ids)) return
327
+ controlledExpanded = ids
328
+ emit()
329
+ },
330
+
331
+ setSelectedId(id) {
332
+ if (controlledSelected !== undefined && controlledSelected === id) return
333
+ controlledSelected = id
334
+ emit()
335
+ },
336
+
337
+ setOptions(options) {
338
+ if (options.onExpandedChange !== undefined) onExpandedChange = options.onExpandedChange
339
+ if (options.onSelectionChange !== undefined) onSelectionChange = options.onSelectionChange
340
+ if (options['aria-label'] !== undefined) ariaLabel = options['aria-label']
341
+ },
342
+
343
+ getTreeAria(): Record<string, string | number | boolean> {
344
+ return { role: 'tree', 'aria-label': ariaLabel }
345
+ },
346
+
347
+ getItemAria(item): Record<string, string | number | boolean> {
348
+ const aria: Record<string, string | number | boolean> = {
349
+ role: 'treeitem',
350
+ 'aria-level': item.depth,
351
+ 'aria-selected': item.isSelected,
352
+ }
353
+ if (item.hasChildren) aria['aria-expanded'] = item.isExpanded
354
+ if (item.node.disabled) aria['aria-disabled'] = true
355
+ return aria
356
+ },
357
+ }
358
+
359
+ return api
360
+ }
@@ -0,0 +1,15 @@
1
+ export { createFileTree } from './file-tree.js'
2
+
3
+ export type {
4
+ FileTreeNode,
5
+ FileTreeConfig,
6
+ FileTreeState,
7
+ FileTreeAPI,
8
+ VisibleTreeItem,
9
+ } from './file-tree.js'
10
+
11
+ export {
12
+ fileTreeVariants,
13
+ fileTreeItemVariants,
14
+ fileTreeGlyphClass,
15
+ } from './file-tree.styles.js'
@@ -9,7 +9,7 @@ export const inputVariants = cva({
9
9
  lg: 'h-10 text-base',
10
10
  },
11
11
  validationState: {
12
- valid: 'border-green-500 focus-visible:ring-green-500',
12
+ valid: 'border-success focus-visible:ring-success',
13
13
  invalid: 'border-destructive focus-visible:ring-destructive',
14
14
  },
15
15
  },
@@ -4,11 +4,11 @@ export const presenceDotVariants = cva({
4
4
  base: 'inline-block rounded-full',
5
5
  variants: {
6
6
  status: {
7
- online: 'bg-green-500',
8
- offline: 'bg-gray-400',
9
- away: 'bg-yellow-500',
10
- busy: 'bg-red-500',
11
- dnd: 'bg-red-500',
7
+ online: 'bg-success',
8
+ offline: 'bg-neutral',
9
+ away: 'bg-warning',
10
+ busy: 'bg-destructive',
11
+ dnd: 'bg-destructive',
12
12
  },
13
13
  size: {
14
14
  sm: 'h-2 w-2',
@@ -20,8 +20,8 @@ export const statCardVariants = cva({
20
20
  color: {
21
21
  default: 'bg-card text-card-foreground',
22
22
  primary: 'bg-primary/10 text-primary border-primary/20',
23
- success: 'bg-green-500/10 text-green-700 border-green-500/20',
24
- warning: 'bg-yellow-500/10 text-yellow-700 border-yellow-500/20',
23
+ success: 'bg-success/10 text-success border-success/20',
24
+ warning: 'bg-warning/10 text-warning border-warning/20',
25
25
  destructive: 'bg-destructive/10 text-destructive border-destructive/20',
26
26
  },
27
27
  },
@@ -7,12 +7,12 @@ export const statusDotVariants = cva({
7
7
  base: 'inline-block h-2 w-2 rounded-full',
8
8
  variants: {
9
9
  type: {
10
- success: 'bg-green-500',
11
- error: 'bg-red-500',
12
- warning: 'bg-yellow-500',
13
- info: 'bg-blue-500',
14
- pending: 'bg-orange-500',
15
- neutral: 'bg-gray-400',
10
+ success: 'bg-success',
11
+ error: 'bg-destructive',
12
+ warning: 'bg-warning',
13
+ info: 'bg-info',
14
+ pending: 'bg-pending',
15
+ neutral: 'bg-neutral',
16
16
  },
17
17
  },
18
18
  defaultVariants: {
@@ -24,12 +24,12 @@ export const statusPulseVariants = cva({
24
24
  base: 'animate-pulse inline-block h-2 w-2 rounded-full',
25
25
  variants: {
26
26
  type: {
27
- success: 'bg-green-500',
28
- error: 'bg-red-500',
29
- warning: 'bg-yellow-500',
30
- info: 'bg-blue-500',
31
- pending: 'bg-orange-500',
32
- neutral: 'bg-gray-400',
27
+ success: 'bg-success',
28
+ error: 'bg-destructive',
29
+ warning: 'bg-warning',
30
+ info: 'bg-info',
31
+ pending: 'bg-pending',
32
+ neutral: 'bg-neutral',
33
33
  },
34
34
  },
35
35
  defaultVariants: {