@nikala-ui/core 0.9.11 → 0.10.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 (48) hide show
  1. package/package.json +1 -1
  2. package/registry/aspect-ratio.json +17 -0
  3. package/registry/button.json +4 -1
  4. package/registry/collapsible.json +18 -0
  5. package/registry/combobox.json +4 -1
  6. package/registry/command.json +3 -2
  7. package/registry/context-menu.json +24 -0
  8. package/registry/create-form.json +1 -1
  9. package/registry/dialog.json +4 -1
  10. package/registry/dropdown-menu.json +4 -1
  11. package/registry/empty.json +17 -0
  12. package/registry/field.json +20 -0
  13. package/registry/form-message.json +16 -0
  14. package/registry/form.json +17 -0
  15. package/registry/icon-button.json +20 -0
  16. package/registry/index.json +191 -1
  17. package/registry/number-input.json +24 -0
  18. package/registry/resizable.json +21 -0
  19. package/registry/scroll-area.json +21 -0
  20. package/registry/select.json +4 -1
  21. package/registry/sheet.json +4 -1
  22. package/registry/spinner.json +18 -0
  23. package/registry/status.json +18 -0
  24. package/registry/theme-manager.json +1 -1
  25. package/registry/toggle.json +18 -0
  26. package/src/registry/components/ui/aspect-ratio.tsx +31 -0
  27. package/src/registry/components/ui/button.tsx +18 -3
  28. package/src/registry/components/ui/collapsible.tsx +73 -0
  29. package/src/registry/components/ui/combobox.tsx +23 -84
  30. package/src/registry/components/ui/command.tsx +154 -66
  31. package/src/registry/components/ui/context-menu.tsx +192 -0
  32. package/src/registry/components/ui/dialog.tsx +39 -44
  33. package/src/registry/components/ui/dropdown-menu.tsx +40 -45
  34. package/src/registry/components/ui/empty.tsx +83 -0
  35. package/src/registry/components/ui/field.tsx +67 -0
  36. package/src/registry/components/ui/form-message.tsx +36 -0
  37. package/src/registry/components/ui/form.tsx +21 -0
  38. package/src/registry/components/ui/icon-button.tsx +34 -0
  39. package/src/registry/components/ui/number-input.tsx +150 -0
  40. package/src/registry/components/ui/resizable.tsx +193 -0
  41. package/src/registry/components/ui/scroll-area.tsx +218 -0
  42. package/src/registry/components/ui/select.tsx +22 -16
  43. package/src/registry/components/ui/sheet.tsx +62 -63
  44. package/src/registry/components/ui/spinner.tsx +41 -0
  45. package/src/registry/components/ui/status.tsx +119 -0
  46. package/src/registry/components/ui/theme-toggle.tsx +6 -4
  47. package/src/registry/components/ui/toggle.tsx +101 -0
  48. package/src/registry/metadata.ts +84 -2
@@ -0,0 +1,192 @@
1
+ import { splitProps, type Component, type JSX, type ValidComponent } from "solid-js";
2
+ import * as ContextMenuPrimitive from "@kobalte/core/context-menu";
3
+ import type { PolymorphicProps } from "@kobalte/core/polymorphic";
4
+ import { createClickOutside } from "@nikala-ui/hooks";
5
+ import { ScrollArea } from "./scroll-area";
6
+ import { Separator } from "./separator";
7
+ import { Kbd } from "./kbd";
8
+ import { cn } from "@/lib/cn";
9
+
10
+ export type ContextMenuRootProps = ContextMenuPrimitive.ContextMenuRootProps;
11
+
12
+ export const ContextMenu: Component<ContextMenuRootProps> = (props) => {
13
+ return <ContextMenuPrimitive.Root {...props} />;
14
+ };
15
+
16
+ export const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
17
+ export const ContextMenuGroup = ContextMenuPrimitive.Group;
18
+ export const ContextMenuSub = ContextMenuPrimitive.Sub;
19
+
20
+ export type ContextMenuSubTriggerProps<T extends ValidComponent = "div"> =
21
+ ContextMenuPrimitive.ContextMenuSubTriggerProps<T> & {
22
+ class?: string;
23
+ children?: JSX.Element;
24
+ inset?: boolean;
25
+ };
26
+
27
+ export const ContextMenuSubTrigger = <T extends ValidComponent = "div">(
28
+ props: PolymorphicProps<T, ContextMenuSubTriggerProps<T>>
29
+ ) => {
30
+ const [local, rest] = splitProps(props as ContextMenuSubTriggerProps, ["class", "children", "inset"]);
31
+
32
+ return (
33
+ <ContextMenuPrimitive.SubTrigger
34
+ class={cn(
35
+ "flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground text-foreground",
36
+ local.inset && "pl-8",
37
+ local.class
38
+ )}
39
+ {...(rest as any)}
40
+ >
41
+ {local.children}
42
+ <svg class="ml-auto h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
43
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
44
+ </svg>
45
+ </ContextMenuPrimitive.SubTrigger>
46
+ );
47
+ };
48
+
49
+ export type ContextMenuSubContentProps<T extends ValidComponent = "div"> =
50
+ ContextMenuPrimitive.ContextMenuSubContentProps<T> & {
51
+ class?: string;
52
+ };
53
+
54
+ export const ContextMenuSubContent = <T extends ValidComponent = "div">(
55
+ props: PolymorphicProps<T, ContextMenuSubContentProps<T>>
56
+ ) => {
57
+ const [local, rest] = splitProps(props as ContextMenuSubContentProps, ["class"]);
58
+
59
+ return (
60
+ <ContextMenuPrimitive.Portal>
61
+ <ContextMenuPrimitive.SubContent
62
+ class={cn(
63
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md transition-all animate-in fade-in-80 slide-in-from-top-1",
64
+ local.class
65
+ )}
66
+ {...(rest as any)}
67
+ />
68
+ </ContextMenuPrimitive.Portal>
69
+ );
70
+ };
71
+
72
+ export type ContextMenuContentProps<T extends ValidComponent = "div"> =
73
+ ContextMenuPrimitive.ContextMenuContentProps<T> & {
74
+ class?: string;
75
+ ref?: (el: HTMLElement) => void;
76
+ children?: JSX.Element;
77
+ };
78
+
79
+ export const ContextMenuContent = <T extends ValidComponent = "div">(
80
+ props: PolymorphicProps<T, ContextMenuContentProps<T>>
81
+ ) => {
82
+ const [local, rest] = splitProps(props as ContextMenuContentProps, ["class", "ref", "children"]);
83
+ let menuRef: HTMLElement | undefined;
84
+
85
+ createClickOutside({
86
+ target: () => menuRef,
87
+ onInteractOutside: () => {
88
+ // Automatic portal dismiss on outside click
89
+ },
90
+ });
91
+
92
+ return (
93
+ <ContextMenuPrimitive.Portal>
94
+ <ContextMenuPrimitive.Content
95
+ ref={(el) => {
96
+ menuRef = el;
97
+ if (typeof local.ref === "function") local.ref(el);
98
+ }}
99
+ class={cn(
100
+ "z-50 min-w-[8rem] rounded-md border border-border bg-popover text-popover-foreground shadow-md transition-all animate-in fade-in-80 slide-in-from-top-1 max-h-72 flex flex-col",
101
+ local.class
102
+ )}
103
+ {...(rest as any)}
104
+ >
105
+ <ScrollArea class="max-h-72 w-full rounded-[inherit]">
106
+ <div class="p-1">
107
+ {local.children}
108
+ </div>
109
+ </ScrollArea>
110
+ </ContextMenuPrimitive.Content>
111
+ </ContextMenuPrimitive.Portal>
112
+ );
113
+ };
114
+
115
+ export type ContextMenuItemProps<T extends ValidComponent = "div"> =
116
+ ContextMenuPrimitive.ContextMenuItemProps<T> & {
117
+ class?: string;
118
+ inset?: boolean;
119
+ };
120
+
121
+ export const ContextMenuItem = <T extends ValidComponent = "div">(
122
+ props: PolymorphicProps<T, ContextMenuItemProps<T>>
123
+ ) => {
124
+ const [local, rest] = splitProps(props as ContextMenuItemProps, ["class", "inset"]);
125
+
126
+ return (
127
+ <ContextMenuPrimitive.Item
128
+ class={cn(
129
+ "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 text-foreground",
130
+ local.inset && "pl-8",
131
+ local.class
132
+ )}
133
+ {...(rest as any)}
134
+ />
135
+ );
136
+ };
137
+
138
+ export type ContextMenuCheckboxItemProps<T extends ValidComponent = "div"> =
139
+ ContextMenuPrimitive.ContextMenuCheckboxItemProps<T> & {
140
+ class?: string;
141
+ children?: JSX.Element;
142
+ checked?: boolean;
143
+ };
144
+
145
+ export const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(
146
+ props: PolymorphicProps<T, ContextMenuCheckboxItemProps<T>>
147
+ ) => {
148
+ const [local, rest] = splitProps(props as ContextMenuCheckboxItemProps, ["class", "children", "checked"]);
149
+
150
+ return (
151
+ <ContextMenuPrimitive.CheckboxItem
152
+ checked={local.checked}
153
+ class={cn(
154
+ "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 text-foreground",
155
+ local.class
156
+ )}
157
+ {...(rest as any)}
158
+ >
159
+ <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
160
+ <ContextMenuPrimitive.ItemIndicator>
161
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
162
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
163
+ </svg>
164
+ </ContextMenuPrimitive.ItemIndicator>
165
+ </span>
166
+ {local.children}
167
+ </ContextMenuPrimitive.CheckboxItem>
168
+ );
169
+ };
170
+
171
+ export interface ContextMenuShortcutProps extends JSX.HTMLAttributes<HTMLSpanElement> {
172
+ class?: string;
173
+ children?: JSX.Element;
174
+ }
175
+
176
+ export const ContextMenuShortcut: Component<ContextMenuShortcutProps> = (props) => {
177
+ const [local, rest] = splitProps(props, ["class", "children"]);
178
+
179
+ return (
180
+ <span class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)} {...rest}>
181
+ <Kbd size="sm">{local.children}</Kbd>
182
+ </span>
183
+ );
184
+ };
185
+
186
+ export interface ContextMenuSeparatorProps {
187
+ class?: string;
188
+ }
189
+
190
+ export const ContextMenuSeparator: Component<ContextMenuSeparatorProps> = (props) => {
191
+ return <Separator class={cn("-mx-1 my-1 h-px bg-border", props.class)} />;
192
+ };
@@ -1,28 +1,30 @@
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
5
  import { cn } from "@/lib/cn";
5
6
 
6
7
  export type DialogRootProps = DialogPrimitive.DialogRootProps;
7
8
 
8
9
  /**
9
- * Root Dialog component managing modal open state.
10
+ * Root Dialog container managing state and context.
10
11
  */
11
12
  export const Dialog: Component<DialogRootProps> = (props) => {
12
13
  return <DialogPrimitive.Root {...props} />;
13
14
  };
14
15
 
15
16
  export const DialogTrigger = DialogPrimitive.Trigger;
17
+ export const DialogClose = DialogPrimitive.CloseButton;
16
18
 
17
19
  export type DialogOverlayProps<T extends ValidComponent = "div"> =
18
20
  DialogPrimitive.DialogOverlayProps<T> & {
19
- /** Whether to apply background blur effect (default: true) */
21
+ /** Whether to apply background backdrop blur (default: true) */
20
22
  blur?: boolean;
21
23
  class?: string;
22
24
  };
23
25
 
24
26
  /**
25
- * Backdrop overlay wrapper with optional backdrop blur styling.
27
+ * Darkened background overlay behind open dialog.
26
28
  */
27
29
  export const DialogOverlay = <T extends ValidComponent = "div">(
28
30
  props: PolymorphicProps<T, DialogOverlayProps<T>>
@@ -32,8 +34,8 @@ export const DialogOverlay = <T extends ValidComponent = "div">(
32
34
  return (
33
35
  <DialogPrimitive.Overlay
34
36
  class={cn(
35
- "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",
36
- local.blur !== false && "backdrop-blur-sm",
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",
37
39
  local.class
38
40
  )}
39
41
  {...(rest as any)}
@@ -95,14 +97,18 @@ export const DialogContent = <T extends ValidComponent = "div">(
95
97
  onPointerDownOutside={handlePointerDownOutside}
96
98
  onInteractOutside={handleInteractOutside}
97
99
  class={cn(
98
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-popover p-6 shadow-lg duration-200 data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-closed:zoom-out-95 data-expanded:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg text-card-foreground",
100
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] border border-border bg-popover shadow-lg duration-200 data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-closed:zoom-out-95 data-expanded:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg text-card-foreground overflow-hidden max-h-[80vh]",
99
101
  local.class
100
102
  )}
101
103
  {...(rest as any)}
102
104
  >
103
- {local.children}
105
+ <ScrollArea class="max-h-[80vh] w-full">
106
+ <div class="p-6 space-y-4">
107
+ {local.children}
108
+ </div>
109
+ </ScrollArea>
104
110
  <Show when={local.showCloseButton !== false}>
105
- <DialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-expanded:bg-accent data-expanded:text-muted-foreground cursor-pointer">
111
+ <DialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-expanded:bg-accent data-expanded:text-muted-foreground cursor-pointer z-50">
106
112
  <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
107
113
  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
108
114
  </svg>
@@ -118,9 +124,6 @@ export interface DialogHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {
118
124
  class?: string;
119
125
  }
120
126
 
121
- /**
122
- * Dialog top header container.
123
- */
124
127
  export const DialogHeader: Component<DialogHeaderProps> = (props) => {
125
128
  const [local, rest] = splitProps(props, ["class"]);
126
129
 
@@ -136,60 +139,52 @@ export interface DialogFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
136
139
  class?: string;
137
140
  }
138
141
 
139
- /**
140
- * Dialog bottom action buttons area.
141
- */
142
142
  export const DialogFooter: Component<DialogFooterProps> = (props) => {
143
143
  const [local, rest] = splitProps(props, ["class"]);
144
144
 
145
145
  return (
146
146
  <div
147
- class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", local.class)}
147
+ class={cn(
148
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4",
149
+ local.class
150
+ )}
148
151
  {...rest}
149
152
  />
150
153
  );
151
154
  };
152
155
 
153
- export type DialogTitleProps<T extends ValidComponent = "h2"> =
154
- DialogPrimitive.DialogTitleProps<T> & {
155
- class?: string;
156
- };
156
+ export interface DialogTitleProps {
157
+ class?: string;
158
+ children?: JSX.Element;
159
+ }
157
160
 
158
- /**
159
- * Dialog title heading.
160
- */
161
- export const DialogTitle = <T extends ValidComponent = "h2">(
162
- props: PolymorphicProps<T, DialogTitleProps<T>>
163
- ) => {
164
- const [local, rest] = splitProps(props as DialogTitleProps, ["class"]);
161
+ export const DialogTitle: Component<DialogTitleProps> = (props) => {
162
+ const [local, rest] = splitProps(props, ["class", "children"]);
165
163
 
166
164
  return (
167
165
  <DialogPrimitive.Title
168
166
  class={cn("text-lg font-semibold leading-none tracking-tight text-foreground", local.class)}
169
- {...(rest as any)}
170
- />
167
+ {...rest}
168
+ >
169
+ {local.children}
170
+ </DialogPrimitive.Title>
171
171
  );
172
172
  };
173
173
 
174
- export type DialogDescriptionProps<T extends ValidComponent = "p"> =
175
- DialogPrimitive.DialogDescriptionProps<T> & {
176
- class?: string;
177
- };
174
+ export interface DialogDescriptionProps {
175
+ class?: string;
176
+ children?: JSX.Element;
177
+ }
178
178
 
179
- /**
180
- * Dialog subtitle or description text.
181
- */
182
- export const DialogDescription = <T extends ValidComponent = "p">(
183
- props: PolymorphicProps<T, DialogDescriptionProps<T>>
184
- ) => {
185
- const [local, rest] = splitProps(props as DialogDescriptionProps, ["class"]);
179
+ export const DialogDescription: Component<DialogDescriptionProps> = (props) => {
180
+ const [local, rest] = splitProps(props, ["class", "children"]);
186
181
 
187
182
  return (
188
183
  <DialogPrimitive.Description
189
184
  class={cn("text-sm text-muted-foreground", local.class)}
190
- {...(rest as any)}
191
- />
185
+ {...rest}
186
+ >
187
+ {local.children}
188
+ </DialogPrimitive.Description>
192
189
  );
193
- };
194
-
195
- export const DialogClose = DialogPrimitive.CloseButton;
190
+ };
@@ -2,6 +2,7 @@ import { splitProps, type Component, type JSX, type ValidComponent } from "solid
2
2
  import * as DropdownMenuPrimitive from "@kobalte/core/dropdown-menu";
3
3
  import type { PolymorphicProps } from "@kobalte/core/polymorphic";
4
4
  import { createClickOutside } from "@nikala-ui/hooks";
5
+ import { ScrollArea } from "./scroll-area";
5
6
  import { cn } from "@/lib/cn";
6
7
 
7
8
  export type DropdownMenuRootProps = Omit<
@@ -87,12 +88,13 @@ export const DropdownMenuSubContent = <T extends ValidComponent = "div">(
87
88
  export type DropdownMenuContentProps<T extends ValidComponent = "div"> =
88
89
  DropdownMenuPrimitive.DropdownMenuContentProps<T> & {
89
90
  class?: string;
91
+ children?: JSX.Element;
90
92
  };
91
93
 
92
94
  export const DropdownMenuContent = <T extends ValidComponent = "div">(
93
95
  props: PolymorphicProps<T, DropdownMenuContentProps<T>>
94
96
  ) => {
95
- const [local, rest] = splitProps(props as DropdownMenuContentProps, ["class"]);
97
+ const [local, rest] = splitProps(props as DropdownMenuContentProps, ["class", "children"]);
96
98
  let contentRef: HTMLElement | undefined;
97
99
 
98
100
  createClickOutside({
@@ -111,12 +113,19 @@ export const DropdownMenuContent = <T extends ValidComponent = "div">(
111
113
  contentRef = el;
112
114
  if (typeof (props as any).ref === "function") (props as any).ref(el);
113
115
  }}
116
+ {...(rest as any)}
117
+ sideOffset={(rest as any).sideOffset ?? 8}
114
118
  class={cn(
115
- "z-50 min-w-32 mt-1 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
119
+ "z-50 min-w-32 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md animate-in fade-in-80 max-h-72 flex flex-col mt-1",
116
120
  local.class
117
121
  )}
118
- {...(rest as any)}
119
- />
122
+ >
123
+ <ScrollArea class="max-h-72 w-full rounded-[inherit]">
124
+ <div class="p-1">
125
+ {local.children}
126
+ </div>
127
+ </ScrollArea>
128
+ </DropdownMenuPrimitive.Content>
120
129
  </DropdownMenuPrimitive.Portal>
121
130
  );
122
131
  };
@@ -125,25 +134,18 @@ export type DropdownMenuItemProps<T extends ValidComponent = "div"> =
125
134
  DropdownMenuPrimitive.DropdownMenuItemProps<T> & {
126
135
  class?: string;
127
136
  inset?: boolean;
128
- variant?: "default" | "destructive";
129
137
  };
130
138
 
131
139
  export const DropdownMenuItem = <T extends ValidComponent = "div">(
132
140
  props: PolymorphicProps<T, DropdownMenuItemProps<T>>
133
141
  ) => {
134
- const [local, rest] = splitProps(props as DropdownMenuItemProps, [
135
- "class",
136
- "inset",
137
- "variant",
138
- ]);
142
+ const [local, rest] = splitProps(props as DropdownMenuItemProps, ["class", "inset"]);
139
143
 
140
144
  return (
141
145
  <DropdownMenuPrimitive.Item
142
146
  class={cn(
143
- "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",
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",
144
148
  local.inset && "pl-8",
145
- local.variant === "destructive" &&
146
- "text-destructive focus:bg-destructive/15 focus:text-destructive",
147
149
  local.class
148
150
  )}
149
151
  {...(rest as any)}
@@ -155,24 +157,26 @@ export type DropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> =
155
157
  DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & {
156
158
  class?: string;
157
159
  children?: JSX.Element;
160
+ checked?: boolean;
158
161
  };
159
162
 
160
163
  export const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">(
161
164
  props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>>
162
165
  ) => {
163
- const [local, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children"]);
166
+ const [local, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children", "checked"]);
164
167
 
165
168
  return (
166
169
  <DropdownMenuPrimitive.CheckboxItem
170
+ checked={local.checked}
167
171
  class={cn(
168
- "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",
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",
169
173
  local.class
170
174
  )}
171
175
  {...(rest as any)}
172
176
  >
173
177
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
174
178
  <DropdownMenuPrimitive.ItemIndicator>
175
- <svg class="h-4 w-4 fill-none stroke-current stroke-2" viewBox="0 0 24 24">
179
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
176
180
  <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
177
181
  </svg>
178
182
  </DropdownMenuPrimitive.ItemIndicator>
@@ -196,14 +200,16 @@ export const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
196
200
  return (
197
201
  <DropdownMenuPrimitive.RadioItem
198
202
  class={cn(
199
- "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",
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",
200
204
  local.class
201
205
  )}
202
206
  {...(rest as any)}
203
207
  >
204
208
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
205
209
  <DropdownMenuPrimitive.ItemIndicator>
206
- <span class="h-2 w-2 rounded-full bg-current" />
210
+ <svg class="h-2 w-2 fill-current" viewBox="0 0 8 8">
211
+ <circle cx="4" cy="4" r="3" />
212
+ </svg>
207
213
  </DropdownMenuPrimitive.ItemIndicator>
208
214
  </span>
209
215
  {local.children}
@@ -211,55 +217,44 @@ export const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
211
217
  );
212
218
  };
213
219
 
214
- export interface DropdownMenuLabelProps extends JSX.HTMLAttributes<HTMLDivElement> {
220
+ export interface DropdownMenuLabelProps {
215
221
  class?: string;
216
222
  inset?: boolean;
223
+ children?: JSX.Element;
217
224
  }
218
225
 
219
226
  export const DropdownMenuLabel: Component<DropdownMenuLabelProps> = (props) => {
220
- const [local, rest] = splitProps(props, ["class", "inset"]);
227
+ const [local, rest] = splitProps(props, ["class", "inset", "children"]);
221
228
 
222
229
  return (
223
- <div
224
- class={cn(
225
- "px-2 py-1.5 text-sm font-semibold text-foreground",
226
- local.inset && "pl-8",
227
- local.class
228
- )}
230
+ <DropdownMenuPrimitive.GroupLabel
231
+ class={cn("px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class)}
229
232
  {...rest}
230
- />
233
+ >
234
+ {local.children}
235
+ </DropdownMenuPrimitive.GroupLabel>
231
236
  );
232
237
  };
233
238
 
234
- export type DropdownMenuSeparatorProps<T extends ValidComponent = "hr"> =
235
- DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & {
236
- class?: string;
237
- };
238
-
239
- export const DropdownMenuSeparator = <T extends ValidComponent = "hr">(
240
- props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>>
241
- ) => {
242
- const [local, rest] = splitProps(props as DropdownMenuSeparatorProps, ["class"]);
243
-
239
+ export const DropdownMenuSeparator: Component<{ class?: string }> = (props) => {
244
240
  return (
245
241
  <DropdownMenuPrimitive.Separator
246
- class={cn("-mx-1 my-1 h-px bg-border", local.class)}
247
- {...(rest as any)}
242
+ class={cn("-mx-1 my-1 h-px bg-border", props.class)}
248
243
  />
249
244
  );
250
245
  };
251
246
 
252
- export interface DropdownMenuShortcutProps extends JSX.HTMLAttributes<HTMLSpanElement> {
247
+ export interface DropdownMenuShortcutProps {
253
248
  class?: string;
249
+ children?: JSX.Element;
254
250
  }
255
251
 
256
252
  export const DropdownMenuShortcut: Component<DropdownMenuShortcutProps> = (props) => {
257
- const [local, rest] = splitProps(props, ["class"]);
253
+ const [local, rest] = splitProps(props, ["class", "children"]);
258
254
 
259
255
  return (
260
- <span
261
- class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)}
262
- {...rest}
263
- />
256
+ <span class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)} {...rest}>
257
+ {local.children}
258
+ </span>
264
259
  );
265
260
  };
@@ -0,0 +1,83 @@
1
+ import { splitProps, type Component, type JSX } from "solid-js";
2
+ import { cn } from "@/lib/cn";
3
+
4
+ export interface EmptyProps extends JSX.HTMLAttributes<HTMLDivElement> {
5
+ class?: string;
6
+ }
7
+
8
+ /** A centered layout for empty collections, search results, and initial states. */
9
+ export const Empty: Component<EmptyProps> = (props) => {
10
+ const [local, rest] = splitProps(props, ["class"]);
11
+
12
+ return (
13
+ <div
14
+ class={cn(
15
+ "flex min-h-64 w-full flex-col items-center justify-center rounded-lg border border-dashed border-border bg-card/30 p-6 text-center",
16
+ local.class
17
+ )}
18
+ {...rest}
19
+ />
20
+ );
21
+ };
22
+
23
+ export interface EmptyIconProps extends JSX.HTMLAttributes<HTMLDivElement> {
24
+ class?: string;
25
+ }
26
+
27
+ export const EmptyIcon: Component<EmptyIconProps> = (props) => {
28
+ const [local, rest] = splitProps(props, ["class"]);
29
+
30
+ return (
31
+ <div
32
+ aria-hidden="true"
33
+ class={cn(
34
+ "mb-4 flex size-12 items-center justify-center rounded-lg bg-muted text-muted-foreground [&_svg]:size-6",
35
+ local.class
36
+ )}
37
+ {...rest}
38
+ />
39
+ );
40
+ };
41
+
42
+ export interface EmptyTitleProps extends JSX.HTMLAttributes<HTMLHeadingElement> {
43
+ class?: string;
44
+ }
45
+
46
+ export const EmptyTitle: Component<EmptyTitleProps> = (props) => {
47
+ const [local, rest] = splitProps(props, ["class"]);
48
+
49
+ return (
50
+ <h3
51
+ class={cn("text-lg font-semibold tracking-tight text-foreground", local.class)}
52
+ {...rest}
53
+ />
54
+ );
55
+ };
56
+
57
+ export interface EmptyDescriptionProps
58
+ extends JSX.HTMLAttributes<HTMLParagraphElement> {
59
+ class?: string;
60
+ }
61
+
62
+ export const EmptyDescription: Component<EmptyDescriptionProps> = (props) => {
63
+ const [local, rest] = splitProps(props, ["class"]);
64
+
65
+ return (
66
+ <p
67
+ class={cn("mt-1 max-w-sm text-sm text-muted-foreground", local.class)}
68
+ {...rest}
69
+ />
70
+ );
71
+ };
72
+
73
+ export interface EmptyActionProps extends JSX.HTMLAttributes<HTMLDivElement> {
74
+ class?: string;
75
+ }
76
+
77
+ export const EmptyAction: Component<EmptyActionProps> = (props) => {
78
+ const [local, rest] = splitProps(props, ["class"]);
79
+
80
+ return (
81
+ <div class={cn("mt-5 flex items-center gap-2", local.class)} {...rest} />
82
+ );
83
+ };