@schandlergarcia/sf-web-components 1.9.38 → 1.9.39
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/package.json +4 -1
- package/scripts/postinstall.mjs +36 -17
- package/src/components/library/cards/ActionList.jsx +38 -0
- package/src/components/library/cards/ActivityCard.jsx +56 -0
- package/src/components/library/cards/BaseCard.jsx +109 -0
- package/src/components/library/cards/CalloutCard.jsx +37 -0
- package/src/components/library/cards/ChartCard.jsx +105 -0
- package/src/components/library/cards/FeedPanel.jsx +39 -0
- package/src/components/library/cards/ListCard.jsx +193 -0
- package/src/components/library/cards/MetricCard.jsx +109 -0
- package/src/components/library/cards/MetricsStrip.jsx +78 -0
- package/src/components/library/cards/SectionCard.jsx +83 -0
- package/src/components/library/cards/SemanticMetricCard.jsx +52 -0
- package/src/components/library/cards/SemanticMetricCardWithLoading.jsx +23 -0
- package/src/components/library/cards/SemanticTableCard.jsx +48 -0
- package/src/components/library/cards/SemanticTableCardWithLoading.jsx +22 -0
- package/src/components/library/cards/StatusCard.jsx +220 -0
- package/src/components/library/cards/TableCard.jsx +337 -0
- package/src/components/library/cards/WidgetCard.jsx +90 -0
- package/src/components/library/charts/D3Chart.jsx +109 -0
- package/src/components/library/charts/D3ChartTemplates.jsx +126 -0
- package/src/components/library/charts/GeoMap.jsx +293 -0
- package/src/components/library/chat/ChatBar.jsx +256 -0
- package/src/components/library/chat/ChatInput.jsx +89 -0
- package/src/components/library/chat/ChatMessage.jsx +178 -0
- package/src/components/library/chat/ChatMessageList.jsx +73 -0
- package/src/components/library/chat/ChatPanel.jsx +97 -0
- package/src/components/library/chat/ChatSuggestions.jsx +28 -0
- package/src/components/library/chat/ChatToolCall.jsx +100 -0
- package/src/components/library/chat/ChatTypingIndicator.jsx +23 -0
- package/src/components/library/chat/ChatWelcome.jsx +43 -0
- package/src/components/library/chat/index.jsx +10 -0
- package/src/components/library/chat/useChatState.jsx +130 -0
- package/src/components/library/data/DataModeProvider.jsx +67 -0
- package/src/components/library/data/DataModeToggle.jsx +36 -0
- package/src/components/library/data/chartDataProvider.jsx +61 -0
- package/src/components/library/data/filterUtils.jsx +141 -0
- package/src/components/library/data/useDataSource.jsx +33 -0
- package/src/components/library/data/usePageFilters.jsx +99 -0
- package/src/components/library/filters/FilterBar.jsx +95 -0
- package/src/components/library/filters/SearchFilter.jsx +36 -0
- package/src/components/library/filters/SelectFilter.jsx +55 -0
- package/src/components/library/filters/ToggleFilter.jsx +52 -0
- package/src/components/library/filters/index.jsx +4 -0
- package/src/components/library/forms/FormField.jsx +291 -0
- package/src/components/library/forms/FormModal.jsx +201 -0
- package/src/components/library/forms/FormRenderer.jsx +46 -0
- package/src/components/library/forms/FormSection.jsx +69 -0
- package/src/components/library/forms/index.jsx +5 -0
- package/src/components/library/forms/useFormState.jsx +165 -0
- package/src/components/library/heroui/Accordion.jsx +26 -0
- package/src/components/library/heroui/Alert.jsx +8 -0
- package/src/components/library/heroui/Badge.jsx +8 -0
- package/src/components/library/heroui/Breadcrumbs.jsx +22 -0
- package/src/components/library/heroui/Button.jsx +58 -0
- package/src/components/library/heroui/Card.jsx +8 -0
- package/src/components/library/heroui/Collapsible.jsx +42 -0
- package/src/components/library/heroui/DatePicker.jsx +34 -0
- package/src/components/library/heroui/Dialog.jsx +37 -0
- package/src/components/library/heroui/Drawer.jsx +32 -0
- package/src/components/library/heroui/Dropdown.jsx +28 -0
- package/src/components/library/heroui/Field.jsx +51 -0
- package/src/components/library/heroui/Input.jsx +6 -0
- package/src/components/library/heroui/Kbd.jsx +8 -0
- package/src/components/library/heroui/Meter.jsx +8 -0
- package/src/components/library/heroui/Modal.jsx +32 -0
- package/src/components/library/heroui/Pagination.jsx +8 -0
- package/src/components/library/heroui/Popover.jsx +64 -0
- package/src/components/library/heroui/ProgressBar.jsx +8 -0
- package/src/components/library/heroui/ProgressCircle.jsx +8 -0
- package/src/components/library/heroui/ScrollShadow.jsx +8 -0
- package/src/components/library/heroui/Select.jsx +37 -0
- package/src/components/library/heroui/Separator.jsx +8 -0
- package/src/components/library/heroui/Skeleton.jsx +8 -0
- package/src/components/library/heroui/Tabs.jsx +26 -0
- package/src/components/library/heroui/Toast.jsx +25 -0
- package/src/components/library/heroui/Toggle.jsx +14 -0
- package/src/components/library/heroui/Tooltip.jsx +21 -0
- package/src/components/library/index.jsx +146 -0
- package/src/components/library/layout/PageContainer.jsx +11 -0
- package/src/components/library/skeletons/CardSkeleton.jsx +30 -0
- package/src/components/library/theme/AppThemeProvider.jsx +67 -0
- package/src/components/library/theme/tokens.jsx +72 -0
- package/src/components/library/ui/Alert.jsx +80 -0
- package/src/components/library/ui/Avatar.jsx +44 -0
- package/src/components/library/ui/BreadcrumbExtras.tsx +120 -0
- package/src/components/library/ui/Button.jsx +61 -0
- package/src/components/library/ui/Card.jsx +117 -0
- package/src/components/library/ui/Checkbox.jsx +17 -0
- package/src/components/library/ui/Chip.jsx +38 -0
- package/src/components/library/ui/Collapsible.tsx +31 -0
- package/src/components/library/ui/Container.jsx +56 -0
- package/src/components/library/ui/DatePicker.tsx +34 -0
- package/src/components/library/ui/Dialog.tsx +141 -0
- package/src/components/library/ui/EmptyState.jsx +46 -0
- package/src/components/library/ui/Field.tsx +82 -0
- package/src/components/library/ui/FieldGroup.jsx +17 -0
- package/src/components/library/ui/Input.jsx +21 -0
- package/src/components/library/ui/Label.jsx +22 -0
- package/src/components/library/ui/PaginationExtras.tsx +142 -0
- package/src/components/library/ui/Popover.tsx +39 -0
- package/src/components/library/ui/Select.tsx +113 -0
- package/src/components/library/ui/Spinner.d.ts +10 -0
- package/src/components/library/ui/Spinner.jsx +64 -0
- package/src/components/library/ui/Text.jsx +46 -0
- package/src/components/library/ui/Toggle.jsx +42 -0
- package/src/components/workspace/ComponentRegistry.jsx +297 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/utils.ts +6 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
// Shadcn-style Breadcrumb subcomponents to work with HeroUI Breadcrumbs
|
|
5
|
+
|
|
6
|
+
function Breadcrumb({ className, children, ...props }: React.ComponentProps<"nav">) {
|
|
7
|
+
return (
|
|
8
|
+
<nav aria-label="breadcrumb" className={className} {...props}>
|
|
9
|
+
{children}
|
|
10
|
+
</nav>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function BreadcrumbList({ className, children, ...props }: React.ComponentProps<"ol">) {
|
|
15
|
+
return (
|
|
16
|
+
<ol
|
|
17
|
+
className={[
|
|
18
|
+
"flex flex-wrap items-center gap-1.5 break-words text-sm text-slate-500 dark:text-slate-400",
|
|
19
|
+
className
|
|
20
|
+
].filter(Boolean).join(" ")}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</ol>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function BreadcrumbItem({ className, children, ...props }: React.ComponentProps<"li">) {
|
|
29
|
+
return (
|
|
30
|
+
<li
|
|
31
|
+
className={[
|
|
32
|
+
"inline-flex items-center gap-1.5",
|
|
33
|
+
className
|
|
34
|
+
].filter(Boolean).join(" ")}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</li>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function BreadcrumbLink({ className, href, children, ...props }: React.ComponentProps<typeof Link>) {
|
|
43
|
+
return (
|
|
44
|
+
<Link
|
|
45
|
+
to={href || "#"}
|
|
46
|
+
className={[
|
|
47
|
+
"transition-colors hover:text-slate-900 dark:hover:text-slate-50",
|
|
48
|
+
className
|
|
49
|
+
].filter(Boolean).join(" ")}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
{children}
|
|
53
|
+
</Link>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function BreadcrumbPage({ className, children, ...props }: React.ComponentProps<"span">) {
|
|
58
|
+
return (
|
|
59
|
+
<span
|
|
60
|
+
role="link"
|
|
61
|
+
aria-disabled="true"
|
|
62
|
+
aria-current="page"
|
|
63
|
+
className={[
|
|
64
|
+
"font-normal text-slate-900 dark:text-slate-50",
|
|
65
|
+
className
|
|
66
|
+
].filter(Boolean).join(" ")}
|
|
67
|
+
{...props}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</span>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">) {
|
|
75
|
+
return (
|
|
76
|
+
<li
|
|
77
|
+
role="presentation"
|
|
78
|
+
aria-hidden="true"
|
|
79
|
+
className={[
|
|
80
|
+
"select-none",
|
|
81
|
+
className
|
|
82
|
+
].filter(Boolean).join(" ")}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
{children ?? "/"}
|
|
86
|
+
</li>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">) {
|
|
91
|
+
return (
|
|
92
|
+
<span
|
|
93
|
+
role="presentation"
|
|
94
|
+
aria-hidden="true"
|
|
95
|
+
className={[
|
|
96
|
+
"flex h-9 w-9 items-center justify-center",
|
|
97
|
+
className
|
|
98
|
+
].filter(Boolean).join(" ")}
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none">
|
|
102
|
+
<path
|
|
103
|
+
d="M3.625 7.5C3.625 8.12132 3.12132 8.625 2.5 8.625C1.87868 8.625 1.375 8.12132 1.375 7.5C1.375 6.87868 1.87868 6.375 2.5 6.375C3.12132 6.375 3.625 6.87868 3.625 7.5ZM8.625 7.5C8.625 8.12132 8.12132 8.625 7.5 8.625C6.87868 8.625 6.375 8.12132 6.375 7.5C6.375 6.87868 6.87868 6.375 7.5 6.375C8.12132 6.375 8.625 6.87868 8.625 7.5ZM12.5 8.625C13.1213 8.625 13.625 8.12132 13.625 7.5C13.625 6.87868 13.1213 6.375 12.5 6.375C11.8787 6.375 11.375 6.87868 11.375 7.5C11.375 8.12132 11.8787 8.625 12.5 8.625Z"
|
|
104
|
+
fill="currentColor"
|
|
105
|
+
/>
|
|
106
|
+
</svg>
|
|
107
|
+
<span className="sr-only">More</span>
|
|
108
|
+
</span>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
Breadcrumb,
|
|
114
|
+
BreadcrumbList,
|
|
115
|
+
BreadcrumbItem,
|
|
116
|
+
BreadcrumbLink,
|
|
117
|
+
BreadcrumbPage,
|
|
118
|
+
BreadcrumbSeparator,
|
|
119
|
+
BreadcrumbEllipsis,
|
|
120
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const VARIANT_CLASSES = {
|
|
4
|
+
primary:
|
|
5
|
+
"bg-brand-600 text-white hover:bg-brand-500 dark:bg-brand-500 dark:hover:bg-brand-400 border-transparent",
|
|
6
|
+
secondary:
|
|
7
|
+
"bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200 border-transparent",
|
|
8
|
+
destructive:
|
|
9
|
+
"bg-red-600 text-white hover:bg-red-500 dark:bg-red-500 dark:hover:bg-red-400 border-transparent",
|
|
10
|
+
outline:
|
|
11
|
+
"bg-transparent text-slate-900 hover:bg-slate-50 dark:text-slate-50 dark:hover:bg-slate-900 border-slate-200 dark:border-slate-800",
|
|
12
|
+
ghost:
|
|
13
|
+
"bg-transparent text-slate-900 hover:bg-slate-100 dark:text-slate-50 dark:hover:bg-slate-900 border-transparent"
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const SIZE_CLASSES = {
|
|
17
|
+
sm: "h-8 px-3 text-sm",
|
|
18
|
+
md: "h-10 px-4 text-sm",
|
|
19
|
+
lg: "h-12 px-5 text-base",
|
|
20
|
+
icon: "h-10 w-10 p-0"
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default function UIButton({
|
|
24
|
+
variant = "primary",
|
|
25
|
+
size = "md",
|
|
26
|
+
fullWidth = false,
|
|
27
|
+
disabled = false,
|
|
28
|
+
onClick = () => {},
|
|
29
|
+
children,
|
|
30
|
+
style = undefined,
|
|
31
|
+
className = "",
|
|
32
|
+
...rest
|
|
33
|
+
}) {
|
|
34
|
+
const variantClass = VARIANT_CLASSES[variant] ?? VARIANT_CLASSES.primary;
|
|
35
|
+
const sizeClass = SIZE_CLASSES[size] ?? SIZE_CLASSES.md;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<button
|
|
39
|
+
type="button"
|
|
40
|
+
onClick={onClick}
|
|
41
|
+
disabled={disabled}
|
|
42
|
+
style={style}
|
|
43
|
+
className={[
|
|
44
|
+
"inline-flex items-center justify-center gap-2 rounded-lg border font-medium shadow-sm transition",
|
|
45
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950",
|
|
46
|
+
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
47
|
+
variantClass,
|
|
48
|
+
sizeClass,
|
|
49
|
+
fullWidth ? "w-full" : "",
|
|
50
|
+
className
|
|
51
|
+
]
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.join(" ")}
|
|
54
|
+
{...rest}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function UICard({ children, padding = "p-5", style, className = "", ...rest }) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
style={style}
|
|
7
|
+
className={[
|
|
8
|
+
"rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",
|
|
9
|
+
padding,
|
|
10
|
+
className
|
|
11
|
+
]
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
.join(" ")}
|
|
14
|
+
{...rest}
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// shadcn-compatible Card components
|
|
22
|
+
export function Card({ className = "", children, ...rest }) {
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={[
|
|
26
|
+
"rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",
|
|
27
|
+
className
|
|
28
|
+
]
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.join(" ")}
|
|
31
|
+
{...rest}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function CardHeader({ className = "", children, ...rest }) {
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
className={[
|
|
42
|
+
"flex flex-col space-y-1.5 p-6",
|
|
43
|
+
className
|
|
44
|
+
]
|
|
45
|
+
.filter(Boolean)
|
|
46
|
+
.join(" ")}
|
|
47
|
+
{...rest}
|
|
48
|
+
>
|
|
49
|
+
{children}
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function CardTitle({ className = "", children, ...rest }) {
|
|
55
|
+
return (
|
|
56
|
+
<h3
|
|
57
|
+
className={[
|
|
58
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
59
|
+
className
|
|
60
|
+
]
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.join(" ")}
|
|
63
|
+
{...rest}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</h3>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function CardDescription({ className = "", children, ...rest }) {
|
|
71
|
+
return (
|
|
72
|
+
<p
|
|
73
|
+
className={[
|
|
74
|
+
"text-sm text-slate-500 dark:text-slate-400",
|
|
75
|
+
className
|
|
76
|
+
]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join(" ")}
|
|
79
|
+
{...rest}
|
|
80
|
+
>
|
|
81
|
+
{children}
|
|
82
|
+
</p>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function CardContent({ className = "", children, ...rest }) {
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
className={[
|
|
90
|
+
"p-6 pt-0",
|
|
91
|
+
className
|
|
92
|
+
]
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.join(" ")}
|
|
95
|
+
{...rest}
|
|
96
|
+
>
|
|
97
|
+
{children}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function CardFooter({ className = "", children, ...rest }) {
|
|
103
|
+
return (
|
|
104
|
+
<div
|
|
105
|
+
className={[
|
|
106
|
+
"flex items-center p-6 pt-0",
|
|
107
|
+
className
|
|
108
|
+
]
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.join(" ")}
|
|
111
|
+
{...rest}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function Checkbox({ className = "", ...rest }) {
|
|
4
|
+
return (
|
|
5
|
+
<input
|
|
6
|
+
type="checkbox"
|
|
7
|
+
className={[
|
|
8
|
+
"h-4 w-4 rounded border-slate-300 text-brand-600 focus:ring-brand-500",
|
|
9
|
+
"dark:border-slate-600 dark:bg-slate-800 dark:focus:ring-brand-400",
|
|
10
|
+
className
|
|
11
|
+
]
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
.join(" ")}
|
|
14
|
+
{...rest}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const TONE_STYLES = {
|
|
4
|
+
neutral:
|
|
5
|
+
"border-slate-200/80 bg-white/60 text-slate-700 ring-black/5 hover:bg-white/80 dark:border-slate-800/80 dark:bg-slate-950/30 dark:text-slate-200 dark:ring-white/10 dark:hover:bg-slate-900/50",
|
|
6
|
+
primary:
|
|
7
|
+
"border-brand-200/80 bg-brand-50/70 text-brand-800 ring-brand-900/5 hover:bg-brand-50 dark:border-brand-900/40 dark:bg-brand-950/25 dark:text-brand-200 dark:ring-brand-300/10 dark:hover:bg-brand-950/35",
|
|
8
|
+
success:
|
|
9
|
+
"border-emerald-200/80 bg-emerald-50/70 text-emerald-800 ring-emerald-900/5 hover:bg-emerald-50 dark:border-emerald-900/40 dark:bg-emerald-950/20 dark:text-emerald-200 dark:ring-emerald-300/10 dark:hover:bg-emerald-950/30",
|
|
10
|
+
warning:
|
|
11
|
+
"border-amber-200/80 bg-amber-50/70 text-amber-900 ring-amber-900/5 hover:bg-amber-50 dark:border-amber-900/40 dark:bg-amber-950/20 dark:text-amber-200 dark:ring-amber-300/10 dark:hover:bg-amber-950/30",
|
|
12
|
+
danger:
|
|
13
|
+
"border-rose-200/80 bg-rose-50/70 text-rose-900 ring-rose-900/5 hover:bg-rose-50 dark:border-rose-900/40 dark:bg-rose-950/20 dark:text-rose-200 dark:ring-rose-300/10 dark:hover:bg-rose-950/30"
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const SIZE_STYLES = {
|
|
17
|
+
xs: "px-2 py-0.5 text-[11px]",
|
|
18
|
+
sm: "px-2.5 py-1 text-xs"
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function UIChip({ tone = "neutral", size = "xs", className = "", children, ...rest }) {
|
|
22
|
+
return (
|
|
23
|
+
<span
|
|
24
|
+
className={[
|
|
25
|
+
"inline-flex items-center gap-1 rounded-full border font-semibold shadow-sm ring-1 transition",
|
|
26
|
+
SIZE_STYLES[size] ?? SIZE_STYLES.xs,
|
|
27
|
+
TONE_STYLES[tone] ?? TONE_STYLES.neutral,
|
|
28
|
+
className
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(" ")}
|
|
32
|
+
{...rest}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</span>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
|
3
|
+
|
|
4
|
+
function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
5
|
+
return <CollapsiblePrimitive.Root {...props} />;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function CollapsibleTrigger({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Trigger>) {
|
|
9
|
+
return <CollapsiblePrimitive.Trigger {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function CollapsibleContent({
|
|
13
|
+
className,
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.Content>) {
|
|
16
|
+
return (
|
|
17
|
+
<CollapsiblePrimitive.Content
|
|
18
|
+
className={[
|
|
19
|
+
"overflow-hidden data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
20
|
+
className
|
|
21
|
+
].filter(Boolean).join(" ")}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
Collapsible,
|
|
29
|
+
CollapsibleTrigger,
|
|
30
|
+
CollapsibleContent,
|
|
31
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import UIText from "./Text";
|
|
3
|
+
|
|
4
|
+
export default function UIContainer({
|
|
5
|
+
title,
|
|
6
|
+
subtitle,
|
|
7
|
+
description,
|
|
8
|
+
actions,
|
|
9
|
+
empty = false,
|
|
10
|
+
emptyText = "Nothing here yet.",
|
|
11
|
+
emptyIcon,
|
|
12
|
+
emptyHeight = 160,
|
|
13
|
+
className = "",
|
|
14
|
+
style,
|
|
15
|
+
children
|
|
16
|
+
}) {
|
|
17
|
+
const sub = subtitle ?? description;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<section className={className} style={style}>
|
|
21
|
+
{(title || sub || actions) && (
|
|
22
|
+
<div className="mb-4 flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
|
|
23
|
+
<div className="min-w-0">
|
|
24
|
+
{title && (
|
|
25
|
+
<UIText as="h2" size="lg" weight="bold" className="truncate">
|
|
26
|
+
{title}
|
|
27
|
+
</UIText>
|
|
28
|
+
)}
|
|
29
|
+
{sub && (
|
|
30
|
+
<UIText as="p" size="sm" muted className="mt-1">
|
|
31
|
+
{sub}
|
|
32
|
+
</UIText>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
{actions && <div className="flex shrink-0 items-center gap-2">{actions}</div>}
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
|
|
39
|
+
{empty ? (
|
|
40
|
+
<div
|
|
41
|
+
className="flex w-full flex-col items-center justify-center rounded-2xl border border-dashed border-slate-300 bg-slate-50 px-6 text-center dark:border-slate-700 dark:bg-slate-900/40"
|
|
42
|
+
style={{ minHeight: emptyHeight }}
|
|
43
|
+
>
|
|
44
|
+
{emptyIcon ? <div className="mb-2">{emptyIcon}</div> : null}
|
|
45
|
+
<UIText size="sm" muted>
|
|
46
|
+
{emptyText}
|
|
47
|
+
</UIText>
|
|
48
|
+
</div>
|
|
49
|
+
) : (
|
|
50
|
+
children
|
|
51
|
+
)}
|
|
52
|
+
</section>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./Popover";
|
|
3
|
+
|
|
4
|
+
// Simplified DatePicker components
|
|
5
|
+
// Full implementation would use react-day-picker
|
|
6
|
+
|
|
7
|
+
function DatePicker({ children, ...props }: { children: React.ReactNode } & React.ComponentProps<typeof Popover>) {
|
|
8
|
+
return <Popover {...props}>{children}</Popover>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function DatePickerTrigger({ ...props }: React.ComponentProps<typeof PopoverTrigger>) {
|
|
12
|
+
return <PopoverTrigger {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DatePickerContent({ ...props }: React.ComponentProps<typeof PopoverContent>) {
|
|
16
|
+
return <PopoverContent {...props} />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function DatePickerCalendar({ ...props }: any) {
|
|
20
|
+
// Simplified calendar placeholder
|
|
21
|
+
return <div {...props}>Calendar component</div>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function DatePickerRangeTrigger({ ...props }: React.ComponentProps<typeof PopoverTrigger>) {
|
|
25
|
+
return <PopoverTrigger {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
DatePicker,
|
|
30
|
+
DatePickerTrigger,
|
|
31
|
+
DatePickerContent,
|
|
32
|
+
DatePickerCalendar,
|
|
33
|
+
DatePickerRangeTrigger,
|
|
34
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
3
|
+
|
|
4
|
+
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
5
|
+
return <DialogPrimitive.Root {...props} />;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
9
|
+
return <DialogPrimitive.Trigger {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
13
|
+
return <DialogPrimitive.Portal {...props} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
17
|
+
return <DialogPrimitive.Close {...props} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DialogOverlay({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
24
|
+
return (
|
|
25
|
+
<DialogPrimitive.Overlay
|
|
26
|
+
className={[
|
|
27
|
+
"fixed inset-0 z-50 bg-black/50",
|
|
28
|
+
className
|
|
29
|
+
].filter(Boolean).join(" ")}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function DialogContent({
|
|
36
|
+
className,
|
|
37
|
+
children,
|
|
38
|
+
showCloseButton = true,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & { showCloseButton?: boolean }) {
|
|
41
|
+
return (
|
|
42
|
+
<DialogPortal>
|
|
43
|
+
<DialogOverlay />
|
|
44
|
+
<DialogPrimitive.Content
|
|
45
|
+
className={[
|
|
46
|
+
"fixed left-1/2 top-1/2 z-50 max-h-[85vh] w-[90vw] max-w-[500px] -translate-x-1/2 -translate-y-1/2",
|
|
47
|
+
"rounded-lg border border-slate-200 bg-white p-6 shadow-lg",
|
|
48
|
+
"dark:border-slate-800 dark:bg-slate-900",
|
|
49
|
+
className
|
|
50
|
+
].filter(Boolean).join(" ")}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
{showCloseButton && (
|
|
55
|
+
<DialogClose className="absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100">
|
|
56
|
+
<span className="sr-only">Close</span>
|
|
57
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none">
|
|
58
|
+
<path
|
|
59
|
+
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
|
|
60
|
+
fill="currentColor"
|
|
61
|
+
/>
|
|
62
|
+
</svg>
|
|
63
|
+
</DialogClose>
|
|
64
|
+
)}
|
|
65
|
+
</DialogPrimitive.Content>
|
|
66
|
+
</DialogPortal>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function DialogHeader({
|
|
71
|
+
className,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<"div">) {
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
className={[
|
|
77
|
+
"flex flex-col space-y-1.5 text-center sm:text-left",
|
|
78
|
+
className
|
|
79
|
+
].filter(Boolean).join(" ")}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function DialogFooter({
|
|
86
|
+
className,
|
|
87
|
+
...props
|
|
88
|
+
}: React.ComponentProps<"div">) {
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
className={[
|
|
92
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
93
|
+
className
|
|
94
|
+
].filter(Boolean).join(" ")}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function DialogTitle({
|
|
101
|
+
className,
|
|
102
|
+
...props
|
|
103
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
104
|
+
return (
|
|
105
|
+
<DialogPrimitive.Title
|
|
106
|
+
className={[
|
|
107
|
+
"text-lg font-semibold leading-none tracking-tight",
|
|
108
|
+
className
|
|
109
|
+
].filter(Boolean).join(" ")}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function DialogDescription({
|
|
116
|
+
className,
|
|
117
|
+
...props
|
|
118
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
119
|
+
return (
|
|
120
|
+
<DialogPrimitive.Description
|
|
121
|
+
className={[
|
|
122
|
+
"text-sm text-slate-500 dark:text-slate-400",
|
|
123
|
+
className
|
|
124
|
+
].filter(Boolean).join(" ")}
|
|
125
|
+
{...props}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
Dialog,
|
|
132
|
+
DialogTrigger,
|
|
133
|
+
DialogPortal,
|
|
134
|
+
DialogClose,
|
|
135
|
+
DialogOverlay,
|
|
136
|
+
DialogContent,
|
|
137
|
+
DialogHeader,
|
|
138
|
+
DialogFooter,
|
|
139
|
+
DialogTitle,
|
|
140
|
+
DialogDescription,
|
|
141
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const SIZES = {
|
|
4
|
+
sm: { icon: "h-8 w-8", heading: "text-sm", body: "text-xs", gap: "gap-2", pad: "py-6" },
|
|
5
|
+
md: { icon: "h-10 w-10", heading: "text-base", body: "text-sm", gap: "gap-3", pad: "py-10" },
|
|
6
|
+
lg: { icon: "h-14 w-14", heading: "text-lg", body: "text-sm", gap: "gap-4", pad: "py-16" },
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Empty state placeholder — shows when a list, table, or section has no content.
|
|
11
|
+
*
|
|
12
|
+
* @param {ReactNode} icon Optional icon element
|
|
13
|
+
* @param {string} heading Primary message
|
|
14
|
+
* @param {string} body Secondary description
|
|
15
|
+
* @param {ReactNode} action Optional CTA (button, link, etc.)
|
|
16
|
+
* @param {"sm"|"md"|"lg"} size Visual density
|
|
17
|
+
*/
|
|
18
|
+
export default function EmptyState({
|
|
19
|
+
icon,
|
|
20
|
+
heading = "Nothing here yet",
|
|
21
|
+
body,
|
|
22
|
+
action,
|
|
23
|
+
size = "md",
|
|
24
|
+
className = "",
|
|
25
|
+
}) {
|
|
26
|
+
const s = SIZES[size] ?? SIZES.md;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className={`flex flex-col items-center justify-center text-center ${s.pad} ${s.gap} ${className}`}>
|
|
30
|
+
{icon && (
|
|
31
|
+
<div className={`${s.icon} text-slate-300 dark:text-slate-600`}>
|
|
32
|
+
{icon}
|
|
33
|
+
</div>
|
|
34
|
+
)}
|
|
35
|
+
<div className={`${s.heading} font-semibold text-slate-700 dark:text-slate-200`}>
|
|
36
|
+
{heading}
|
|
37
|
+
</div>
|
|
38
|
+
{body && (
|
|
39
|
+
<div className={`${s.body} max-w-sm text-slate-500 dark:text-slate-400`}>
|
|
40
|
+
{body}
|
|
41
|
+
</div>
|
|
42
|
+
)}
|
|
43
|
+
{action && <div className="mt-1">{action}</div>}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|