@withmata/blueprints 0.3.5 → 0.4.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.
- package/.claude/skills/audit/SKILL.md +4 -4
- package/.claude/skills/blueprint-catalog/SKILL.md +17 -7
- package/.claude/skills/copywrite/SKILL.md +187 -0
- package/.claude/skills/copywrite-landing/SKILL.md +489 -0
- package/.claude/skills/design-system/SKILL.md +970 -0
- package/.claude/skills/new-project/SKILL.md +168 -112
- package/.claude/skills/scaffold-auth/SKILL.md +9 -9
- package/.claude/skills/scaffold-db/SKILL.md +14 -14
- package/.claude/skills/scaffold-env/SKILL.md +4 -4
- package/.claude/skills/scaffold-foundation/SKILL.md +15 -15
- package/.claude/skills/scaffold-tailwind/SKILL.md +17 -3
- package/.claude/skills/scaffold-ui/SKILL.md +155 -36
- package/ENGINEERING.md +2 -2
- package/blueprints/discovery/design-system/BLUEPRINT.md +1479 -0
- package/blueprints/discovery/marketing-copywriting/BLUEPRINT.md +664 -0
- package/blueprints/features/auth-better-auth/BLUEPRINT.md +20 -22
- package/blueprints/features/db-drizzle-postgres/BLUEPRINT.md +12 -12
- package/blueprints/features/db-drizzle-postgres/files/db/src/example-entity.ts +1 -1
- package/blueprints/features/db-drizzle-postgres/files/db/src/scripts/seed.ts +1 -1
- package/blueprints/features/env-t3/BLUEPRINT.md +1 -1
- package/blueprints/features/tailwind-v4/BLUEPRINT.md +9 -2
- package/blueprints/features/tailwind-v4/files/tailwind-config/shared-styles.css +80 -1
- package/blueprints/features/ui-shared-components/BLUEPRINT.md +411 -78
- package/blueprints/features/ui-shared-components/files/ui/components/ui/alert-dialog.tsx +192 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/avatar.tsx +71 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/badge.tsx +52 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/breadcrumb.tsx +122 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/button.tsx +56 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/card-select.tsx +72 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/card.tsx +100 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/collapsible.tsx +34 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/combobox.tsx +301 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/dropdown-menu.tsx +264 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/empty-state.tsx +43 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/entity-select.tsx +110 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/field.tsx +237 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/form-field.tsx +217 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/input-group.tsx +161 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/input.tsx +20 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/label.tsx +20 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/org-switcher.tsx +114 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/page-header.tsx +45 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/pagination.tsx +52 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/pill-select.tsx +151 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/popover.tsx +41 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/search-input.tsx +49 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/select.tsx +205 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/selected-entity-card.tsx +47 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/separator.tsx +25 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/sidebar.tsx +389 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/status-filter.tsx +43 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/tag-input.tsx +131 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/textarea.tsx +18 -0
- package/blueprints/features/ui-shared-components/files/ui/components/ui/user-menu.tsx +149 -0
- package/blueprints/features/ui-shared-components/files/ui/components.json +11 -8
- package/blueprints/features/ui-shared-components/files/ui/package.json +20 -11
- package/blueprints/foundation/monorepo-turbo/BLUEPRINT.md +19 -20
- package/blueprints/foundation/monorepo-turbo/files/root/package.json +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/blueprints/features/tailwind-v4/files/tailwind-config/package.json +0 -20
- package/blueprints/foundation/monorepo-turbo/files/root/pnpm-workspace.yaml +0 -5
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Combobox as ComboboxPrimitive } from "@base-ui/react";
|
|
4
|
+
import { CaretDownIcon } from "@phosphor-icons/react/CaretDown";
|
|
5
|
+
import { CheckIcon } from "@phosphor-icons/react/Check";
|
|
6
|
+
import { XIcon } from "@phosphor-icons/react/X";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { Button } from "#components/ui/button";
|
|
9
|
+
import {
|
|
10
|
+
InputGroup,
|
|
11
|
+
InputGroupAddon,
|
|
12
|
+
InputGroupButton,
|
|
13
|
+
InputGroupInput,
|
|
14
|
+
} from "#components/ui/input-group";
|
|
15
|
+
import { cn } from "#utils/cn";
|
|
16
|
+
|
|
17
|
+
const Combobox = ComboboxPrimitive.Root;
|
|
18
|
+
|
|
19
|
+
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
|
|
20
|
+
return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ComboboxTrigger({
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
...props
|
|
27
|
+
}: ComboboxPrimitive.Trigger.Props) {
|
|
28
|
+
return (
|
|
29
|
+
<ComboboxPrimitive.Trigger
|
|
30
|
+
data-slot="combobox-trigger"
|
|
31
|
+
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
<CaretDownIcon className="text-muted-foreground size-4 pointer-events-none" />
|
|
36
|
+
</ComboboxPrimitive.Trigger>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
|
|
41
|
+
return (
|
|
42
|
+
<ComboboxPrimitive.Clear
|
|
43
|
+
data-slot="combobox-clear"
|
|
44
|
+
render={<InputGroupButton variant="ghost" size="icon-xs" />}
|
|
45
|
+
className={cn(className)}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
<XIcon className="pointer-events-none" />
|
|
49
|
+
</ComboboxPrimitive.Clear>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function ComboboxInput({
|
|
54
|
+
className,
|
|
55
|
+
children,
|
|
56
|
+
disabled = false,
|
|
57
|
+
showTrigger = true,
|
|
58
|
+
showClear = false,
|
|
59
|
+
...props
|
|
60
|
+
}: ComboboxPrimitive.Input.Props & {
|
|
61
|
+
showTrigger?: boolean;
|
|
62
|
+
showClear?: boolean;
|
|
63
|
+
}) {
|
|
64
|
+
return (
|
|
65
|
+
<InputGroup className={cn("w-auto", className)}>
|
|
66
|
+
<ComboboxPrimitive.Input
|
|
67
|
+
render={<InputGroupInput disabled={disabled} />}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
<InputGroupAddon align="inline-end">
|
|
71
|
+
{showTrigger && (
|
|
72
|
+
<InputGroupButton
|
|
73
|
+
size="icon-xs"
|
|
74
|
+
variant="ghost"
|
|
75
|
+
render={<ComboboxTrigger />}
|
|
76
|
+
data-slot="input-group-button"
|
|
77
|
+
className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
|
|
78
|
+
disabled={disabled}
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
{showClear && <ComboboxClear disabled={disabled} />}
|
|
82
|
+
</InputGroupAddon>
|
|
83
|
+
{children}
|
|
84
|
+
</InputGroup>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ComboboxContent({
|
|
89
|
+
className,
|
|
90
|
+
side = "bottom",
|
|
91
|
+
sideOffset = 6,
|
|
92
|
+
align = "start",
|
|
93
|
+
alignOffset = 0,
|
|
94
|
+
anchor,
|
|
95
|
+
...props
|
|
96
|
+
}: ComboboxPrimitive.Popup.Props &
|
|
97
|
+
Pick<
|
|
98
|
+
ComboboxPrimitive.Positioner.Props,
|
|
99
|
+
"side" | "align" | "sideOffset" | "alignOffset" | "anchor"
|
|
100
|
+
>) {
|
|
101
|
+
return (
|
|
102
|
+
<ComboboxPrimitive.Portal>
|
|
103
|
+
<ComboboxPrimitive.Positioner
|
|
104
|
+
side={side}
|
|
105
|
+
sideOffset={sideOffset}
|
|
106
|
+
align={align}
|
|
107
|
+
alignOffset={alignOffset}
|
|
108
|
+
anchor={anchor}
|
|
109
|
+
className="isolate z-50"
|
|
110
|
+
>
|
|
111
|
+
<ComboboxPrimitive.Popup
|
|
112
|
+
data-slot="combobox-content"
|
|
113
|
+
data-chips={!!anchor}
|
|
114
|
+
className={cn(
|
|
115
|
+
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 *:data-[slot=input-group]:bg-input/30 max-h-72 min-w-36 overflow-hidden rounded-2xl shadow-2xl ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-9 *:data-[slot=input-group]:border-none *:data-[slot=input-group]:shadow-none group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)",
|
|
116
|
+
className,
|
|
117
|
+
)}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
</ComboboxPrimitive.Positioner>
|
|
121
|
+
</ComboboxPrimitive.Portal>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
|
|
126
|
+
return (
|
|
127
|
+
<ComboboxPrimitive.List
|
|
128
|
+
data-slot="combobox-list"
|
|
129
|
+
className={cn(
|
|
130
|
+
"no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0 overflow-y-auto overscroll-contain",
|
|
131
|
+
className,
|
|
132
|
+
)}
|
|
133
|
+
{...props}
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function ComboboxItem({
|
|
139
|
+
className,
|
|
140
|
+
children,
|
|
141
|
+
...props
|
|
142
|
+
}: ComboboxPrimitive.Item.Props) {
|
|
143
|
+
return (
|
|
144
|
+
<ComboboxPrimitive.Item
|
|
145
|
+
data-slot="combobox-item"
|
|
146
|
+
className={cn(
|
|
147
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
148
|
+
className,
|
|
149
|
+
)}
|
|
150
|
+
{...props}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
<ComboboxPrimitive.ItemIndicator
|
|
154
|
+
render={
|
|
155
|
+
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
|
|
156
|
+
}
|
|
157
|
+
>
|
|
158
|
+
<CheckIcon className="pointer-events-none" />
|
|
159
|
+
</ComboboxPrimitive.ItemIndicator>
|
|
160
|
+
</ComboboxPrimitive.Item>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
|
|
165
|
+
return (
|
|
166
|
+
<ComboboxPrimitive.Group
|
|
167
|
+
data-slot="combobox-group"
|
|
168
|
+
className={cn(className)}
|
|
169
|
+
{...props}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function ComboboxLabel({
|
|
175
|
+
className,
|
|
176
|
+
...props
|
|
177
|
+
}: ComboboxPrimitive.GroupLabel.Props) {
|
|
178
|
+
return (
|
|
179
|
+
<ComboboxPrimitive.GroupLabel
|
|
180
|
+
data-slot="combobox-label"
|
|
181
|
+
className={cn("text-muted-foreground px-3.5 py-2.5 text-xs", className)}
|
|
182
|
+
{...props}
|
|
183
|
+
/>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
|
|
188
|
+
return (
|
|
189
|
+
<ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
|
|
194
|
+
return (
|
|
195
|
+
<ComboboxPrimitive.Empty
|
|
196
|
+
data-slot="combobox-empty"
|
|
197
|
+
className={cn(
|
|
198
|
+
"text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex",
|
|
199
|
+
className,
|
|
200
|
+
)}
|
|
201
|
+
{...props}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function ComboboxSeparator({
|
|
207
|
+
className,
|
|
208
|
+
...props
|
|
209
|
+
}: ComboboxPrimitive.Separator.Props) {
|
|
210
|
+
return (
|
|
211
|
+
<ComboboxPrimitive.Separator
|
|
212
|
+
data-slot="combobox-separator"
|
|
213
|
+
className={cn("bg-border/50 -mx-1 my-1 h-px", className)}
|
|
214
|
+
{...props}
|
|
215
|
+
/>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function ComboboxChips({
|
|
220
|
+
className,
|
|
221
|
+
...props
|
|
222
|
+
}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &
|
|
223
|
+
ComboboxPrimitive.Chips.Props) {
|
|
224
|
+
return (
|
|
225
|
+
<ComboboxPrimitive.Chips
|
|
226
|
+
data-slot="combobox-chips"
|
|
227
|
+
className={cn(
|
|
228
|
+
"bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-9 flex-wrap items-center gap-1.5 rounded-4xl border bg-clip-padding px-2.5 py-1.5 text-sm transition-colors focus-within:ring-[3px] has-aria-invalid:ring-[3px] has-data-[slot=combobox-chip]:px-1.5",
|
|
229
|
+
className,
|
|
230
|
+
)}
|
|
231
|
+
{...props}
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function ComboboxChip({
|
|
237
|
+
className,
|
|
238
|
+
children,
|
|
239
|
+
showRemove = true,
|
|
240
|
+
...props
|
|
241
|
+
}: ComboboxPrimitive.Chip.Props & {
|
|
242
|
+
showRemove?: boolean;
|
|
243
|
+
}) {
|
|
244
|
+
return (
|
|
245
|
+
<ComboboxPrimitive.Chip
|
|
246
|
+
data-slot="combobox-chip"
|
|
247
|
+
className={cn(
|
|
248
|
+
"bg-muted-foreground/10 text-foreground flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-4xl px-2 text-xs font-medium whitespace-nowrap has-data-[slot=combobox-chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50",
|
|
249
|
+
className,
|
|
250
|
+
)}
|
|
251
|
+
{...props}
|
|
252
|
+
>
|
|
253
|
+
{children}
|
|
254
|
+
{showRemove && (
|
|
255
|
+
<ComboboxPrimitive.ChipRemove
|
|
256
|
+
render={<Button variant="ghost" size="icon-xs" />}
|
|
257
|
+
className="-ml-1 opacity-50 hover:opacity-100"
|
|
258
|
+
data-slot="combobox-chip-remove"
|
|
259
|
+
>
|
|
260
|
+
<XIcon className="pointer-events-none" />
|
|
261
|
+
</ComboboxPrimitive.ChipRemove>
|
|
262
|
+
)}
|
|
263
|
+
</ComboboxPrimitive.Chip>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function ComboboxChipsInput({
|
|
268
|
+
className,
|
|
269
|
+
...props
|
|
270
|
+
}: ComboboxPrimitive.Input.Props) {
|
|
271
|
+
return (
|
|
272
|
+
<ComboboxPrimitive.Input
|
|
273
|
+
data-slot="combobox-chip-input"
|
|
274
|
+
className={cn("min-w-16 flex-1 outline-none", className)}
|
|
275
|
+
{...props}
|
|
276
|
+
/>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function useComboboxAnchor() {
|
|
281
|
+
return React.useRef<HTMLDivElement | null>(null);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export {
|
|
285
|
+
Combobox,
|
|
286
|
+
ComboboxInput,
|
|
287
|
+
ComboboxContent,
|
|
288
|
+
ComboboxList,
|
|
289
|
+
ComboboxItem,
|
|
290
|
+
ComboboxGroup,
|
|
291
|
+
ComboboxLabel,
|
|
292
|
+
ComboboxCollection,
|
|
293
|
+
ComboboxEmpty,
|
|
294
|
+
ComboboxSeparator,
|
|
295
|
+
ComboboxChips,
|
|
296
|
+
ComboboxChip,
|
|
297
|
+
ComboboxChipsInput,
|
|
298
|
+
ComboboxTrigger,
|
|
299
|
+
ComboboxValue,
|
|
300
|
+
useComboboxAnchor,
|
|
301
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu";
|
|
4
|
+
import { CaretRightIcon } from "@phosphor-icons/react/CaretRight";
|
|
5
|
+
import { CheckIcon } from "@phosphor-icons/react/Check";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
import { cn } from "#utils/cn";
|
|
8
|
+
|
|
9
|
+
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
|
10
|
+
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
|
|
14
|
+
return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
|
|
18
|
+
return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function DropdownMenuContent({
|
|
22
|
+
align = "start",
|
|
23
|
+
alignOffset = 0,
|
|
24
|
+
side = "bottom",
|
|
25
|
+
sideOffset = 4,
|
|
26
|
+
className,
|
|
27
|
+
...props
|
|
28
|
+
}: MenuPrimitive.Popup.Props &
|
|
29
|
+
Pick<
|
|
30
|
+
MenuPrimitive.Positioner.Props,
|
|
31
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
32
|
+
>) {
|
|
33
|
+
return (
|
|
34
|
+
<MenuPrimitive.Portal>
|
|
35
|
+
<MenuPrimitive.Positioner
|
|
36
|
+
className="isolate z-50 outline-none"
|
|
37
|
+
align={align}
|
|
38
|
+
alignOffset={alignOffset}
|
|
39
|
+
side={side}
|
|
40
|
+
sideOffset={sideOffset}
|
|
41
|
+
>
|
|
42
|
+
<MenuPrimitive.Popup
|
|
43
|
+
data-slot="dropdown-menu-content"
|
|
44
|
+
className={cn(
|
|
45
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 bg-popover text-popover-foreground min-w-48 rounded-2xl p-1 shadow-2xl ring-1 duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
</MenuPrimitive.Positioner>
|
|
51
|
+
</MenuPrimitive.Portal>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
|
|
56
|
+
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function DropdownMenuLabel({
|
|
60
|
+
className,
|
|
61
|
+
inset,
|
|
62
|
+
...props
|
|
63
|
+
}: MenuPrimitive.GroupLabel.Props & {
|
|
64
|
+
inset?: boolean;
|
|
65
|
+
}) {
|
|
66
|
+
return (
|
|
67
|
+
<MenuPrimitive.GroupLabel
|
|
68
|
+
data-slot="dropdown-menu-label"
|
|
69
|
+
data-inset={inset}
|
|
70
|
+
className={cn(
|
|
71
|
+
"text-muted-foreground px-3 py-2.5 text-xs data-[inset]:pl-8",
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function DropdownMenuItem({
|
|
80
|
+
className,
|
|
81
|
+
inset,
|
|
82
|
+
variant = "default",
|
|
83
|
+
...props
|
|
84
|
+
}: MenuPrimitive.Item.Props & {
|
|
85
|
+
inset?: boolean;
|
|
86
|
+
variant?: "default" | "destructive";
|
|
87
|
+
}) {
|
|
88
|
+
return (
|
|
89
|
+
<MenuPrimitive.Item
|
|
90
|
+
data-slot="dropdown-menu-item"
|
|
91
|
+
data-inset={inset}
|
|
92
|
+
data-variant={variant}
|
|
93
|
+
className={cn(
|
|
94
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
95
|
+
className,
|
|
96
|
+
)}
|
|
97
|
+
{...props}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
|
|
103
|
+
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function DropdownMenuSubTrigger({
|
|
107
|
+
className,
|
|
108
|
+
inset,
|
|
109
|
+
children,
|
|
110
|
+
...props
|
|
111
|
+
}: MenuPrimitive.SubmenuTrigger.Props & {
|
|
112
|
+
inset?: boolean;
|
|
113
|
+
}) {
|
|
114
|
+
return (
|
|
115
|
+
<MenuPrimitive.SubmenuTrigger
|
|
116
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
117
|
+
data-inset={inset}
|
|
118
|
+
className={cn(
|
|
119
|
+
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
120
|
+
className,
|
|
121
|
+
)}
|
|
122
|
+
{...props}
|
|
123
|
+
>
|
|
124
|
+
{children}
|
|
125
|
+
<CaretRightIcon className="ml-auto" />
|
|
126
|
+
</MenuPrimitive.SubmenuTrigger>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function DropdownMenuSubContent({
|
|
131
|
+
align = "start",
|
|
132
|
+
alignOffset = -3,
|
|
133
|
+
side = "right",
|
|
134
|
+
sideOffset = 0,
|
|
135
|
+
className,
|
|
136
|
+
...props
|
|
137
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
138
|
+
return (
|
|
139
|
+
<DropdownMenuContent
|
|
140
|
+
data-slot="dropdown-menu-sub-content"
|
|
141
|
+
className={cn(
|
|
142
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 bg-popover text-popover-foreground min-w-36 rounded-2xl p-1 shadow-2xl ring-1 duration-100 w-auto",
|
|
143
|
+
className,
|
|
144
|
+
)}
|
|
145
|
+
align={align}
|
|
146
|
+
alignOffset={alignOffset}
|
|
147
|
+
side={side}
|
|
148
|
+
sideOffset={sideOffset}
|
|
149
|
+
{...props}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function DropdownMenuCheckboxItem({
|
|
155
|
+
className,
|
|
156
|
+
children,
|
|
157
|
+
checked,
|
|
158
|
+
...props
|
|
159
|
+
}: MenuPrimitive.CheckboxItem.Props) {
|
|
160
|
+
return (
|
|
161
|
+
<MenuPrimitive.CheckboxItem
|
|
162
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
163
|
+
className={cn(
|
|
164
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
165
|
+
className,
|
|
166
|
+
)}
|
|
167
|
+
checked={checked}
|
|
168
|
+
{...props}
|
|
169
|
+
>
|
|
170
|
+
<span
|
|
171
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none"
|
|
172
|
+
data-slot="dropdown-menu-checkbox-item-indicator"
|
|
173
|
+
>
|
|
174
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
175
|
+
<CheckIcon />
|
|
176
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
177
|
+
</span>
|
|
178
|
+
{children}
|
|
179
|
+
</MenuPrimitive.CheckboxItem>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
|
|
184
|
+
return (
|
|
185
|
+
<MenuPrimitive.RadioGroup
|
|
186
|
+
data-slot="dropdown-menu-radio-group"
|
|
187
|
+
{...props}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function DropdownMenuRadioItem({
|
|
193
|
+
className,
|
|
194
|
+
children,
|
|
195
|
+
...props
|
|
196
|
+
}: MenuPrimitive.RadioItem.Props) {
|
|
197
|
+
return (
|
|
198
|
+
<MenuPrimitive.RadioItem
|
|
199
|
+
data-slot="dropdown-menu-radio-item"
|
|
200
|
+
className={cn(
|
|
201
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
202
|
+
className,
|
|
203
|
+
)}
|
|
204
|
+
{...props}
|
|
205
|
+
>
|
|
206
|
+
<span
|
|
207
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none"
|
|
208
|
+
data-slot="dropdown-menu-radio-item-indicator"
|
|
209
|
+
>
|
|
210
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
211
|
+
<CheckIcon />
|
|
212
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
213
|
+
</span>
|
|
214
|
+
{children}
|
|
215
|
+
</MenuPrimitive.RadioItem>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function DropdownMenuSeparator({
|
|
220
|
+
className,
|
|
221
|
+
...props
|
|
222
|
+
}: MenuPrimitive.Separator.Props) {
|
|
223
|
+
return (
|
|
224
|
+
<MenuPrimitive.Separator
|
|
225
|
+
data-slot="dropdown-menu-separator"
|
|
226
|
+
className={cn("bg-border/50 -mx-1 my-1 h-px", className)}
|
|
227
|
+
{...props}
|
|
228
|
+
/>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function DropdownMenuShortcut({
|
|
233
|
+
className,
|
|
234
|
+
...props
|
|
235
|
+
}: React.ComponentProps<"span">) {
|
|
236
|
+
return (
|
|
237
|
+
<span
|
|
238
|
+
data-slot="dropdown-menu-shortcut"
|
|
239
|
+
className={cn(
|
|
240
|
+
"text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest",
|
|
241
|
+
className,
|
|
242
|
+
)}
|
|
243
|
+
{...props}
|
|
244
|
+
/>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export {
|
|
249
|
+
DropdownMenu,
|
|
250
|
+
DropdownMenuPortal,
|
|
251
|
+
DropdownMenuTrigger,
|
|
252
|
+
DropdownMenuContent,
|
|
253
|
+
DropdownMenuGroup,
|
|
254
|
+
DropdownMenuLabel,
|
|
255
|
+
DropdownMenuItem,
|
|
256
|
+
DropdownMenuCheckboxItem,
|
|
257
|
+
DropdownMenuRadioGroup,
|
|
258
|
+
DropdownMenuRadioItem,
|
|
259
|
+
DropdownMenuSeparator,
|
|
260
|
+
DropdownMenuShortcut,
|
|
261
|
+
DropdownMenuSub,
|
|
262
|
+
DropdownMenuSubTrigger,
|
|
263
|
+
DropdownMenuSubContent,
|
|
264
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Button } from "#components/ui/button";
|
|
2
|
+
import { cn } from "#utils/cn";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
|
|
5
|
+
export interface EmptyStateProps {
|
|
6
|
+
icon: React.ReactNode;
|
|
7
|
+
heading: string;
|
|
8
|
+
description: string;
|
|
9
|
+
actionLabel?: string;
|
|
10
|
+
onAction?: () => void;
|
|
11
|
+
className?: string;
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function EmptyState({
|
|
16
|
+
icon,
|
|
17
|
+
heading,
|
|
18
|
+
description,
|
|
19
|
+
actionLabel,
|
|
20
|
+
onAction,
|
|
21
|
+
className,
|
|
22
|
+
children,
|
|
23
|
+
}: EmptyStateProps) {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
data-slot="empty-state"
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex flex-col items-center justify-center py-12 px-4",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
>
|
|
32
|
+
<div className="bg-muted rounded-full p-6 mb-4">{icon}</div>
|
|
33
|
+
<h3 className="text-lg font-medium mb-2">{heading}</h3>
|
|
34
|
+
<p className="text-sm text-muted-foreground text-center max-w-sm mb-6">
|
|
35
|
+
{description}
|
|
36
|
+
</p>
|
|
37
|
+
{actionLabel && onAction && (
|
|
38
|
+
<Button onClick={onAction}>{actionLabel}</Button>
|
|
39
|
+
)}
|
|
40
|
+
{children}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|