@nikala-ui/core 0.10.1 → 0.11.0-nightly.b01fa01

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 (106) hide show
  1. package/package.json +1 -1
  2. package/registry/banner.json +1 -1
  3. package/registry/bubble.json +18 -0
  4. package/registry/button-group.json +20 -0
  5. package/registry/button.json +1 -1
  6. package/registry/checkbox.json +4 -1
  7. package/registry/combobox.json +1 -1
  8. package/registry/command.json +3 -2
  9. package/registry/context-menu.json +1 -1
  10. package/registry/create-app-updater.json +13 -0
  11. package/registry/create-chat-scroll.json +13 -0
  12. package/registry/create-document-tabs.json +13 -0
  13. package/registry/create-drop-zone.json +13 -0
  14. package/registry/create-global-shortcut.json +13 -0
  15. package/registry/create-pagination.json +13 -0
  16. package/registry/create-tauri-window.json +13 -0
  17. package/registry/dialog.json +1 -1
  18. package/registry/dropdown-menu.json +3 -2
  19. package/registry/dropzone.json +21 -0
  20. package/registry/field.json +1 -1
  21. package/registry/footer.json +18 -0
  22. package/registry/forgot-password-01.json +28 -0
  23. package/registry/form-message.json +3 -2
  24. package/registry/hero-01.json +22 -0
  25. package/registry/icon-button.json +1 -1
  26. package/registry/index.json +432 -5
  27. package/registry/login-01.json +28 -0
  28. package/registry/marker.json +17 -0
  29. package/registry/marquee.json +17 -0
  30. package/registry/message.json +17 -0
  31. package/registry/navbar.json +19 -0
  32. package/registry/navigation-menu.json +22 -0
  33. package/registry/number-input.json +1 -1
  34. package/registry/otp-verification-01.json +26 -0
  35. package/registry/pagination.json +22 -0
  36. package/registry/popover.json +4 -1
  37. package/registry/rating.json +19 -0
  38. package/registry/register-01.json +31 -0
  39. package/registry/resizable.json +1 -1
  40. package/registry/review-card.json +23 -0
  41. package/registry/scroll-area.json +1 -1
  42. package/registry/select.json +3 -2
  43. package/registry/sheet.json +1 -1
  44. package/registry/sidebar.json +24 -0
  45. package/registry/spinner.json +1 -1
  46. package/registry/stat.json +19 -0
  47. package/registry/status.json +1 -1
  48. package/registry/switch.json +4 -1
  49. package/registry/table.json +17 -0
  50. package/registry/tabs.json +4 -1
  51. package/registry/theme-manager.json +4 -3
  52. package/registry/timeline.json +18 -0
  53. package/registry/titlebar-tabs.json +24 -0
  54. package/registry/titlebar.json +26 -0
  55. package/registry/toggle-group.json +21 -0
  56. package/registry/updater-modal.json +26 -0
  57. package/src/registry/blocks/forgot-password-01.tsx +141 -0
  58. package/src/registry/blocks/hero-01.tsx +28 -0
  59. package/src/registry/blocks/login-01.tsx +231 -0
  60. package/src/registry/blocks/otp-verification-01.tsx +170 -0
  61. package/src/registry/blocks/register-01.tsx +318 -0
  62. package/src/registry/components/ui/banner.tsx +0 -2
  63. package/src/registry/components/ui/bubble.tsx +142 -0
  64. package/src/registry/components/ui/button-group.tsx +42 -0
  65. package/src/registry/components/ui/button.tsx +1 -1
  66. package/src/registry/components/ui/checkbox.tsx +1 -1
  67. package/src/registry/components/ui/combobox.tsx +83 -18
  68. package/src/registry/components/ui/command.tsx +101 -105
  69. package/src/registry/components/ui/context-menu.tsx +67 -22
  70. package/src/registry/components/ui/dialog.tsx +42 -32
  71. package/src/registry/components/ui/dropdown-menu.tsx +69 -31
  72. package/src/registry/components/ui/dropzone.tsx +189 -0
  73. package/src/registry/components/ui/field.tsx +7 -3
  74. package/src/registry/components/ui/footer.tsx +247 -0
  75. package/src/registry/components/ui/form-message.tsx +2 -2
  76. package/src/registry/components/ui/icon-button.tsx +1 -1
  77. package/src/registry/components/ui/marker.tsx +102 -0
  78. package/src/registry/components/ui/marquee.tsx +118 -0
  79. package/src/registry/components/ui/message.tsx +168 -0
  80. package/src/registry/components/ui/navbar.tsx +375 -0
  81. package/src/registry/components/ui/navigation-menu.tsx +358 -0
  82. package/src/registry/components/ui/number-input.tsx +3 -3
  83. package/src/registry/components/ui/pagination.tsx +258 -0
  84. package/src/registry/components/ui/popover.tsx +1 -1
  85. package/src/registry/components/ui/rating.tsx +185 -0
  86. package/src/registry/components/ui/resizable.tsx +3 -1
  87. package/src/registry/components/ui/review-card.tsx +195 -0
  88. package/src/registry/components/ui/scroll-area.tsx +13 -3
  89. package/src/registry/components/ui/select.tsx +16 -19
  90. package/src/registry/components/ui/sheet.tsx +58 -53
  91. package/src/registry/components/ui/sidebar.tsx +692 -0
  92. package/src/registry/components/ui/spinner.tsx +1 -1
  93. package/src/registry/components/ui/stat.tsx +245 -0
  94. package/src/registry/components/ui/status.tsx +7 -5
  95. package/src/registry/components/ui/switch.tsx +1 -1
  96. package/src/registry/components/ui/table.tsx +160 -0
  97. package/src/registry/components/ui/tabs.tsx +1 -1
  98. package/src/registry/components/ui/theme-toggle.tsx +80 -79
  99. package/src/registry/components/ui/timeline.tsx +350 -0
  100. package/src/registry/components/ui/titlebar-tabs.tsx +412 -0
  101. package/src/registry/components/ui/titlebar.tsx +399 -0
  102. package/src/registry/components/ui/toggle-group.tsx +203 -0
  103. package/src/registry/components/ui/updater-modal.tsx +254 -0
  104. package/src/registry/index.ts +3 -3
  105. package/src/registry/metadata.ts +184 -5
  106. package/src/registry/providers/theme-transitions.ts +81 -50
@@ -1,30 +1,29 @@
1
1
  import { splitProps, type Component, type JSX, type ValidComponent, Show } from "solid-js";
2
2
  import * as DialogPrimitive from "@kobalte/core/dialog";
3
3
  import type { PolymorphicProps } from "@kobalte/core/polymorphic";
4
- import { ScrollArea } from "./scroll-area";
4
+ import { ScrollArea } from "@/components/ui/scroll-area";
5
5
  import { cn } from "@/lib/cn";
6
6
 
7
7
  export type DialogRootProps = DialogPrimitive.DialogRootProps;
8
8
 
9
9
  /**
10
- * Root Dialog container managing state and context.
10
+ * Root Dialog component managing modal open state.
11
11
  */
12
12
  export const Dialog: Component<DialogRootProps> = (props) => {
13
13
  return <DialogPrimitive.Root {...props} />;
14
14
  };
15
15
 
16
16
  export const DialogTrigger = DialogPrimitive.Trigger;
17
- export const DialogClose = DialogPrimitive.CloseButton;
18
17
 
19
18
  export type DialogOverlayProps<T extends ValidComponent = "div"> =
20
19
  DialogPrimitive.DialogOverlayProps<T> & {
21
- /** Whether to apply background backdrop blur (default: true) */
20
+ /** Whether to apply background blur effect (default: true) */
22
21
  blur?: boolean;
23
22
  class?: string;
24
23
  };
25
24
 
26
25
  /**
27
- * Darkened background overlay behind open dialog.
26
+ * Backdrop overlay wrapper with optional backdrop blur styling.
28
27
  */
29
28
  export const DialogOverlay = <T extends ValidComponent = "div">(
30
29
  props: PolymorphicProps<T, DialogOverlayProps<T>>
@@ -34,8 +33,8 @@ export const DialogOverlay = <T extends ValidComponent = "div">(
34
33
  return (
35
34
  <DialogPrimitive.Overlay
36
35
  class={cn(
37
- "fixed inset-0 z-50 transition-all duration-200 data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
38
- local.blur !== false ? "bg-black/80 backdrop-blur-sm" : "bg-black/80",
36
+ "fixed inset-0 z-50 bg-black/80 data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
37
+ local.blur !== false && "backdrop-blur-sm",
39
38
  local.class
40
39
  )}
41
40
  {...(rest as any)}
@@ -124,6 +123,9 @@ export interface DialogHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {
124
123
  class?: string;
125
124
  }
126
125
 
126
+ /**
127
+ * Dialog top header container.
128
+ */
127
129
  export const DialogHeader: Component<DialogHeaderProps> = (props) => {
128
130
  const [local, rest] = splitProps(props, ["class"]);
129
131
 
@@ -139,52 +141,60 @@ export interface DialogFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
139
141
  class?: string;
140
142
  }
141
143
 
144
+ /**
145
+ * Dialog bottom action buttons area.
146
+ */
142
147
  export const DialogFooter: Component<DialogFooterProps> = (props) => {
143
148
  const [local, rest] = splitProps(props, ["class"]);
144
149
 
145
150
  return (
146
151
  <div
147
- class={cn(
148
- "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4",
149
- local.class
150
- )}
152
+ class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", local.class)}
151
153
  {...rest}
152
154
  />
153
155
  );
154
156
  };
155
157
 
156
- export interface DialogTitleProps {
157
- class?: string;
158
- children?: JSX.Element;
159
- }
158
+ export type DialogTitleProps<T extends ValidComponent = "h2"> =
159
+ DialogPrimitive.DialogTitleProps<T> & {
160
+ class?: string;
161
+ };
160
162
 
161
- export const DialogTitle: Component<DialogTitleProps> = (props) => {
162
- const [local, rest] = splitProps(props, ["class", "children"]);
163
+ /**
164
+ * Dialog title heading.
165
+ */
166
+ export const DialogTitle = <T extends ValidComponent = "h2">(
167
+ props: PolymorphicProps<T, DialogTitleProps<T>>
168
+ ) => {
169
+ const [local, rest] = splitProps(props as DialogTitleProps, ["class"]);
163
170
 
164
171
  return (
165
172
  <DialogPrimitive.Title
166
173
  class={cn("text-lg font-semibold leading-none tracking-tight text-foreground", local.class)}
167
- {...rest}
168
- >
169
- {local.children}
170
- </DialogPrimitive.Title>
174
+ {...(rest as any)}
175
+ />
171
176
  );
172
177
  };
173
178
 
174
- export interface DialogDescriptionProps {
175
- class?: string;
176
- children?: JSX.Element;
177
- }
179
+ export type DialogDescriptionProps<T extends ValidComponent = "p"> =
180
+ DialogPrimitive.DialogDescriptionProps<T> & {
181
+ class?: string;
182
+ };
178
183
 
179
- export const DialogDescription: Component<DialogDescriptionProps> = (props) => {
180
- const [local, rest] = splitProps(props, ["class", "children"]);
184
+ /**
185
+ * Dialog subtitle or description text.
186
+ */
187
+ export const DialogDescription = <T extends ValidComponent = "p">(
188
+ props: PolymorphicProps<T, DialogDescriptionProps<T>>
189
+ ) => {
190
+ const [local, rest] = splitProps(props as DialogDescriptionProps, ["class"]);
181
191
 
182
192
  return (
183
193
  <DialogPrimitive.Description
184
194
  class={cn("text-sm text-muted-foreground", local.class)}
185
- {...rest}
186
- >
187
- {local.children}
188
- </DialogPrimitive.Description>
195
+ {...(rest as any)}
196
+ />
189
197
  );
190
- };
198
+ };
199
+
200
+ export const DialogClose = DialogPrimitive.CloseButton;
@@ -1,10 +1,12 @@
1
- import { splitProps, type Component, type JSX, type ValidComponent } from "solid-js";
1
+ import { splitProps, type Component, type JSX, type ValidComponent, createContext, useContext } from "solid-js";
2
2
  import * as DropdownMenuPrimitive from "@kobalte/core/dropdown-menu";
3
3
  import type { PolymorphicProps } from "@kobalte/core/polymorphic";
4
- import { createClickOutside } from "@nikala-ui/hooks";
5
- import { ScrollArea } from "./scroll-area";
4
+ import { createClickOutside } from "@/hooks/create-click-outside";
5
+ import { ScrollArea } from "@/components/ui/scroll-area";
6
6
  import { cn } from "@/lib/cn";
7
7
 
8
+ const DropdownGroupContext = createContext<boolean>(false);
9
+
8
10
  export type DropdownMenuRootProps = Omit<
9
11
  DropdownMenuPrimitive.DropdownMenuRootProps,
10
12
  "placement"
@@ -29,7 +31,15 @@ export const DropdownMenu: Component<DropdownMenuRootProps> = (props) => {
29
31
  };
30
32
 
31
33
  export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
32
- export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
34
+
35
+ export const DropdownMenuGroup: Component<DropdownMenuPrimitive.DropdownMenuGroupProps> = (props) => {
36
+ return (
37
+ <DropdownGroupContext.Provider value={true}>
38
+ <DropdownMenuPrimitive.Group {...props} />
39
+ </DropdownGroupContext.Provider>
40
+ );
41
+ };
42
+
33
43
  export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
34
44
  export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
35
45
 
@@ -120,7 +130,7 @@ export const DropdownMenuContent = <T extends ValidComponent = "div">(
120
130
  local.class
121
131
  )}
122
132
  >
123
- <ScrollArea class="max-h-72 w-full rounded-[inherit]">
133
+ <ScrollArea class="max-h-72 w-full">
124
134
  <div class="p-1">
125
135
  {local.children}
126
136
  </div>
@@ -134,18 +144,25 @@ export type DropdownMenuItemProps<T extends ValidComponent = "div"> =
134
144
  DropdownMenuPrimitive.DropdownMenuItemProps<T> & {
135
145
  class?: string;
136
146
  inset?: boolean;
147
+ variant?: "default" | "destructive";
137
148
  };
138
149
 
139
150
  export const DropdownMenuItem = <T extends ValidComponent = "div">(
140
151
  props: PolymorphicProps<T, DropdownMenuItemProps<T>>
141
152
  ) => {
142
- const [local, rest] = splitProps(props as DropdownMenuItemProps, ["class", "inset"]);
153
+ const [local, rest] = splitProps(props as DropdownMenuItemProps, [
154
+ "class",
155
+ "inset",
156
+ "variant",
157
+ ]);
143
158
 
144
159
  return (
145
160
  <DropdownMenuPrimitive.Item
146
161
  class={cn(
147
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 text-foreground",
162
+ "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
148
163
  local.inset && "pl-8",
164
+ local.variant === "destructive" &&
165
+ "text-destructive focus:bg-destructive/15 focus:text-destructive",
149
166
  local.class
150
167
  )}
151
168
  {...(rest as any)}
@@ -157,26 +174,24 @@ export type DropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> =
157
174
  DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & {
158
175
  class?: string;
159
176
  children?: JSX.Element;
160
- checked?: boolean;
161
177
  };
162
178
 
163
179
  export const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">(
164
180
  props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>>
165
181
  ) => {
166
- const [local, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children", "checked"]);
182
+ const [local, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children"]);
167
183
 
168
184
  return (
169
185
  <DropdownMenuPrimitive.CheckboxItem
170
- checked={local.checked}
171
186
  class={cn(
172
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 text-foreground",
187
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
173
188
  local.class
174
189
  )}
175
190
  {...(rest as any)}
176
191
  >
177
192
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
178
193
  <DropdownMenuPrimitive.ItemIndicator>
179
- <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
194
+ <svg class="h-4 w-4 fill-none stroke-current stroke-2" viewBox="0 0 24 24">
180
195
  <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
181
196
  </svg>
182
197
  </DropdownMenuPrimitive.ItemIndicator>
@@ -200,16 +215,14 @@ export const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
200
215
  return (
201
216
  <DropdownMenuPrimitive.RadioItem
202
217
  class={cn(
203
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 text-foreground",
218
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
204
219
  local.class
205
220
  )}
206
221
  {...(rest as any)}
207
222
  >
208
223
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
209
224
  <DropdownMenuPrimitive.ItemIndicator>
210
- <svg class="h-2 w-2 fill-current" viewBox="0 0 8 8">
211
- <circle cx="4" cy="4" r="3" />
212
- </svg>
225
+ <span class="h-2 w-2 rounded-lg bg-current" />
213
226
  </DropdownMenuPrimitive.ItemIndicator>
214
227
  </span>
215
228
  {local.children}
@@ -217,7 +230,9 @@ export const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
217
230
  );
218
231
  };
219
232
 
220
- export interface DropdownMenuLabelProps {
233
+ export const DropdownMenuGroupLabel = DropdownMenuPrimitive.GroupLabel;
234
+
235
+ export interface DropdownMenuLabelProps extends JSX.HTMLAttributes<HTMLDivElement> {
221
236
  class?: string;
222
237
  inset?: boolean;
223
238
  children?: JSX.Element;
@@ -225,36 +240,59 @@ export interface DropdownMenuLabelProps {
225
240
 
226
241
  export const DropdownMenuLabel: Component<DropdownMenuLabelProps> = (props) => {
227
242
  const [local, rest] = splitProps(props, ["class", "inset", "children"]);
243
+ const isInsideGroup = useContext(DropdownGroupContext);
244
+
245
+ const classes = () =>
246
+ cn(
247
+ "px-2 py-1.5 text-sm font-semibold text-foreground",
248
+ local.inset && "pl-8",
249
+ local.class
250
+ );
228
251
 
229
252
  return (
230
- <DropdownMenuPrimitive.GroupLabel
231
- class={cn("px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class)}
232
- {...rest}
233
- >
234
- {local.children}
235
- </DropdownMenuPrimitive.GroupLabel>
253
+ <>
254
+ {isInsideGroup ? (
255
+ <DropdownMenuPrimitive.GroupLabel class={classes()} {...(rest as any)}>
256
+ {local.children}
257
+ </DropdownMenuPrimitive.GroupLabel>
258
+ ) : (
259
+ <div class={classes()} {...rest}>
260
+ {local.children}
261
+ </div>
262
+ )}
263
+ </>
236
264
  );
237
265
  };
238
266
 
239
- export const DropdownMenuSeparator: Component<{ class?: string }> = (props) => {
267
+ export type DropdownMenuSeparatorProps<T extends ValidComponent = "hr"> =
268
+ DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & {
269
+ class?: string;
270
+ };
271
+
272
+ export const DropdownMenuSeparator = <T extends ValidComponent = "hr">(
273
+ props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>>
274
+ ) => {
275
+ const [local, rest] = splitProps(props as DropdownMenuSeparatorProps, ["class"]);
276
+
240
277
  return (
241
278
  <DropdownMenuPrimitive.Separator
242
- class={cn("-mx-1 my-1 h-px bg-border", props.class)}
279
+ class={cn("-mx-1 my-1 h-px bg-border", local.class)}
280
+ {...(rest as any)}
243
281
  />
244
282
  );
245
283
  };
246
284
 
247
- export interface DropdownMenuShortcutProps {
285
+ export interface DropdownMenuShortcutProps extends JSX.HTMLAttributes<HTMLSpanElement> {
248
286
  class?: string;
249
- children?: JSX.Element;
250
287
  }
251
288
 
252
289
  export const DropdownMenuShortcut: Component<DropdownMenuShortcutProps> = (props) => {
253
- const [local, rest] = splitProps(props, ["class", "children"]);
290
+ const [local, rest] = splitProps(props, ["class"]);
254
291
 
255
292
  return (
256
- <span class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)} {...rest}>
257
- {local.children}
258
- </span>
293
+ <span
294
+ class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)}
295
+ {...rest}
296
+ />
259
297
  );
260
298
  };
@@ -0,0 +1,189 @@
1
+ import { splitProps, type Component, type JSX } from "solid-js";
2
+ import { cn } from "@/lib/cn";
3
+ import { CloudUpload, FileText, X, AlertCircle } from "lucide-solid";
4
+
5
+ export interface DropzoneProps extends JSX.HTMLAttributes<HTMLDivElement> {
6
+ class?: string;
7
+ isOver?: boolean;
8
+ disabled?: boolean;
9
+ }
10
+
11
+ /**
12
+ * Root container for the Dropzone file upload component.
13
+ */
14
+ export const Dropzone: Component<DropzoneProps> = (props) => {
15
+ const [local, rest] = splitProps(props, ["class", "isOver", "disabled"]);
16
+
17
+ return (
18
+ <div
19
+ class={cn(
20
+ "group relative flex w-full flex-col items-center justify-center rounded-lg border-2 border-dashed border-border bg-card/50 p-8 text-center transition-all",
21
+ "hover:border-primary/50 hover:bg-card/80",
22
+ local.isOver && "border-primary bg-primary/5 ring-2 ring-primary/20",
23
+ local.disabled && "pointer-events-none opacity-50",
24
+ local.class
25
+ )}
26
+ {...rest}
27
+ />
28
+ );
29
+ };
30
+
31
+ export interface DropzoneIconProps extends JSX.HTMLAttributes<HTMLDivElement> {
32
+ class?: string;
33
+ }
34
+
35
+ /**
36
+ * Centered icon placeholder for Dropzone.
37
+ */
38
+ export const DropzoneIcon: Component<DropzoneIconProps> = (props) => {
39
+ const [local, rest] = splitProps(props, ["class", "children"]);
40
+
41
+ return (
42
+ <div
43
+ class={cn(
44
+ "mb-3 flex size-12 items-center justify-center rounded-lg bg-primary/10 text-primary transition-transform group-hover:scale-105",
45
+ local.class
46
+ )}
47
+ {...rest}
48
+ >
49
+ {local.children || <CloudUpload class="size-6" />}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export interface DropzoneTitleProps extends JSX.HTMLAttributes<HTMLHeadingElement> {
55
+ class?: string;
56
+ }
57
+
58
+ /**
59
+ * Primary title text for the dropzone prompt.
60
+ */
61
+ export const DropzoneTitle: Component<DropzoneTitleProps> = (props) => {
62
+ const [local, rest] = splitProps(props, ["class"]);
63
+
64
+ return (
65
+ <h4
66
+ class={cn("text-sm font-semibold tracking-tight text-foreground", local.class)}
67
+ {...rest}
68
+ />
69
+ );
70
+ };
71
+
72
+ export interface DropzoneDescriptionProps extends JSX.HTMLAttributes<HTMLParagraphElement> {
73
+ class?: string;
74
+ }
75
+
76
+ /**
77
+ * Subtitle description text for dropzone file specifications.
78
+ */
79
+ export const DropzoneDescription: Component<DropzoneDescriptionProps> = (props) => {
80
+ const [local, rest] = splitProps(props, ["class"]);
81
+
82
+ return (
83
+ <p
84
+ class={cn("mt-1 text-xs text-muted-foreground", local.class)}
85
+ {...rest}
86
+ />
87
+ );
88
+ };
89
+
90
+ export interface DropzoneFileListProps extends JSX.HTMLAttributes<HTMLDivElement> {
91
+ class?: string;
92
+ }
93
+
94
+ /**
95
+ * Container list for uploaded files.
96
+ */
97
+ export const DropzoneFileList: Component<DropzoneFileListProps> = (props) => {
98
+ const [local, rest] = splitProps(props, ["class"]);
99
+
100
+ return (
101
+ <div
102
+ class={cn("mt-4 flex w-full flex-col gap-2", local.class)}
103
+ {...rest}
104
+ />
105
+ );
106
+ };
107
+
108
+ export interface DropzoneFileItemProps extends JSX.HTMLAttributes<HTMLDivElement> {
109
+ class?: string;
110
+ name: string;
111
+ size?: string;
112
+ onRemove?: () => void;
113
+ }
114
+
115
+ /**
116
+ * Individual uploaded file card with name, formatted size, and remove button.
117
+ */
118
+ export const DropzoneFileItem: Component<DropzoneFileItemProps> = (props) => {
119
+ const [local, rest] = splitProps(props, ["class", "name", "size", "onRemove", "children"]);
120
+
121
+ return (
122
+ <div
123
+ class={cn(
124
+ "flex items-center justify-between gap-3 rounded-md border border-border bg-card p-2.5 text-xs transition-colors",
125
+ local.class
126
+ )}
127
+ {...rest}
128
+ >
129
+ <div class="flex min-w-0 items-center gap-2.5">
130
+ <div class="flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
131
+ {local.children || <FileText class="size-4" />}
132
+ </div>
133
+ <div class="flex min-w-0 flex-col text-left">
134
+ <span class="truncate font-medium text-foreground">{local.name}</span>
135
+ {local.size && (
136
+ <span class="font-mono text-[11px] text-muted-foreground">{local.size}</span>
137
+ )}
138
+ </div>
139
+ </div>
140
+
141
+ {local.onRemove && (
142
+ <button
143
+ type="button"
144
+ onClick={(e) => {
145
+ e.stopPropagation();
146
+ local.onRemove?.();
147
+ }}
148
+ class="flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive"
149
+ aria-label="Remove file"
150
+ >
151
+ <X class="size-3.5" />
152
+ </button>
153
+ )}
154
+ </div>
155
+ );
156
+ };
157
+
158
+ export interface DropzoneRejectedItemProps extends JSX.HTMLAttributes<HTMLDivElement> {
159
+ class?: string;
160
+ name: string;
161
+ error?: string;
162
+ size?: string;
163
+ }
164
+
165
+ /**
166
+ * Card for displaying rejected files and validation errors.
167
+ */
168
+ export const DropzoneRejectedItem: Component<DropzoneRejectedItemProps> = (props) => {
169
+ const [local, rest] = splitProps(props, ["class", "name", "error", "size"]);
170
+
171
+ return (
172
+ <div
173
+ class={cn(
174
+ "flex items-center justify-between gap-3 rounded-md border border-destructive/30 bg-destructive/10 p-2.5 text-xs text-destructive",
175
+ local.class
176
+ )}
177
+ {...rest}
178
+ >
179
+ <div class="flex min-w-0 items-center gap-2">
180
+ <AlertCircle class="size-4 shrink-0" />
181
+ <span class="truncate font-medium">{local.name}</span>
182
+ {local.size && <span class="font-mono text-[11px] opacity-80">({local.size})</span>}
183
+ </div>
184
+ {local.error && (
185
+ <span class="shrink-0 text-[11px] font-medium">{local.error}</span>
186
+ )}
187
+ </div>
188
+ );
189
+ };
@@ -1,6 +1,6 @@
1
1
  import { splitProps, type Component, type JSX } from "solid-js";
2
2
  import { cn } from "@/lib/cn";
3
- import { Label, type LabelProps } from "./label";
3
+ import { Label, type LabelProps } from "@/components/ui/label";
4
4
 
5
5
  export interface FieldProps extends JSX.HTMLAttributes<HTMLDivElement> {
6
6
  class?: string;
@@ -17,8 +17,9 @@ export const Field: Component<FieldProps> = (props) => {
17
17
  );
18
18
  };
19
19
 
20
- export interface FieldLabelProps extends LabelProps {
20
+ export interface FieldLabelProps extends JSX.LabelHTMLAttributes<HTMLLabelElement>, LabelProps {
21
21
  class?: string;
22
+ for?: string;
22
23
  children?: JSX.Element;
23
24
  }
24
25
 
@@ -41,7 +42,10 @@ export const FieldDescription: Component<FieldDescriptionProps> = (props) => {
41
42
  const [local, rest] = splitProps(props, ["class", "children"]);
42
43
 
43
44
  return (
44
- <p class={cn("text-sm text-muted-foreground", local.class)} {...rest}>
45
+ <p
46
+ class={cn("text-sm text-muted-foreground", local.class)}
47
+ {...rest}
48
+ >
45
49
  {local.children}
46
50
  </p>
47
51
  );