@adea-ai/ui 0.22.2 → 0.23.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 (92) hide show
  1. package/README.md +6 -3
  2. package/dist/components/account-drawer.d.ts +3 -3
  3. package/dist/components/account-drawer.js +119 -19
  4. package/dist/components/on-screen-controls.d.ts +2 -1
  5. package/dist/components/on-screen-controls.js +158 -46
  6. package/dist/components/scene-settings.d.ts +3 -3
  7. package/dist/components/scene-settings.js +247 -28
  8. package/dist/components/theme-provider.d.ts +27 -5
  9. package/dist/components/theme-provider.js +96 -7
  10. package/dist/components/theme-toggle.d.ts +4 -4
  11. package/dist/components/theme-toggle.js +62 -18
  12. package/dist/components/ui/badge.d.ts +4 -3
  13. package/dist/components/ui/badge.js +31 -30
  14. package/dist/components/ui/button.d.ts +4 -3
  15. package/dist/components/ui/button.js +49 -32
  16. package/dist/components/ui/card.d.ts +3 -3
  17. package/dist/components/ui/card.js +25 -6
  18. package/dist/components/ui/dialog.d.ts +13 -13
  19. package/dist/components/ui/dialog.js +94 -26
  20. package/dist/components/ui/drawer.d.ts +23 -15
  21. package/dist/components/ui/drawer.js +109 -64
  22. package/dist/components/ui/dropdown-menu.d.ts +28 -22
  23. package/dist/components/ui/dropdown-menu.js +165 -36
  24. package/dist/components/ui/empty.d.ts +7 -6
  25. package/dist/components/ui/empty.js +78 -26
  26. package/dist/components/ui/field.d.ts +15 -14
  27. package/dist/components/ui/field.js +177 -55
  28. package/dist/components/ui/input.d.ts +2 -2
  29. package/dist/components/ui/input.js +20 -5
  30. package/dist/components/ui/label.d.ts +2 -2
  31. package/dist/components/ui/label.js +15 -5
  32. package/dist/components/ui/radio-group.d.ts +4 -4
  33. package/dist/components/ui/radio-group.js +36 -9
  34. package/dist/components/ui/separator.d.ts +6 -2
  35. package/dist/components/ui/separator.js +18 -6
  36. package/dist/components/ui/skeleton.d.ts +2 -1
  37. package/dist/components/ui/skeleton.js +15 -4
  38. package/dist/components/ui/spinner.d.ts +4 -3
  39. package/dist/components/ui/spinner.js +15 -5
  40. package/dist/components/ui/switch.d.ts +6 -3
  41. package/dist/components/ui/switch.js +57 -6
  42. package/dist/components/ui/tabs.d.ts +6 -5
  43. package/dist/components/ui/tabs.js +52 -24
  44. package/dist/components/ui/toggle-group.d.ts +10 -8
  45. package/dist/components/ui/toggle-group.js +91 -20
  46. package/dist/components/ui/toggle.d.ts +4 -2
  47. package/dist/components/ui/toggle.js +38 -23
  48. package/dist/components/ui/tooltip.d.ts +13 -6
  49. package/dist/components/ui/tooltip.js +51 -14
  50. package/dist/components/version-dialog.d.ts +2 -2
  51. package/dist/components/version-dialog.js +322 -129
  52. package/dist/components/workspace-brand.d.ts +1 -1
  53. package/dist/components/workspace-brand.js +12 -3
  54. package/dist/components/workspace-logo.d.ts +2 -2
  55. package/dist/components/workspace-logo.js +15 -3
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.js +20 -19
  58. package/dist/lib/utils.js +7 -4
  59. package/dist/lib/version-notes.js +14 -29
  60. package/package.json +18 -16
  61. package/src/components/account-drawer.tsx +48 -67
  62. package/src/components/on-screen-controls.tsx +55 -62
  63. package/src/components/scene-settings.tsx +97 -100
  64. package/src/components/theme-provider.tsx +122 -22
  65. package/src/components/theme-toggle.tsx +29 -54
  66. package/src/components/ui/badge.tsx +14 -23
  67. package/src/components/ui/button.tsx +9 -11
  68. package/src/components/ui/card.tsx +9 -6
  69. package/src/components/ui/dialog.tsx +58 -55
  70. package/src/components/ui/drawer.tsx +84 -125
  71. package/src/components/ui/dropdown-menu.tsx +130 -153
  72. package/src/components/ui/empty.tsx +29 -26
  73. package/src/components/ui/field.tsx +83 -87
  74. package/src/components/ui/input.tsx +8 -8
  75. package/src/components/ui/label.tsx +6 -7
  76. package/src/components/ui/radio-group.tsx +22 -19
  77. package/src/components/ui/separator.tsx +16 -9
  78. package/src/components/ui/skeleton.tsx +6 -3
  79. package/src/components/ui/spinner.tsx +9 -5
  80. package/src/components/ui/switch.tsx +47 -20
  81. package/src/components/ui/tabs.tsx +31 -30
  82. package/src/components/ui/toggle-group.tsx +65 -54
  83. package/src/components/ui/toggle.tsx +9 -12
  84. package/src/components/ui/tooltip.tsx +41 -36
  85. package/src/components/version-dialog.tsx +144 -144
  86. package/src/components/workspace-brand.tsx +5 -5
  87. package/src/components/workspace-logo.tsx +9 -8
  88. package/src/index.tsx +7 -1
  89. package/src/styles/base.css +637 -0
  90. package/src/styles/globals.css +1 -1
  91. package/tsconfig.json +2 -1
  92. package/components.json +0 -19
@@ -1,52 +1,49 @@
1
- 'use client'
2
-
3
- import { useMemo } from 'react'
4
1
  import { cva, type VariantProps } from 'class-variance-authority'
2
+ import { createMemo, Show, splitProps, type ComponentProps, type JSX } from 'solid-js'
5
3
 
6
4
  import { cn } from '#lib/utils'
7
5
  import { Label } from '#components/ui/label'
8
6
  import { Separator } from '#components/ui/separator'
9
7
 
10
- function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {
8
+ function FieldSet(props: ComponentProps<'fieldset'>) {
9
+ const [local, rest] = splitProps(props, ['class'])
11
10
  return (
12
11
  <fieldset
13
12
  data-slot="field-set"
14
- className={cn(
13
+ class={cn(
15
14
  'flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
16
- className
15
+ local.class
17
16
  )}
18
- {...props}
17
+ {...rest}
19
18
  />
20
19
  )
21
20
  }
22
21
 
23
- function FieldLegend({
24
- className,
25
- variant = 'legend',
26
- ...props
27
- }: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
22
+ function FieldLegend(props: ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
23
+ const [local, rest] = splitProps(props, ['class', 'variant'])
28
24
  return (
29
25
  <legend
30
26
  data-slot="field-legend"
31
- data-variant={variant}
32
- className={cn(
27
+ data-variant={local.variant ?? 'legend'}
28
+ class={cn(
33
29
  'mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
34
- className
30
+ local.class
35
31
  )}
36
- {...props}
32
+ {...rest}
37
33
  />
38
34
  )
39
35
  }
40
36
 
41
- function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
37
+ function FieldGroup(props: ComponentProps<'div'>) {
38
+ const [local, rest] = splitProps(props, ['class'])
42
39
  return (
43
40
  <div
44
41
  data-slot="field-group"
45
- className={cn(
42
+ class={cn(
46
43
  'group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4',
47
- className
44
+ local.class
48
45
  )}
49
- {...props}
46
+ {...rest}
50
47
  />
51
48
  )
52
49
  }
@@ -66,147 +63,146 @@ const fieldVariants = cva('group/field flex w-full gap-2 data-[invalid=true]:tex
66
63
  },
67
64
  })
68
65
 
69
- function Field({
70
- className,
71
- orientation = 'vertical',
72
- ...props
73
- }: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
66
+ function Field(props: ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
67
+ const [local, rest] = splitProps(props, ['class', 'orientation'])
74
68
  return (
75
69
  <div
76
70
  role="group"
77
71
  data-slot="field"
78
- data-orientation={orientation}
79
- className={cn(fieldVariants({ orientation }), className)}
80
- {...props}
72
+ data-orientation={local.orientation ?? 'vertical'}
73
+ class={cn(fieldVariants({ orientation: local.orientation }), local.class)}
74
+ {...rest}
81
75
  />
82
76
  )
83
77
  }
84
78
 
85
- function FieldContent({ className, ...props }: React.ComponentProps<'div'>) {
79
+ function FieldContent(props: ComponentProps<'div'>) {
80
+ const [local, rest] = splitProps(props, ['class'])
86
81
  return (
87
82
  <div
88
83
  data-slot="field-content"
89
- className={cn('group/field-content flex flex-1 flex-col gap-0.5 leading-snug', className)}
90
- {...props}
84
+ class={cn('group/field-content flex flex-1 flex-col gap-0.5 leading-snug', local.class)}
85
+ {...rest}
91
86
  />
92
87
  )
93
88
  }
94
89
 
95
- function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
90
+ function FieldLabel(props: ComponentProps<typeof Label>) {
91
+ const [local, rest] = splitProps(props, ['class'])
96
92
  return (
97
93
  <Label
98
94
  data-slot="field-label"
99
- className={cn(
95
+ class={cn(
100
96
  'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10',
101
97
  'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
102
- className
98
+ local.class
103
99
  )}
104
- {...props}
100
+ {...rest}
105
101
  />
106
102
  )
107
103
  }
108
104
 
109
- function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
105
+ function FieldTitle(props: ComponentProps<'div'>) {
106
+ const [local, rest] = splitProps(props, ['class'])
110
107
  return (
111
108
  <div
112
109
  data-slot="field-label"
113
- className={cn(
110
+ class={cn(
114
111
  'flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50',
115
- className
112
+ local.class
116
113
  )}
117
- {...props}
114
+ {...rest}
118
115
  />
119
116
  )
120
117
  }
121
118
 
122
- function FieldDescription({ className, ...props }: React.ComponentProps<'p'>) {
119
+ function FieldDescription(props: ComponentProps<'p'>) {
120
+ const [local, rest] = splitProps(props, ['class'])
123
121
  return (
124
122
  <p
125
123
  data-slot="field-description"
126
- className={cn(
124
+ class={cn(
127
125
  'text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5',
128
126
  'last:mt-0 nth-last-2:-mt-1',
129
127
  '[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
130
- className
128
+ local.class
131
129
  )}
132
- {...props}
130
+ {...rest}
133
131
  />
134
132
  )
135
133
  }
136
134
 
137
- function FieldSeparator({
138
- children,
139
- className,
140
- ...props
141
- }: React.ComponentProps<'div'> & {
142
- children?: React.ReactNode
143
- }) {
135
+ function FieldSeparator(
136
+ props: ComponentProps<'div'> & {
137
+ children?: JSX.Element
138
+ }
139
+ ) {
140
+ const [local, rest] = splitProps(props, ['class', 'children'])
144
141
  return (
145
142
  <div
146
143
  data-slot="field-separator"
147
- data-content={!!children}
148
- className={cn(
144
+ data-content={!!local.children}
145
+ class={cn(
149
146
  'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
150
- className
147
+ local.class
151
148
  )}
152
- {...props}
149
+ {...rest}
153
150
  >
154
- <Separator className="absolute inset-0 top-1/2" />
155
- {children && (
151
+ <Separator class="absolute inset-0 top-1/2" />
152
+ <Show when={local.children}>
156
153
  <span
157
- className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
154
+ class="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
158
155
  data-slot="field-separator-content"
159
156
  >
160
- {children}
157
+ {local.children}
161
158
  </span>
162
- )}
159
+ </Show>
163
160
  </div>
164
161
  )
165
162
  }
166
163
 
167
- function FieldError({
168
- className,
169
- children,
170
- errors,
171
- ...props
172
- }: React.ComponentProps<'div'> & {
173
- errors?: Array<{ message?: string } | undefined>
174
- }) {
175
- const content = useMemo(() => {
176
- if (children) {
177
- return children
164
+ function FieldError(
165
+ props: ComponentProps<'div'> & {
166
+ errors?: Array<{ message?: string } | undefined>
167
+ }
168
+ ) {
169
+ const [local, rest] = splitProps(props, ['class', 'children', 'errors'])
170
+
171
+ const content = createMemo(() => {
172
+ if (local.children) {
173
+ return local.children
178
174
  }
179
175
 
176
+ const errors = local.errors
180
177
  if (!errors?.length) {
181
178
  return null
182
179
  }
183
180
 
184
181
  const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()]
185
182
 
186
- if (uniqueErrors?.length == 1) {
187
- return uniqueErrors[0]?.message
183
+ if (uniqueErrors.length === 1) {
184
+ const message = uniqueErrors[0]?.message
185
+ return message ? <span>{message}</span> : null
188
186
  }
189
187
 
190
188
  return (
191
- <ul className="ml-4 flex list-disc flex-col gap-1">
192
- {uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
189
+ <ul class="ml-4 flex list-disc flex-col gap-1">
190
+ {uniqueErrors.map((error) => error?.message && <li>{error.message}</li>)}
193
191
  </ul>
194
192
  )
195
- }, [children, errors])
196
-
197
- if (!content) {
198
- return null
199
- }
193
+ })
200
194
 
201
195
  return (
202
- <div
203
- role="alert"
204
- data-slot="field-error"
205
- className={cn('text-sm font-normal text-destructive', className)}
206
- {...props}
207
- >
208
- {content}
209
- </div>
196
+ <Show when={content()}>
197
+ <div
198
+ role="alert"
199
+ data-slot="field-error"
200
+ class={cn('text-sm font-normal text-destructive', local.class)}
201
+ {...rest}
202
+ >
203
+ {content()}
204
+ </div>
205
+ </Show>
210
206
  )
211
207
  }
212
208
 
@@ -1,18 +1,18 @@
1
- import * as React from 'react'
2
- import { Input as InputPrimitive } from '@base-ui/react/input'
1
+ import { splitProps, type ComponentProps } from 'solid-js'
3
2
 
4
3
  import { cn } from '#lib/utils'
5
4
 
6
- function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
5
+ function Input(props: ComponentProps<'input'>) {
6
+ const [local, rest] = splitProps(props, ['class', 'type'])
7
7
  return (
8
- <InputPrimitive
9
- type={type}
8
+ <input
9
+ type={local.type}
10
10
  data-slot="input"
11
- className={cn(
11
+ class={cn(
12
12
  'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
13
- className
13
+ local.class
14
14
  )}
15
- {...props}
15
+ {...rest}
16
16
  />
17
17
  )
18
18
  }
@@ -1,18 +1,17 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
1
+ import { splitProps, type ComponentProps } from 'solid-js'
4
2
 
5
3
  import { cn } from '#lib/utils'
6
4
 
7
- function Label({ className, ...props }: React.ComponentProps<'label'>) {
5
+ function Label(props: ComponentProps<'label'>) {
6
+ const [local, rest] = splitProps(props, ['class'])
8
7
  return (
9
8
  <label
10
9
  data-slot="label"
11
- className={cn(
10
+ class={cn(
12
11
  'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
13
- className
12
+ local.class
14
13
  )}
15
- {...props}
14
+ {...rest}
16
15
  />
17
16
  )
18
17
  }
@@ -1,37 +1,40 @@
1
- 'use client'
2
-
3
- import { Radio as RadioPrimitive } from '@base-ui/react/radio'
4
- import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group'
1
+ import { RadioGroup as RadioGroupPrimitive } from '@kobalte/core/radio-group'
2
+ import { splitProps, type ComponentProps } from 'solid-js'
5
3
 
6
4
  import { cn } from '#lib/utils'
7
5
 
8
- function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
6
+ function RadioGroup(props: ComponentProps<typeof RadioGroupPrimitive>) {
7
+ const [local, rest] = splitProps(props, ['class'])
9
8
  return (
10
9
  <RadioGroupPrimitive
11
10
  data-slot="radio-group"
12
- className={cn('grid w-full gap-2', className)}
13
- {...props}
11
+ class={cn('grid w-full gap-2', local.class)}
12
+ {...rest}
14
13
  />
15
14
  )
16
15
  }
17
16
 
18
- function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {
17
+ function RadioGroupItem(props: ComponentProps<typeof RadioGroupPrimitive.Item>) {
18
+ const [local, rest] = splitProps(props, ['class'])
19
19
  return (
20
- <RadioPrimitive.Root
20
+ <RadioGroupPrimitive.Item
21
21
  data-slot="radio-group-item"
22
- className={cn(
22
+ class={cn(
23
23
  'group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary',
24
- className
24
+ local.class
25
25
  )}
26
- {...props}
26
+ {...rest}
27
27
  >
28
- <RadioPrimitive.Indicator
29
- data-slot="radio-group-indicator"
30
- className="flex size-4 items-center justify-center"
31
- >
32
- <span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
33
- </RadioPrimitive.Indicator>
34
- </RadioPrimitive.Root>
28
+ <RadioGroupPrimitive.ItemInput />
29
+ <RadioGroupPrimitive.ItemControl class="flex size-4 items-center justify-center rounded-full">
30
+ <RadioGroupPrimitive.ItemIndicator
31
+ data-slot="radio-group-indicator"
32
+ class="flex size-4 items-center justify-center"
33
+ >
34
+ <span class="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
35
+ </RadioGroupPrimitive.ItemIndicator>
36
+ </RadioGroupPrimitive.ItemControl>
37
+ </RadioGroupPrimitive.Item>
35
38
  )
36
39
  }
37
40
 
@@ -1,19 +1,26 @@
1
- 'use client'
2
-
3
- import { Separator as SeparatorPrimitive } from '@base-ui/react/separator'
1
+ import { Separator as SeparatorPrimitive } from '@kobalte/core/separator'
2
+ import { splitProps, type ComponentProps } from 'solid-js'
4
3
 
5
4
  import { cn } from '#lib/utils'
6
5
 
7
- function Separator({ className, orientation = 'horizontal', ...props }: SeparatorPrimitive.Props) {
6
+ type SeparatorProps = ComponentProps<typeof SeparatorPrimitive> & {
7
+ orientation?: 'horizontal' | 'vertical'
8
+ }
9
+
10
+ function Separator(props: SeparatorProps) {
11
+ const [local, rest] = splitProps(props, ['class', 'orientation'])
12
+ const orientation = () => local.orientation ?? 'horizontal'
13
+
8
14
  return (
9
15
  <SeparatorPrimitive
10
16
  data-slot="separator"
11
- orientation={orientation}
12
- className={cn(
13
- 'shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch',
14
- className
17
+ orientation={orientation()}
18
+ class={cn(
19
+ 'shrink-0 bg-border',
20
+ orientation() === 'vertical' ? 'w-px self-stretch' : 'h-px w-full',
21
+ local.class
15
22
  )}
16
- {...props}
23
+ {...rest}
17
24
  />
18
25
  )
19
26
  }
@@ -1,11 +1,14 @@
1
+ import { splitProps, type ComponentProps } from 'solid-js'
2
+
1
3
  import { cn } from '#lib/utils'
2
4
 
3
- function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
5
+ function Skeleton(props: ComponentProps<'div'>) {
6
+ const [local, rest] = splitProps(props, ['class'])
4
7
  return (
5
8
  <div
6
9
  data-slot="skeleton"
7
- className={cn('animate-pulse rounded-md bg-muted', className)}
8
- {...props}
10
+ class={cn('animate-pulse rounded-md bg-muted', local.class)}
11
+ {...rest}
9
12
  />
10
13
  )
11
14
  }
@@ -1,14 +1,18 @@
1
- import { LoaderCircle } from 'lucide-react'
2
- import type { ComponentProps } from 'react'
1
+ import { LoaderCircle } from 'lucide-solid'
2
+ import { splitProps, type ComponentProps } from 'solid-js'
3
+
3
4
  import { cn } from '#lib/utils'
4
5
 
5
- function Spinner({ className, ...props }: ComponentProps<typeof LoaderCircle>) {
6
+ type SpinnerProps = ComponentProps<typeof LoaderCircle>
7
+
8
+ function Spinner(props: SpinnerProps) {
9
+ const [local, rest] = splitProps(props, ['class'])
6
10
  return (
7
11
  <LoaderCircle
8
12
  role="status"
9
13
  aria-label="Loading"
10
- className={cn('animate-spin', className)}
11
- {...props}
14
+ class={cn('animate-spin', local.class)}
15
+ {...rest}
12
16
  />
13
17
  )
14
18
  }
@@ -1,31 +1,58 @@
1
- 'use client'
2
-
3
- import { Switch as SwitchPrimitive } from '@base-ui/react/switch'
1
+ import { Switch as SwitchPrimitive } from '@kobalte/core/switch'
2
+ import { splitProps, type ComponentProps } from 'solid-js'
4
3
 
5
4
  import { cn } from '#lib/utils'
6
5
 
7
- function Switch({
8
- className,
9
- size = 'default',
10
- ...props
11
- }: SwitchPrimitive.Root.Props & {
6
+ type SwitchProps = ComponentProps<typeof SwitchPrimitive> & {
7
+ class?: string
12
8
  size?: 'sm' | 'default'
13
- }) {
9
+ }
10
+
11
+ function Switch(props: SwitchProps) {
12
+ const [local, rest] = splitProps(props, ['class', 'size', 'aria-label', 'aria-describedby'])
14
13
  return (
15
- <SwitchPrimitive.Root
14
+ <SwitchPrimitive
16
15
  data-slot="switch"
17
- data-size={size}
18
- className={cn(
19
- 'peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50',
20
- className
21
- )}
22
- {...props}
16
+ data-size={local.size ?? 'default'}
17
+ class={cn('peer group/switch relative inline-flex shrink-0 items-center', local.class)}
18
+ {...rest}
23
19
  >
24
- <SwitchPrimitive.Thumb
25
- data-slot="switch-thumb"
26
- className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
20
+ {/* The input is the real control: let it cover the visual track so
21
+ pointer and keyboard interaction (and automated checks) use it
22
+ directly, while the styled control stays the visible surface. */}
23
+ <SwitchPrimitive.Input
24
+ aria-label={local['aria-label']}
25
+ aria-describedby={local['aria-describedby']}
26
+ style={{
27
+ position: 'absolute',
28
+ inset: '0',
29
+ width: '100%',
30
+ height: '100%',
31
+ margin: '0',
32
+ padding: '0',
33
+ border: '0',
34
+ opacity: '0',
35
+ cursor: 'pointer',
36
+ // Undo the visually-hidden clipping so the input keeps a hit area
37
+ // over the visual track: the input is the control.
38
+ clip: 'auto',
39
+ 'clip-path': 'none',
40
+ overflow: 'visible',
41
+ 'white-space': 'normal',
42
+ 'z-index': '2',
43
+ }}
27
44
  />
28
- </SwitchPrimitive.Root>
45
+ <SwitchPrimitive.Control
46
+ class={cn(
47
+ 'inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 group-data-[size=default]/switch:h-[18.4px] group-data-[size=default]/switch:w-[32px] group-data-[size=sm]/switch:h-[14px] group-data-[size=sm]/switch:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 group-data-checked/switch:bg-primary group-not-data-checked/switch:bg-input dark:group-not-data-checked/switch:bg-input/80 group-data-disabled/switch:cursor-not-allowed group-data-disabled/switch:opacity-50'
48
+ )}
49
+ >
50
+ <SwitchPrimitive.Thumb
51
+ data-slot="switch-thumb"
52
+ class="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-checked/switch:translate-x-[calc(100%-2px)] dark:group-data-checked/switch:bg-primary-foreground dark:group-not-data-checked/switch:bg-foreground"
53
+ />
54
+ </SwitchPrimitive.Control>
55
+ </SwitchPrimitive>
29
56
  )
30
57
  }
31
58