@schandlergarcia/sf-web-components 1.9.37 → 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 +116 -65
- 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,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
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
function Field({
|
|
4
|
+
className,
|
|
5
|
+
children,
|
|
6
|
+
...props
|
|
7
|
+
}: React.ComponentProps<"div">) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
className={[
|
|
11
|
+
"flex flex-col gap-1.5",
|
|
12
|
+
className
|
|
13
|
+
].filter(Boolean).join(" ")}
|
|
14
|
+
{...props}
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function FieldLabel({
|
|
22
|
+
className,
|
|
23
|
+
htmlFor,
|
|
24
|
+
children,
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<"label">) {
|
|
27
|
+
return (
|
|
28
|
+
<label
|
|
29
|
+
htmlFor={htmlFor}
|
|
30
|
+
className={[
|
|
31
|
+
"block text-sm font-medium text-slate-700 dark:text-slate-200",
|
|
32
|
+
className
|
|
33
|
+
].filter(Boolean).join(" ")}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</label>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function FieldDescription({
|
|
42
|
+
className,
|
|
43
|
+
children,
|
|
44
|
+
...props
|
|
45
|
+
}: React.ComponentProps<"p">) {
|
|
46
|
+
return (
|
|
47
|
+
<p
|
|
48
|
+
className={[
|
|
49
|
+
"text-xs text-slate-500 dark:text-slate-400",
|
|
50
|
+
className
|
|
51
|
+
].filter(Boolean).join(" ")}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
</p>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function FieldError({
|
|
60
|
+
className,
|
|
61
|
+
children,
|
|
62
|
+
...props
|
|
63
|
+
}: React.ComponentProps<"p">) {
|
|
64
|
+
return (
|
|
65
|
+
<p
|
|
66
|
+
className={[
|
|
67
|
+
"text-xs text-red-600 dark:text-red-400",
|
|
68
|
+
className
|
|
69
|
+
].filter(Boolean).join(" ")}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</p>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
Field,
|
|
79
|
+
FieldLabel,
|
|
80
|
+
FieldDescription,
|
|
81
|
+
FieldError,
|
|
82
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function FieldGroup({ className = "", children, ...rest }) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
className={[
|
|
7
|
+
"flex flex-col gap-1.5",
|
|
8
|
+
className
|
|
9
|
+
]
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
.join(" ")}
|
|
12
|
+
{...rest}
|
|
13
|
+
>
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function UIInput({ style = undefined, className = "", ...rest }) {
|
|
4
|
+
return (
|
|
5
|
+
<input
|
|
6
|
+
style={style}
|
|
7
|
+
className={[
|
|
8
|
+
"h-10 w-full rounded-lg border border-slate-200 bg-white px-3 text-sm text-slate-900 shadow-sm",
|
|
9
|
+
"placeholder:text-slate-400",
|
|
10
|
+
"focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-slate-950",
|
|
11
|
+
"dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500",
|
|
12
|
+
className
|
|
13
|
+
]
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.join(" ")}
|
|
16
|
+
{...rest}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function Label({ children, htmlFor, required, className = "", ...rest }) {
|
|
4
|
+
// If no htmlFor provided, render as div for compatibility
|
|
5
|
+
const Tag = htmlFor ? 'label' : 'div';
|
|
6
|
+
const props = htmlFor ? { htmlFor, ...rest } : rest;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<Tag
|
|
10
|
+
{...props}
|
|
11
|
+
className={[
|
|
12
|
+
"text-sm font-medium text-slate-700 dark:text-slate-200",
|
|
13
|
+
className
|
|
14
|
+
]
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.join(" ")}
|
|
17
|
+
>
|
|
18
|
+
{children}
|
|
19
|
+
{required && <span className="ml-0.5 text-red-500">*</span>}
|
|
20
|
+
</Tag>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
// Shadcn-style Pagination subcomponents
|
|
5
|
+
|
|
6
|
+
function PaginationContent({ className, children, ...props }: React.ComponentProps<"ul">) {
|
|
7
|
+
return (
|
|
8
|
+
<ul
|
|
9
|
+
className={[
|
|
10
|
+
"flex flex-row items-center gap-1",
|
|
11
|
+
className
|
|
12
|
+
].filter(Boolean).join(" ")}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</ul>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function PaginationItem({ className, children, ...props }: React.ComponentProps<"li">) {
|
|
21
|
+
return (
|
|
22
|
+
<li
|
|
23
|
+
className={className}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
{children}
|
|
27
|
+
</li>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function PaginationLink({
|
|
32
|
+
className,
|
|
33
|
+
isActive,
|
|
34
|
+
size = "default",
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof Link> & {
|
|
37
|
+
isActive?: boolean;
|
|
38
|
+
size?: "default" | "sm" | "lg";
|
|
39
|
+
}) {
|
|
40
|
+
const sizeClasses = {
|
|
41
|
+
default: "h-9 min-w-9 px-4 py-2",
|
|
42
|
+
sm: "h-8 min-w-8 px-3 py-1.5 text-sm",
|
|
43
|
+
lg: "h-11 min-w-11 px-8 py-3"
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Link
|
|
48
|
+
aria-current={isActive ? "page" : undefined}
|
|
49
|
+
className={[
|
|
50
|
+
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
|
|
51
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500",
|
|
52
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
53
|
+
isActive
|
|
54
|
+
? "bg-slate-900 text-white dark:bg-slate-50 dark:text-slate-900"
|
|
55
|
+
: "hover:bg-slate-100 dark:hover:bg-slate-800",
|
|
56
|
+
sizeClasses[size],
|
|
57
|
+
className
|
|
58
|
+
].filter(Boolean).join(" ")}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function PaginationPrevious({
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: React.ComponentProps<typeof PaginationLink>) {
|
|
68
|
+
return (
|
|
69
|
+
<PaginationLink
|
|
70
|
+
aria-label="Go to previous page"
|
|
71
|
+
size="default"
|
|
72
|
+
className={[
|
|
73
|
+
"gap-1 pl-2.5",
|
|
74
|
+
className
|
|
75
|
+
].filter(Boolean).join(" ")}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none">
|
|
79
|
+
<path
|
|
80
|
+
d="M8.84182 3.13514C9.04327 3.32401 9.05348 3.64042 8.86462 3.84188L5.43521 7.49991L8.86462 11.1579C9.05348 11.3594 9.04327 11.6758 8.84182 11.8647C8.64036 12.0535 8.32394 12.0433 8.13508 11.8419L4.38508 7.84188C4.20477 7.64955 4.20477 7.35027 4.38508 7.15794L8.13508 3.15794C8.32394 2.95648 8.64036 2.94628 8.84182 3.13514Z"
|
|
81
|
+
fill="currentColor"
|
|
82
|
+
/>
|
|
83
|
+
</svg>
|
|
84
|
+
<span>Previous</span>
|
|
85
|
+
</PaginationLink>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function PaginationNext({
|
|
90
|
+
className,
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<typeof PaginationLink>) {
|
|
93
|
+
return (
|
|
94
|
+
<PaginationLink
|
|
95
|
+
aria-label="Go to next page"
|
|
96
|
+
size="default"
|
|
97
|
+
className={[
|
|
98
|
+
"gap-1 pr-2.5",
|
|
99
|
+
className
|
|
100
|
+
].filter(Boolean).join(" ")}
|
|
101
|
+
{...props}
|
|
102
|
+
>
|
|
103
|
+
<span>Next</span>
|
|
104
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none">
|
|
105
|
+
<path
|
|
106
|
+
d="M6.1584 3.13514C6.35985 2.94628 6.67627 2.95648 6.86513 3.15794L10.6151 7.15794C10.7954 7.35027 10.7954 7.64955 10.6151 7.84188L6.86513 11.8419C6.67627 12.0433 6.35985 12.0535 6.1584 11.8647C5.95694 11.6758 5.94673 11.3594 6.13559 11.1579L9.565 7.49991L6.13559 3.84188C5.94673 3.64042 5.95694 3.32401 6.1584 3.13514Z"
|
|
107
|
+
fill="currentColor"
|
|
108
|
+
/>
|
|
109
|
+
</svg>
|
|
110
|
+
</PaginationLink>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">) {
|
|
115
|
+
return (
|
|
116
|
+
<span
|
|
117
|
+
aria-hidden
|
|
118
|
+
className={[
|
|
119
|
+
"flex h-9 w-9 items-center justify-center",
|
|
120
|
+
className
|
|
121
|
+
].filter(Boolean).join(" ")}
|
|
122
|
+
{...props}
|
|
123
|
+
>
|
|
124
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none">
|
|
125
|
+
<path
|
|
126
|
+
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"
|
|
127
|
+
fill="currentColor"
|
|
128
|
+
/>
|
|
129
|
+
</svg>
|
|
130
|
+
<span className="sr-only">More pages</span>
|
|
131
|
+
</span>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
PaginationContent,
|
|
137
|
+
PaginationItem,
|
|
138
|
+
PaginationLink,
|
|
139
|
+
PaginationPrevious,
|
|
140
|
+
PaginationNext,
|
|
141
|
+
PaginationEllipsis,
|
|
142
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
3
|
+
|
|
4
|
+
function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
|
|
5
|
+
return <PopoverPrimitive.Root {...props} />;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
|
|
9
|
+
return <PopoverPrimitive.Trigger {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function PopoverContent({
|
|
13
|
+
className,
|
|
14
|
+
align = "center",
|
|
15
|
+
sideOffset = 4,
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
|
|
18
|
+
return (
|
|
19
|
+
<PopoverPrimitive.Portal>
|
|
20
|
+
<PopoverPrimitive.Content
|
|
21
|
+
align={align}
|
|
22
|
+
sideOffset={sideOffset}
|
|
23
|
+
className={[
|
|
24
|
+
"z-50 w-72 rounded-md border border-slate-200 bg-white p-4 text-slate-900 shadow-md outline-none",
|
|
25
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
26
|
+
"dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50",
|
|
27
|
+
className
|
|
28
|
+
].filter(Boolean).join(" ")}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
</PopoverPrimitive.Portal>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
Popover,
|
|
37
|
+
PopoverTrigger,
|
|
38
|
+
PopoverContent,
|
|
39
|
+
};
|