@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,165 @@
|
|
|
1
|
+
import { useState, useMemo, useCallback, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MIN_SUBMIT_MS = 4000;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts all field ids and their default values from a form schema.
|
|
7
|
+
*/
|
|
8
|
+
function buildDefaults(sections) {
|
|
9
|
+
const defaults = {};
|
|
10
|
+
for (const section of sections) {
|
|
11
|
+
for (const field of section.fields ?? []) {
|
|
12
|
+
if (field.type === "checkboxGroup") {
|
|
13
|
+
defaults[field.id] = [];
|
|
14
|
+
} else if (field.type === "toggle" || field.type === "checkbox") {
|
|
15
|
+
defaults[field.id] = false;
|
|
16
|
+
} else {
|
|
17
|
+
defaults[field.id] = "";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return defaults;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Runs validation for all fields.
|
|
26
|
+
* Returns an object of { fieldId: errorMessage } (empty object = valid).
|
|
27
|
+
*/
|
|
28
|
+
function runValidation(values, sections) {
|
|
29
|
+
const errors = {};
|
|
30
|
+
for (const section of sections) {
|
|
31
|
+
for (const field of section.fields ?? []) {
|
|
32
|
+
const val = values[field.id];
|
|
33
|
+
|
|
34
|
+
if (field.required) {
|
|
35
|
+
const empty =
|
|
36
|
+
val === undefined ||
|
|
37
|
+
val === null ||
|
|
38
|
+
val === "" ||
|
|
39
|
+
(Array.isArray(val) && val.length === 0);
|
|
40
|
+
if (empty) {
|
|
41
|
+
errors[field.id] = field.requiredMessage ?? `${field.label ?? field.id} is required`;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (field.validate) {
|
|
47
|
+
const msg = field.validate(val, values);
|
|
48
|
+
if (msg) errors[field.id] = msg;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return errors;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Form state management hook.
|
|
57
|
+
*
|
|
58
|
+
* @param {Object} options
|
|
59
|
+
* @param {Object} options.initialValues — prefill for editing (merged over field defaults)
|
|
60
|
+
* @param {Array} options.sections — form schema sections (used for defaults + validation)
|
|
61
|
+
* @param {Function} options.onSubmit — called with (values) when form is valid
|
|
62
|
+
* @param {number} options.minSubmitMs — minimum time the submit spinner shows (default 4000ms, set 0 to disable)
|
|
63
|
+
*
|
|
64
|
+
* @returns {Object} { values, errors, touched, isDirty, isValid, isSubmitting,
|
|
65
|
+
* setValue, setValues, setTouched, validate, reset, handleSubmit }
|
|
66
|
+
*/
|
|
67
|
+
export default function useFormState({ initialValues = {}, sections = [], onSubmit, minSubmitMs = DEFAULT_MIN_SUBMIT_MS } = {}) {
|
|
68
|
+
const defaults = useMemo(() => buildDefaults(sections), [sections]);
|
|
69
|
+
const merged = useMemo(() => ({ ...defaults, ...initialValues }), [defaults, initialValues]);
|
|
70
|
+
|
|
71
|
+
const [values, setValuesState] = useState(merged);
|
|
72
|
+
const [errors, setErrors] = useState({});
|
|
73
|
+
const [touched, setTouchedState] = useState({});
|
|
74
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
75
|
+
const submitRef = useRef(onSubmit);
|
|
76
|
+
submitRef.current = onSubmit;
|
|
77
|
+
|
|
78
|
+
const isDirty = useMemo(() => {
|
|
79
|
+
return Object.keys(merged).some((k) => {
|
|
80
|
+
const a = merged[k];
|
|
81
|
+
const b = values[k];
|
|
82
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
83
|
+
return a.length !== b.length || a.some((v, i) => v !== b[i]);
|
|
84
|
+
}
|
|
85
|
+
return a !== b;
|
|
86
|
+
});
|
|
87
|
+
}, [merged, values]);
|
|
88
|
+
|
|
89
|
+
const setValue = useCallback((id, value) => {
|
|
90
|
+
setValuesState((prev) => ({ ...prev, [id]: value }));
|
|
91
|
+
setErrors((prev) => {
|
|
92
|
+
if (!prev[id]) return prev;
|
|
93
|
+
const next = { ...prev };
|
|
94
|
+
delete next[id];
|
|
95
|
+
return next;
|
|
96
|
+
});
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
const setValues = useCallback((vals) => {
|
|
100
|
+
setValuesState((prev) => ({ ...prev, ...vals }));
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
const setTouched = useCallback((id) => {
|
|
104
|
+
setTouchedState((prev) => (prev[id] ? prev : { ...prev, [id]: true }));
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
const validate = useCallback(() => {
|
|
108
|
+
const errs = runValidation(values, sections);
|
|
109
|
+
setErrors(errs);
|
|
110
|
+
const allTouched = {};
|
|
111
|
+
for (const section of sections) {
|
|
112
|
+
for (const field of section.fields ?? []) {
|
|
113
|
+
allTouched[field.id] = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
setTouchedState(allTouched);
|
|
117
|
+
return Object.keys(errs).length === 0;
|
|
118
|
+
}, [values, sections]);
|
|
119
|
+
|
|
120
|
+
const reset = useCallback(() => {
|
|
121
|
+
setValuesState(merged);
|
|
122
|
+
setErrors({});
|
|
123
|
+
setTouchedState({});
|
|
124
|
+
}, [merged]);
|
|
125
|
+
|
|
126
|
+
const handleSubmit = useCallback(
|
|
127
|
+
async (e) => {
|
|
128
|
+
e?.preventDefault?.();
|
|
129
|
+
const valid = validate();
|
|
130
|
+
if (!valid) return false;
|
|
131
|
+
|
|
132
|
+
setIsSubmitting(true);
|
|
133
|
+
try {
|
|
134
|
+
const delay = minSubmitMs > 0
|
|
135
|
+
? new Promise((r) => setTimeout(r, minSubmitMs))
|
|
136
|
+
: Promise.resolve();
|
|
137
|
+
await Promise.all([submitRef.current?.(values), delay]);
|
|
138
|
+
return true;
|
|
139
|
+
} catch (err) {
|
|
140
|
+
setErrors((prev) => ({ ...prev, _form: err?.message ?? "Submission failed" }));
|
|
141
|
+
return false;
|
|
142
|
+
} finally {
|
|
143
|
+
setIsSubmitting(false);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
[validate, values, minSubmitMs]
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const isValid = Object.keys(errors).length === 0;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
values,
|
|
153
|
+
errors,
|
|
154
|
+
touched,
|
|
155
|
+
isDirty,
|
|
156
|
+
isValid,
|
|
157
|
+
isSubmitting,
|
|
158
|
+
setValue,
|
|
159
|
+
setValues,
|
|
160
|
+
setTouched,
|
|
161
|
+
validate,
|
|
162
|
+
reset,
|
|
163
|
+
handleSubmit,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Accordion } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Accordion — compound component.
|
|
6
|
+
*
|
|
7
|
+
* Sub-components via dot notation on the named `Accordion` export:
|
|
8
|
+
* Accordion.Item, Accordion.Heading, Accordion.Trigger,
|
|
9
|
+
* Accordion.Panel, Accordion.Body, Accordion.Indicator
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { Accordion } from "@/components/library";
|
|
13
|
+
* <Accordion>
|
|
14
|
+
* <Accordion.Item>
|
|
15
|
+
* <Accordion.Heading>
|
|
16
|
+
* <Accordion.Trigger>Section 1<Accordion.Indicator /></Accordion.Trigger>
|
|
17
|
+
* </Accordion.Heading>
|
|
18
|
+
* <Accordion.Panel><Accordion.Body>Content</Accordion.Body></Accordion.Panel>
|
|
19
|
+
* </Accordion.Item>
|
|
20
|
+
* </Accordion>
|
|
21
|
+
*/
|
|
22
|
+
export default function HeroUIAccordion({ variant = "default", ...props }) {
|
|
23
|
+
return <Accordion variant={variant} {...props} />;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { Accordion };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Breadcrumbs } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Breadcrumbs — compound component.
|
|
6
|
+
*
|
|
7
|
+
* Sub-components via dot notation on the named `Breadcrumbs` export:
|
|
8
|
+
* Breadcrumbs.Item
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { Breadcrumbs } from "@/components/library";
|
|
12
|
+
* <Breadcrumbs>
|
|
13
|
+
* <Breadcrumbs.Item href="/">Home</Breadcrumbs.Item>
|
|
14
|
+
* <Breadcrumbs.Item href="/settings">Settings</Breadcrumbs.Item>
|
|
15
|
+
* <Breadcrumbs.Item>Profile</Breadcrumbs.Item>
|
|
16
|
+
* </Breadcrumbs>
|
|
17
|
+
*/
|
|
18
|
+
export default function HeroUIBreadcrumbs(props) {
|
|
19
|
+
return <Breadcrumbs {...props} />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Breadcrumbs };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Button as HeroButton } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Button wrapper with shadcn compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Maps shadcn variants to HeroUI:
|
|
8
|
+
* - primary/secondary/destructive -> solid
|
|
9
|
+
* - outline -> bordered
|
|
10
|
+
* - ghost -> light
|
|
11
|
+
*/
|
|
12
|
+
export default function HeroUIButton({
|
|
13
|
+
variant = "primary",
|
|
14
|
+
size = "md",
|
|
15
|
+
fullWidth,
|
|
16
|
+
onClick,
|
|
17
|
+
children,
|
|
18
|
+
className = "",
|
|
19
|
+
...props
|
|
20
|
+
}) {
|
|
21
|
+
// Map shadcn variants to HeroUI variants
|
|
22
|
+
const variantMap = {
|
|
23
|
+
primary: "solid",
|
|
24
|
+
secondary: "solid",
|
|
25
|
+
destructive: "solid",
|
|
26
|
+
outline: "bordered",
|
|
27
|
+
ghost: "light"
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Map shadcn variants to HeroUI colors
|
|
31
|
+
const colorMap = {
|
|
32
|
+
primary: "primary",
|
|
33
|
+
secondary: "default",
|
|
34
|
+
destructive: "danger",
|
|
35
|
+
outline: "default",
|
|
36
|
+
ghost: "default"
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const heroVariant = variantMap[variant] || "solid";
|
|
40
|
+
const heroColor = colorMap[variant] || "primary";
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<HeroButton
|
|
44
|
+
variant={heroVariant}
|
|
45
|
+
color={heroColor}
|
|
46
|
+
size={size}
|
|
47
|
+
fullWidth={fullWidth}
|
|
48
|
+
onPress={onClick}
|
|
49
|
+
className={className}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
{children}
|
|
53
|
+
</HeroButton>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Also export as Button for shadcn compatibility
|
|
58
|
+
export const Button = HeroUIButton;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Accordion } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Collapsible — maps to Accordion for shadcn compatibility.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/library";
|
|
9
|
+
* <Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
|
10
|
+
* <CollapsibleTrigger>Toggle</CollapsibleTrigger>
|
|
11
|
+
* <CollapsibleContent>Content</CollapsibleContent>
|
|
12
|
+
* </Collapsible>
|
|
13
|
+
*/
|
|
14
|
+
export default function HeroUICollapsible({ open, onOpenChange, children, ...props }) {
|
|
15
|
+
// Map shadcn props to HeroUI Accordion
|
|
16
|
+
const selectedKeys = open ? ["item"] : [];
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Accordion
|
|
20
|
+
selectedKeys={selectedKeys}
|
|
21
|
+
onSelectionChange={(keys) => {
|
|
22
|
+
const isOpen = Array.from(keys).includes("item");
|
|
23
|
+
onOpenChange?.(isOpen);
|
|
24
|
+
}}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
<Accordion.Item key="item" aria-label="Collapsible content">
|
|
28
|
+
{children}
|
|
29
|
+
</Accordion.Item>
|
|
30
|
+
</Accordion>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const Collapsible = HeroUICollapsible;
|
|
35
|
+
|
|
36
|
+
export const CollapsibleTrigger = ({ children, ...props }) => (
|
|
37
|
+
<Accordion.Trigger {...props}>{children}</Accordion.Trigger>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const CollapsibleContent = ({ children, ...props }) => (
|
|
41
|
+
<Accordion.Panel {...props}>{children}</Accordion.Panel>
|
|
42
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Popover, PopoverTrigger, PopoverContent } from "./Popover";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 DatePicker — simple wrapper using Popover.
|
|
6
|
+
* Full implementation would integrate with react-day-picker.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { DatePicker, DatePickerTrigger, DatePickerContent } from "@/components/library";
|
|
10
|
+
* <DatePicker>
|
|
11
|
+
* <DatePickerTrigger>
|
|
12
|
+
* <button>Select date</button>
|
|
13
|
+
* </DatePickerTrigger>
|
|
14
|
+
* <DatePickerContent>
|
|
15
|
+
* <DatePickerCalendar />
|
|
16
|
+
* </DatePickerContent>
|
|
17
|
+
* </DatePicker>
|
|
18
|
+
*/
|
|
19
|
+
export default function HeroUIDatePicker({ children, ...props }) {
|
|
20
|
+
return <Popover {...props}>{children}</Popover>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const DatePicker = HeroUIDatePicker;
|
|
24
|
+
export const DatePickerTrigger = ({ children, date, dateFormat, placeholder, className = "", ...props }) => {
|
|
25
|
+
// Accept shadcn DatePicker props but just pass through as trigger
|
|
26
|
+
return <PopoverTrigger {...props}>{children}</PopoverTrigger>;
|
|
27
|
+
};
|
|
28
|
+
export const DatePickerContent = PopoverContent;
|
|
29
|
+
export const DatePickerCalendar = ({ date, onSelect, ...props }) => (
|
|
30
|
+
<div className="p-4" {...props}>
|
|
31
|
+
<p className="text-sm text-slate-500">Calendar placeholder - integrate react-day-picker here</p>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
export const DatePickerRangeTrigger = DatePickerTrigger;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Modal } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Dialog — maps to Modal for shadcn compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Sub-components via dot notation on the named `Modal` export:
|
|
8
|
+
* Modal.Backdrop, Modal.Container, Modal.Dialog,
|
|
9
|
+
* Modal.CloseTrigger, Modal.Header, Modal.Icon,
|
|
10
|
+
* Modal.Heading, Modal.Body, Modal.Footer
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { Dialog, DialogTrigger, DialogContent } from "@/components/library";
|
|
14
|
+
* <Dialog>
|
|
15
|
+
* <DialogTrigger>Open</DialogTrigger>
|
|
16
|
+
* <DialogContent>
|
|
17
|
+
* <DialogHeader><DialogTitle>Title</DialogTitle></DialogHeader>
|
|
18
|
+
* <DialogDescription>Content</DialogDescription>
|
|
19
|
+
* <DialogFooter>Actions</DialogFooter>
|
|
20
|
+
* </DialogContent>
|
|
21
|
+
* </Dialog>
|
|
22
|
+
*/
|
|
23
|
+
export default function HeroUIDialog(props) {
|
|
24
|
+
return <Modal {...props} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Shadcn-compatible exports
|
|
28
|
+
export const Dialog = HeroUIDialog;
|
|
29
|
+
export const DialogTrigger = Modal.Trigger || (props => <button {...props} />);
|
|
30
|
+
export const DialogPortal = ({ children }) => <>{children}</>;
|
|
31
|
+
export const DialogClose = Modal.CloseTrigger || (props => <button {...props} />);
|
|
32
|
+
export const DialogOverlay = Modal.Backdrop || (props => <div {...props} />);
|
|
33
|
+
export const DialogContent = Modal.Dialog || Modal.Container || (props => <div {...props} />);
|
|
34
|
+
export const DialogHeader = Modal.Header || (props => <div {...props} />);
|
|
35
|
+
export const DialogFooter = Modal.Footer || (props => <div {...props} />);
|
|
36
|
+
export const DialogTitle = Modal.Heading || (props => <h2 {...props} />);
|
|
37
|
+
export const DialogDescription = (props) => <p {...props} />;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Drawer } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Drawer — compound component.
|
|
6
|
+
*
|
|
7
|
+
* Sub-components via dot notation on the named `Drawer` export:
|
|
8
|
+
* Drawer.Backdrop, Drawer.Content, Drawer.Dialog,
|
|
9
|
+
* Drawer.Header, Drawer.Heading, Drawer.Body, Drawer.Footer,
|
|
10
|
+
* Drawer.Handle, Drawer.CloseTrigger
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { Drawer } from "@/components/library";
|
|
14
|
+
* <Drawer>
|
|
15
|
+
* <Button>Open</Button>
|
|
16
|
+
* <Drawer.Backdrop>
|
|
17
|
+
* <Drawer.Content placement="right">
|
|
18
|
+
* <Drawer.Dialog>
|
|
19
|
+
* <Drawer.CloseTrigger />
|
|
20
|
+
* <Drawer.Header><Drawer.Heading>Details</Drawer.Heading></Drawer.Header>
|
|
21
|
+
* <Drawer.Body>…</Drawer.Body>
|
|
22
|
+
* <Drawer.Footer>…</Drawer.Footer>
|
|
23
|
+
* </Drawer.Dialog>
|
|
24
|
+
* </Drawer.Content>
|
|
25
|
+
* </Drawer.Backdrop>
|
|
26
|
+
* </Drawer>
|
|
27
|
+
*/
|
|
28
|
+
export default function HeroUIDrawer(props) {
|
|
29
|
+
return <Drawer {...props} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { Drawer };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Dropdown } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Dropdown — compound component.
|
|
6
|
+
*
|
|
7
|
+
* Sub-components via dot notation on the named `Dropdown` export:
|
|
8
|
+
* Dropdown.Trigger, Dropdown.Popover, Dropdown.Menu,
|
|
9
|
+
* Dropdown.Item, Dropdown.Section, Dropdown.ItemIndicator,
|
|
10
|
+
* Dropdown.SubmenuTrigger, Dropdown.SubmenuIndicator
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { Dropdown } from "@/components/library";
|
|
14
|
+
* <Dropdown>
|
|
15
|
+
* <Button>Actions</Button>
|
|
16
|
+
* <Dropdown.Popover>
|
|
17
|
+
* <Dropdown.Menu onAction={(key) => console.log(key)}>
|
|
18
|
+
* <Dropdown.Item id="edit" textValue="Edit"><Label>Edit</Label></Dropdown.Item>
|
|
19
|
+
* <Dropdown.Item id="delete" textValue="Delete" variant="danger"><Label>Delete</Label></Dropdown.Item>
|
|
20
|
+
* </Dropdown.Menu>
|
|
21
|
+
* </Dropdown.Popover>
|
|
22
|
+
* </Dropdown>
|
|
23
|
+
*/
|
|
24
|
+
export default function HeroUIDropdown(props) {
|
|
25
|
+
return <Dropdown {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { Dropdown };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HeroUI v3 Field components — simple wrappers for form field layout.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { Field, FieldLabel, FieldError } from "@/components/library";
|
|
8
|
+
* <Field>
|
|
9
|
+
* <FieldLabel>Name</FieldLabel>
|
|
10
|
+
* <Input />
|
|
11
|
+
* <FieldError>Required</FieldError>
|
|
12
|
+
* </Field>
|
|
13
|
+
*/
|
|
14
|
+
export default function HeroUIField({ className = "", children, ...props }) {
|
|
15
|
+
return (
|
|
16
|
+
<div className={`flex flex-col gap-1.5 ${className}`} {...props}>
|
|
17
|
+
{children}
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const Field = HeroUIField;
|
|
23
|
+
|
|
24
|
+
export const FieldLabel = ({ className = "", htmlFor, children, ...props }) => (
|
|
25
|
+
<label
|
|
26
|
+
htmlFor={htmlFor}
|
|
27
|
+
className={`text-sm font-medium text-slate-700 dark:text-slate-200 ${className}`}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</label>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const FieldDescription = ({ className = "", children, ...props }) => (
|
|
35
|
+
<p className={`text-xs text-slate-500 dark:text-slate-400 ${className}`} {...props}>
|
|
36
|
+
{children}
|
|
37
|
+
</p>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const FieldError = ({ className = "", errors, children, ...props }) => {
|
|
41
|
+
// Support both children and errors prop
|
|
42
|
+
const errorContent = errors?.length > 0 ? errors.join(", ") : children;
|
|
43
|
+
|
|
44
|
+
if (!errorContent) return null;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<p className={`text-xs text-red-600 dark:text-red-400 ${className}`} {...props}>
|
|
48
|
+
{errorContent}
|
|
49
|
+
</p>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Modal } from "@heroui/react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HeroUI v3 Modal — compound component.
|
|
6
|
+
*
|
|
7
|
+
* Sub-components via dot notation on the named `Modal` export:
|
|
8
|
+
* Modal.Backdrop, Modal.Container, Modal.Dialog,
|
|
9
|
+
* Modal.CloseTrigger, Modal.Header, Modal.Icon,
|
|
10
|
+
* Modal.Heading, Modal.Body, Modal.Footer
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { Modal } from "@/components/library";
|
|
14
|
+
* <Modal>
|
|
15
|
+
* <Button>Open</Button>
|
|
16
|
+
* <Modal.Backdrop>
|
|
17
|
+
* <Modal.Container>
|
|
18
|
+
* <Modal.Dialog>
|
|
19
|
+
* <Modal.CloseTrigger />
|
|
20
|
+
* <Modal.Header><Modal.Heading>Title</Modal.Heading></Modal.Header>
|
|
21
|
+
* <Modal.Body>…</Modal.Body>
|
|
22
|
+
* <Modal.Footer>…</Modal.Footer>
|
|
23
|
+
* </Modal.Dialog>
|
|
24
|
+
* </Modal.Container>
|
|
25
|
+
* </Modal.Backdrop>
|
|
26
|
+
* </Modal>
|
|
27
|
+
*/
|
|
28
|
+
export default function HeroUIModal(props) {
|
|
29
|
+
return <Modal {...props} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { Modal };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HeroUI v3 Popover — simple popover for shadcn compatibility.
|
|
5
|
+
* Using native HTML/CSS since HeroUI v3 doesn't have a Popover component.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { Popover, PopoverTrigger, PopoverContent } from "@/components/library";
|
|
9
|
+
* <Popover>
|
|
10
|
+
* <PopoverTrigger>Open</PopoverTrigger>
|
|
11
|
+
* <PopoverContent>Content</PopoverContent>
|
|
12
|
+
* </Popover>
|
|
13
|
+
*/
|
|
14
|
+
export default function HeroUIPopover({ children, open, onOpenChange, ...props }) {
|
|
15
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
16
|
+
const isControlled = open !== undefined;
|
|
17
|
+
const actualOpen = isControlled ? open : isOpen;
|
|
18
|
+
|
|
19
|
+
const handleOpenChange = (newOpen) => {
|
|
20
|
+
if (!isControlled) setIsOpen(newOpen);
|
|
21
|
+
onOpenChange?.(newOpen);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="relative inline-block" {...props}>
|
|
26
|
+
{React.Children.map(children, (child) => {
|
|
27
|
+
if (React.isValidElement(child)) {
|
|
28
|
+
return React.cloneElement(child, { open: actualOpen, onOpenChange: handleOpenChange });
|
|
29
|
+
}
|
|
30
|
+
return child;
|
|
31
|
+
})}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Popover = HeroUIPopover;
|
|
37
|
+
|
|
38
|
+
export const PopoverTrigger = ({ children, open, onOpenChange, ...props }) => {
|
|
39
|
+
const handleClick = () => onOpenChange?.(!open);
|
|
40
|
+
|
|
41
|
+
return React.cloneElement(children, {
|
|
42
|
+
...props,
|
|
43
|
+
onClick: handleClick,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const PopoverContent = ({ children, open, className = "", align = "center", sideOffset = 4, ...props }) => {
|
|
48
|
+
if (!open) return null;
|
|
49
|
+
|
|
50
|
+
const alignClasses = {
|
|
51
|
+
start: "left-0",
|
|
52
|
+
center: "left-1/2 -translate-x-1/2",
|
|
53
|
+
end: "right-0"
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
className={`absolute z-50 mt-${sideOffset} w-72 rounded-md border border-slate-200 bg-white p-4 text-slate-900 shadow-md dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 ${alignClasses[align] || alignClasses.center} ${className}`}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
};
|