@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.
Files changed (109) hide show
  1. package/package.json +4 -1
  2. package/scripts/postinstall.mjs +36 -17
  3. package/src/components/library/cards/ActionList.jsx +38 -0
  4. package/src/components/library/cards/ActivityCard.jsx +56 -0
  5. package/src/components/library/cards/BaseCard.jsx +109 -0
  6. package/src/components/library/cards/CalloutCard.jsx +37 -0
  7. package/src/components/library/cards/ChartCard.jsx +105 -0
  8. package/src/components/library/cards/FeedPanel.jsx +39 -0
  9. package/src/components/library/cards/ListCard.jsx +193 -0
  10. package/src/components/library/cards/MetricCard.jsx +109 -0
  11. package/src/components/library/cards/MetricsStrip.jsx +78 -0
  12. package/src/components/library/cards/SectionCard.jsx +83 -0
  13. package/src/components/library/cards/SemanticMetricCard.jsx +52 -0
  14. package/src/components/library/cards/SemanticMetricCardWithLoading.jsx +23 -0
  15. package/src/components/library/cards/SemanticTableCard.jsx +48 -0
  16. package/src/components/library/cards/SemanticTableCardWithLoading.jsx +22 -0
  17. package/src/components/library/cards/StatusCard.jsx +220 -0
  18. package/src/components/library/cards/TableCard.jsx +337 -0
  19. package/src/components/library/cards/WidgetCard.jsx +90 -0
  20. package/src/components/library/charts/D3Chart.jsx +109 -0
  21. package/src/components/library/charts/D3ChartTemplates.jsx +126 -0
  22. package/src/components/library/charts/GeoMap.jsx +293 -0
  23. package/src/components/library/chat/ChatBar.jsx +256 -0
  24. package/src/components/library/chat/ChatInput.jsx +89 -0
  25. package/src/components/library/chat/ChatMessage.jsx +178 -0
  26. package/src/components/library/chat/ChatMessageList.jsx +73 -0
  27. package/src/components/library/chat/ChatPanel.jsx +97 -0
  28. package/src/components/library/chat/ChatSuggestions.jsx +28 -0
  29. package/src/components/library/chat/ChatToolCall.jsx +100 -0
  30. package/src/components/library/chat/ChatTypingIndicator.jsx +23 -0
  31. package/src/components/library/chat/ChatWelcome.jsx +43 -0
  32. package/src/components/library/chat/index.jsx +10 -0
  33. package/src/components/library/chat/useChatState.jsx +130 -0
  34. package/src/components/library/data/DataModeProvider.jsx +67 -0
  35. package/src/components/library/data/DataModeToggle.jsx +36 -0
  36. package/src/components/library/data/chartDataProvider.jsx +61 -0
  37. package/src/components/library/data/filterUtils.jsx +141 -0
  38. package/src/components/library/data/useDataSource.jsx +33 -0
  39. package/src/components/library/data/usePageFilters.jsx +99 -0
  40. package/src/components/library/filters/FilterBar.jsx +95 -0
  41. package/src/components/library/filters/SearchFilter.jsx +36 -0
  42. package/src/components/library/filters/SelectFilter.jsx +55 -0
  43. package/src/components/library/filters/ToggleFilter.jsx +52 -0
  44. package/src/components/library/filters/index.jsx +4 -0
  45. package/src/components/library/forms/FormField.jsx +291 -0
  46. package/src/components/library/forms/FormModal.jsx +201 -0
  47. package/src/components/library/forms/FormRenderer.jsx +46 -0
  48. package/src/components/library/forms/FormSection.jsx +69 -0
  49. package/src/components/library/forms/index.jsx +5 -0
  50. package/src/components/library/forms/useFormState.jsx +165 -0
  51. package/src/components/library/heroui/Accordion.jsx +26 -0
  52. package/src/components/library/heroui/Alert.jsx +8 -0
  53. package/src/components/library/heroui/Badge.jsx +8 -0
  54. package/src/components/library/heroui/Breadcrumbs.jsx +22 -0
  55. package/src/components/library/heroui/Button.jsx +58 -0
  56. package/src/components/library/heroui/Card.jsx +8 -0
  57. package/src/components/library/heroui/Collapsible.jsx +42 -0
  58. package/src/components/library/heroui/DatePicker.jsx +34 -0
  59. package/src/components/library/heroui/Dialog.jsx +37 -0
  60. package/src/components/library/heroui/Drawer.jsx +32 -0
  61. package/src/components/library/heroui/Dropdown.jsx +28 -0
  62. package/src/components/library/heroui/Field.jsx +51 -0
  63. package/src/components/library/heroui/Input.jsx +6 -0
  64. package/src/components/library/heroui/Kbd.jsx +8 -0
  65. package/src/components/library/heroui/Meter.jsx +8 -0
  66. package/src/components/library/heroui/Modal.jsx +32 -0
  67. package/src/components/library/heroui/Pagination.jsx +8 -0
  68. package/src/components/library/heroui/Popover.jsx +64 -0
  69. package/src/components/library/heroui/ProgressBar.jsx +8 -0
  70. package/src/components/library/heroui/ProgressCircle.jsx +8 -0
  71. package/src/components/library/heroui/ScrollShadow.jsx +8 -0
  72. package/src/components/library/heroui/Select.jsx +37 -0
  73. package/src/components/library/heroui/Separator.jsx +8 -0
  74. package/src/components/library/heroui/Skeleton.jsx +8 -0
  75. package/src/components/library/heroui/Tabs.jsx +26 -0
  76. package/src/components/library/heroui/Toast.jsx +25 -0
  77. package/src/components/library/heroui/Toggle.jsx +14 -0
  78. package/src/components/library/heroui/Tooltip.jsx +21 -0
  79. package/src/components/library/index.jsx +146 -0
  80. package/src/components/library/layout/PageContainer.jsx +11 -0
  81. package/src/components/library/skeletons/CardSkeleton.jsx +30 -0
  82. package/src/components/library/theme/AppThemeProvider.jsx +67 -0
  83. package/src/components/library/theme/tokens.jsx +72 -0
  84. package/src/components/library/ui/Alert.jsx +80 -0
  85. package/src/components/library/ui/Avatar.jsx +44 -0
  86. package/src/components/library/ui/BreadcrumbExtras.tsx +120 -0
  87. package/src/components/library/ui/Button.jsx +61 -0
  88. package/src/components/library/ui/Card.jsx +117 -0
  89. package/src/components/library/ui/Checkbox.jsx +17 -0
  90. package/src/components/library/ui/Chip.jsx +38 -0
  91. package/src/components/library/ui/Collapsible.tsx +31 -0
  92. package/src/components/library/ui/Container.jsx +56 -0
  93. package/src/components/library/ui/DatePicker.tsx +34 -0
  94. package/src/components/library/ui/Dialog.tsx +141 -0
  95. package/src/components/library/ui/EmptyState.jsx +46 -0
  96. package/src/components/library/ui/Field.tsx +82 -0
  97. package/src/components/library/ui/FieldGroup.jsx +17 -0
  98. package/src/components/library/ui/Input.jsx +21 -0
  99. package/src/components/library/ui/Label.jsx +22 -0
  100. package/src/components/library/ui/PaginationExtras.tsx +142 -0
  101. package/src/components/library/ui/Popover.tsx +39 -0
  102. package/src/components/library/ui/Select.tsx +113 -0
  103. package/src/components/library/ui/Spinner.d.ts +10 -0
  104. package/src/components/library/ui/Spinner.jsx +64 -0
  105. package/src/components/library/ui/Text.jsx +46 -0
  106. package/src/components/library/ui/Toggle.jsx +42 -0
  107. package/src/components/workspace/ComponentRegistry.jsx +297 -0
  108. package/src/lib/index.ts +1 -0
  109. 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,8 @@
1
+ import React from "react";
2
+ import { Alert } from "@heroui/react";
3
+
4
+ export default function HeroUIAlert({ variant = "default", ...props }) {
5
+ return <Alert variant={variant} {...props} />;
6
+ }
7
+
8
+ export { Alert };
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { Badge } from "@heroui/react";
3
+
4
+ export default function HeroUIBadge({ children, ...props }) {
5
+ return <Badge {...props}>{children}</Badge>;
6
+ }
7
+
8
+ export { Badge };
@@ -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,8 @@
1
+ import React from "react";
2
+ import { Card } from "@heroui/react";
3
+
4
+ export default function HeroUICard({ children, ...props }) {
5
+ return <Card {...props}>{children}</Card>;
6
+ }
7
+
8
+ export { Card };
@@ -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,6 @@
1
+ import React from "react";
2
+ import { Input } from "@heroui/react";
3
+
4
+ export default function HeroUIInput(props) {
5
+ return <Input {...props} />;
6
+ }
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { Kbd } from "@heroui/react";
3
+
4
+ export default function HeroUIKbd({ children, ...props }) {
5
+ return <Kbd {...props}>{children}</Kbd>;
6
+ }
7
+
8
+ export { Kbd };
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { Meter } from "@heroui/react";
3
+
4
+ export default function HeroUIMeter({ children, ...props }) {
5
+ return <Meter {...props}>{children}</Meter>;
6
+ }
7
+
8
+ export { Meter };
@@ -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,8 @@
1
+ import React from "react";
2
+ import { Pagination } from "@heroui/react";
3
+
4
+ export default function HeroUIPagination(props) {
5
+ return <Pagination {...props} />;
6
+ }
7
+
8
+ export { Pagination };
@@ -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
+ };
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { ProgressBar } from "@heroui/react";
3
+
4
+ export default function HeroUIProgressBar({ children, ...props }) {
5
+ return <ProgressBar {...props}>{children}</ProgressBar>;
6
+ }
7
+
8
+ export { ProgressBar };