@schandlergarcia/sf-web-components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.a4drules/skills/building-data-visualization/SKILL.md +72 -0
- package/.a4drules/skills/building-data-visualization/implementation/bar-line-chart.md +316 -0
- package/.a4drules/skills/building-data-visualization/implementation/dashboard-layout.md +189 -0
- package/.a4drules/skills/building-data-visualization/implementation/donut-chart.md +181 -0
- package/.a4drules/skills/building-data-visualization/implementation/stat-card.md +150 -0
- package/.a4drules/skills/building-react-components/SKILL.md +96 -0
- package/.a4drules/skills/building-react-components/implementation/component.md +78 -0
- package/.a4drules/skills/building-react-components/implementation/header-footer.md +132 -0
- package/.a4drules/skills/building-react-components/implementation/page.md +93 -0
- package/.a4drules/skills/configuring-csp-trusted-sites/SKILL.md +90 -0
- package/.a4drules/skills/configuring-csp-trusted-sites/implementation/metadata-format.md +281 -0
- package/.a4drules/skills/configuring-webapp-metadata/SKILL.md +158 -0
- package/.a4drules/skills/creating-webapp/SKILL.md +140 -0
- package/.a4drules/skills/deploying-to-salesforce/SKILL.md +226 -0
- package/.a4drules/skills/implementing-file-upload/SKILL.md +396 -0
- package/.a4drules/skills/installing-webapp-features/SKILL.md +210 -0
- package/.a4drules/skills/managing-agentforce-conversation-client/SKILL.md +186 -0
- package/.a4drules/skills/managing-agentforce-conversation-client/references/constraints.md +134 -0
- package/.a4drules/skills/managing-agentforce-conversation-client/references/examples.md +132 -0
- package/.a4drules/skills/managing-agentforce-conversation-client/references/style-tokens.md +101 -0
- package/.a4drules/skills/managing-agentforce-conversation-client/references/troubleshooting.md +57 -0
- package/.a4drules/skills/using-salesforce-data/SKILL.md +363 -0
- package/.a4drules/skills/using-salesforce-data/graphql-search.sh +139 -0
- package/.a4drules/webapp-data.md +353 -0
- package/.a4drules/webapp-ui.md +16 -0
- package/README.md +124 -0
- package/dist/components/library/cards/ActionList.js +27 -0
- package/dist/components/library/cards/ActionList.js.map +1 -0
- package/dist/components/library/cards/ActivityCard.js +40 -0
- package/dist/components/library/cards/ActivityCard.js.map +1 -0
- package/dist/components/library/cards/BaseCard.js +89 -0
- package/dist/components/library/cards/BaseCard.js.map +1 -0
- package/dist/components/library/cards/CalloutCard.js +28 -0
- package/dist/components/library/cards/CalloutCard.js.map +1 -0
- package/dist/components/library/cards/ChartCard.js +79 -0
- package/dist/components/library/cards/ChartCard.js.map +1 -0
- package/dist/components/library/cards/FeedPanel.js +38 -0
- package/dist/components/library/cards/FeedPanel.js.map +1 -0
- package/dist/components/library/cards/ListCard.js +112 -0
- package/dist/components/library/cards/ListCard.js.map +1 -0
- package/dist/components/library/cards/MetricCard.js +86 -0
- package/dist/components/library/cards/MetricCard.js.map +1 -0
- package/dist/components/library/cards/MetricsStrip.js +60 -0
- package/dist/components/library/cards/MetricsStrip.js.map +1 -0
- package/dist/components/library/cards/SectionCard.js +59 -0
- package/dist/components/library/cards/SectionCard.js.map +1 -0
- package/dist/components/library/cards/StatusCard.js +137 -0
- package/dist/components/library/cards/StatusCard.js.map +1 -0
- package/dist/components/library/cards/TableCard.js +244 -0
- package/dist/components/library/cards/TableCard.js.map +1 -0
- package/dist/components/library/cards/WidgetCard.js +60 -0
- package/dist/components/library/cards/WidgetCard.js.map +1 -0
- package/dist/components/library/charts/D3Chart.js +74 -0
- package/dist/components/library/charts/D3Chart.js.map +1 -0
- package/dist/components/library/charts/D3ChartTemplates.js +44 -0
- package/dist/components/library/charts/D3ChartTemplates.js.map +1 -0
- package/dist/components/library/charts/GeoMap.js +229 -0
- package/dist/components/library/charts/GeoMap.js.map +1 -0
- package/dist/components/library/chat/ChatBar.js +194 -0
- package/dist/components/library/chat/ChatBar.js.map +1 -0
- package/dist/components/library/chat/ChatInput.js +67 -0
- package/dist/components/library/chat/ChatInput.js.map +1 -0
- package/dist/components/library/chat/ChatMessage.js +112 -0
- package/dist/components/library/chat/ChatMessage.js.map +1 -0
- package/dist/components/library/chat/ChatMessageList.js +50 -0
- package/dist/components/library/chat/ChatMessageList.js.map +1 -0
- package/dist/components/library/chat/ChatPanel.js +77 -0
- package/dist/components/library/chat/ChatPanel.js.map +1 -0
- package/dist/components/library/chat/ChatSuggestions.js +22 -0
- package/dist/components/library/chat/ChatSuggestions.js.map +1 -0
- package/dist/components/library/chat/ChatToolCall.js +87 -0
- package/dist/components/library/chat/ChatToolCall.js.map +1 -0
- package/dist/components/library/chat/ChatTypingIndicator.js +20 -0
- package/dist/components/library/chat/ChatTypingIndicator.js.map +1 -0
- package/dist/components/library/chat/ChatWelcome.js +24 -0
- package/dist/components/library/chat/ChatWelcome.js.map +1 -0
- package/dist/components/library/chat/useChatState.js +77 -0
- package/dist/components/library/chat/useChatState.js.map +1 -0
- package/dist/components/library/data/DataModeProvider.js +47 -0
- package/dist/components/library/data/DataModeProvider.js.map +1 -0
- package/dist/components/library/data/DataModeToggle.js +28 -0
- package/dist/components/library/data/DataModeToggle.js.map +1 -0
- package/dist/components/library/data/filterUtils.js +71 -0
- package/dist/components/library/data/filterUtils.js.map +1 -0
- package/dist/components/library/data/useDataSource.js +13 -0
- package/dist/components/library/data/useDataSource.js.map +1 -0
- package/dist/components/library/data/usePageFilters.js +46 -0
- package/dist/components/library/data/usePageFilters.js.map +1 -0
- package/dist/components/library/filters/FilterBar.js +89 -0
- package/dist/components/library/filters/FilterBar.js.map +1 -0
- package/dist/components/library/filters/SearchFilter.js +44 -0
- package/dist/components/library/filters/SearchFilter.js.map +1 -0
- package/dist/components/library/filters/SelectFilter.js +44 -0
- package/dist/components/library/filters/SelectFilter.js.map +1 -0
- package/dist/components/library/filters/ToggleFilter.js +48 -0
- package/dist/components/library/filters/ToggleFilter.js.map +1 -0
- package/dist/components/library/forms/FormField.js +256 -0
- package/dist/components/library/forms/FormField.js.map +1 -0
- package/dist/components/library/forms/FormModal.js +161 -0
- package/dist/components/library/forms/FormModal.js.map +1 -0
- package/dist/components/library/forms/FormRenderer.js +32 -0
- package/dist/components/library/forms/FormRenderer.js.map +1 -0
- package/dist/components/library/forms/FormSection.js +49 -0
- package/dist/components/library/forms/FormSection.js.map +1 -0
- package/dist/components/library/forms/useFormState.js +85 -0
- package/dist/components/library/forms/useFormState.js.map +1 -0
- package/dist/components/library/heroui/Accordion.js +12 -0
- package/dist/components/library/heroui/Accordion.js.map +1 -0
- package/dist/components/library/heroui/Alert.js +12 -0
- package/dist/components/library/heroui/Alert.js.map +1 -0
- package/dist/components/library/heroui/Badge.js +12 -0
- package/dist/components/library/heroui/Badge.js.map +1 -0
- package/dist/components/library/heroui/Breadcrumbs.js +12 -0
- package/dist/components/library/heroui/Breadcrumbs.js.map +1 -0
- package/dist/components/library/heroui/Button.js +18 -0
- package/dist/components/library/heroui/Button.js.map +1 -0
- package/dist/components/library/heroui/Card.js +12 -0
- package/dist/components/library/heroui/Card.js.map +1 -0
- package/dist/components/library/heroui/Drawer.js +12 -0
- package/dist/components/library/heroui/Drawer.js.map +1 -0
- package/dist/components/library/heroui/Dropdown.js +12 -0
- package/dist/components/library/heroui/Dropdown.js.map +1 -0
- package/dist/components/library/heroui/Input.js +10 -0
- package/dist/components/library/heroui/Input.js.map +1 -0
- package/dist/components/library/heroui/Kbd.js +12 -0
- package/dist/components/library/heroui/Kbd.js.map +1 -0
- package/dist/components/library/heroui/Meter.js +12 -0
- package/dist/components/library/heroui/Meter.js.map +1 -0
- package/dist/components/library/heroui/Modal.js +12 -0
- package/dist/components/library/heroui/Modal.js.map +1 -0
- package/dist/components/library/heroui/Pagination.js +12 -0
- package/dist/components/library/heroui/Pagination.js.map +1 -0
- package/dist/components/library/heroui/ProgressBar.js +12 -0
- package/dist/components/library/heroui/ProgressBar.js.map +1 -0
- package/dist/components/library/heroui/ProgressCircle.js +12 -0
- package/dist/components/library/heroui/ProgressCircle.js.map +1 -0
- package/dist/components/library/heroui/ScrollShadow.js +12 -0
- package/dist/components/library/heroui/ScrollShadow.js.map +1 -0
- package/dist/components/library/heroui/Select.js +12 -0
- package/dist/components/library/heroui/Select.js.map +1 -0
- package/dist/components/library/heroui/Separator.js +12 -0
- package/dist/components/library/heroui/Separator.js.map +1 -0
- package/dist/components/library/heroui/Skeleton.js +12 -0
- package/dist/components/library/heroui/Skeleton.js.map +1 -0
- package/dist/components/library/heroui/Tabs.js +12 -0
- package/dist/components/library/heroui/Tabs.js.map +1 -0
- package/dist/components/library/heroui/Toast.js +13 -0
- package/dist/components/library/heroui/Toast.js.map +1 -0
- package/dist/components/library/heroui/Toggle.js +12 -0
- package/dist/components/library/heroui/Toggle.js.map +1 -0
- package/dist/components/library/heroui/Tooltip.js +12 -0
- package/dist/components/library/heroui/Tooltip.js.map +1 -0
- package/dist/components/library/layout/PageContainer.js +9 -0
- package/dist/components/library/layout/PageContainer.js.map +1 -0
- package/dist/components/library/skeletons/CardSkeleton.js +29 -0
- package/dist/components/library/skeletons/CardSkeleton.js.map +1 -0
- package/dist/components/library/theme/AppThemeProvider.js +55 -0
- package/dist/components/library/theme/AppThemeProvider.js.map +1 -0
- package/dist/components/library/theme/tokens.js +55 -0
- package/dist/components/library/theme/tokens.js.map +1 -0
- package/dist/components/library/ui/Avatar.js +33 -0
- package/dist/components/library/ui/Avatar.js.map +1 -0
- package/dist/components/library/ui/Button.js +50 -0
- package/dist/components/library/ui/Button.js.map +1 -0
- package/dist/components/library/ui/Card.js +21 -0
- package/dist/components/library/ui/Card.js.map +1 -0
- package/dist/components/library/ui/Chip.js +31 -0
- package/dist/components/library/ui/Chip.js.map +1 -0
- package/dist/components/library/ui/Container.js +42 -0
- package/dist/components/library/ui/Container.js.map +1 -0
- package/dist/components/library/ui/EmptyState.js +27 -0
- package/dist/components/library/ui/EmptyState.js.map +1 -0
- package/dist/components/library/ui/Input.js +22 -0
- package/dist/components/library/ui/Input.js.map +1 -0
- package/dist/components/library/ui/Spinner.js +64 -0
- package/dist/components/library/ui/Spinner.js.map +1 -0
- package/dist/components/library/ui/Text.js +43 -0
- package/dist/components/library/ui/Text.js.map +1 -0
- package/dist/components/library/ui/Toggle.js +47 -0
- package/dist/components/library/ui/Toggle.js.map +1 -0
- package/dist/components/workspace/ComponentRegistry.js +231 -0
- package/dist/components/workspace/ComponentRegistry.js.map +1 -0
- package/dist/index.js +185 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/node_modules/clsx/dist/clsx.js +17 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +2925 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/package.json +81 -0
- package/scripts/get-graphql-schema.mjs +68 -0
- package/scripts/reset-command-center.sh +358 -0
- package/scripts/rewrite-e2e-assets.mjs +23 -0
- package/scripts/validate-dashboard.sh +290 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { jsxs as c, jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { ChevronDownIcon as m } from "@heroicons/react/24/outline";
|
|
4
|
+
const p = "h-10 w-full rounded-lg border border-slate-200 bg-white px-3 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500 dark:focus:ring-offset-slate-950", x = "border-red-300 focus:ring-red-500 dark:border-red-700 dark:focus:ring-red-500";
|
|
5
|
+
function i(...e) {
|
|
6
|
+
return e.filter(Boolean).join(" ");
|
|
7
|
+
}
|
|
8
|
+
function k({ label: e, required: n, htmlFor: s }) {
|
|
9
|
+
return e ? /* @__PURE__ */ c("label", { htmlFor: s, className: "block text-sm font-medium text-slate-700 dark:text-slate-200", children: [
|
|
10
|
+
e,
|
|
11
|
+
n ? /* @__PURE__ */ r("span", { className: "ml-0.5 text-red-500", children: "*" }) : null
|
|
12
|
+
] }) : null;
|
|
13
|
+
}
|
|
14
|
+
function f({ error: e }) {
|
|
15
|
+
return e ? /* @__PURE__ */ r("p", { className: "text-xs text-red-600 dark:text-red-400", children: e }) : null;
|
|
16
|
+
}
|
|
17
|
+
function g({ description: e }) {
|
|
18
|
+
return e ? /* @__PURE__ */ r("p", { className: "text-xs text-slate-500 dark:text-slate-400", children: e }) : null;
|
|
19
|
+
}
|
|
20
|
+
function u({ field: e, value: n, onChange: s, onBlur: a, error: d }) {
|
|
21
|
+
const t = e.inputType ?? e.type, o = { text: "text", email: "email", url: "url", number: "number", date: "date" }[t] ?? "text";
|
|
22
|
+
return /* @__PURE__ */ r(
|
|
23
|
+
"input",
|
|
24
|
+
{
|
|
25
|
+
id: e.id,
|
|
26
|
+
name: e.id,
|
|
27
|
+
type: o,
|
|
28
|
+
value: n ?? "",
|
|
29
|
+
onChange: (l) => s((e.type === "number", l.target.value)),
|
|
30
|
+
onBlur: a,
|
|
31
|
+
placeholder: e.placeholder,
|
|
32
|
+
disabled: e.disabled,
|
|
33
|
+
readOnly: e.readOnly,
|
|
34
|
+
min: e.min,
|
|
35
|
+
max: e.max,
|
|
36
|
+
step: e.step,
|
|
37
|
+
className: i(p, d && x)
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function v({ field: e, value: n, onChange: s, onBlur: a, error: d }) {
|
|
42
|
+
return /* @__PURE__ */ r(
|
|
43
|
+
"textarea",
|
|
44
|
+
{
|
|
45
|
+
id: e.id,
|
|
46
|
+
name: e.id,
|
|
47
|
+
value: n ?? "",
|
|
48
|
+
onChange: (t) => s(t.target.value),
|
|
49
|
+
onBlur: a,
|
|
50
|
+
placeholder: e.placeholder,
|
|
51
|
+
disabled: e.disabled,
|
|
52
|
+
readOnly: e.readOnly,
|
|
53
|
+
rows: e.rows ?? 3,
|
|
54
|
+
className: i(
|
|
55
|
+
"w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500 dark:focus:ring-offset-slate-950",
|
|
56
|
+
d && x
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
function y({ field: e, value: n, onChange: s, onBlur: a, error: d }) {
|
|
62
|
+
const t = (e.options ?? []).map(
|
|
63
|
+
(o) => typeof o == "string" ? { value: o, label: o } : o
|
|
64
|
+
);
|
|
65
|
+
return /* @__PURE__ */ c("div", { className: "relative", children: [
|
|
66
|
+
/* @__PURE__ */ c(
|
|
67
|
+
"select",
|
|
68
|
+
{
|
|
69
|
+
id: e.id,
|
|
70
|
+
name: e.id,
|
|
71
|
+
value: n ?? "",
|
|
72
|
+
onChange: (o) => s(o.target.value),
|
|
73
|
+
onBlur: a,
|
|
74
|
+
disabled: e.disabled,
|
|
75
|
+
className: i(
|
|
76
|
+
"h-10 w-full appearance-none rounded-lg border border-slate-200 bg-white py-0 pl-3 pr-9 text-sm font-medium text-slate-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-200 dark:focus:ring-offset-slate-950",
|
|
77
|
+
d && x
|
|
78
|
+
),
|
|
79
|
+
children: [
|
|
80
|
+
e.placeholder ? /* @__PURE__ */ r("option", { value: "", children: e.placeholder }) : null,
|
|
81
|
+
t.map((o) => /* @__PURE__ */ r("option", { value: o.value, children: o.label }, o.value))
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ r(
|
|
86
|
+
m,
|
|
87
|
+
{
|
|
88
|
+
className: "pointer-events-none absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500",
|
|
89
|
+
"aria-hidden": "true"
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
] });
|
|
93
|
+
}
|
|
94
|
+
function w({ field: e, value: n, onChange: s }) {
|
|
95
|
+
const a = (e.options ?? []).map(
|
|
96
|
+
(t) => typeof t == "string" ? { value: t, label: t } : t
|
|
97
|
+
), d = e.layout ?? (a.length <= 4 ? "horizontal" : "vertical");
|
|
98
|
+
return /* @__PURE__ */ r(
|
|
99
|
+
"div",
|
|
100
|
+
{
|
|
101
|
+
className: i(
|
|
102
|
+
"flex gap-3",
|
|
103
|
+
d === "vertical" ? "flex-col" : "flex-row flex-wrap"
|
|
104
|
+
),
|
|
105
|
+
role: "radiogroup",
|
|
106
|
+
"aria-labelledby": `${e.id}-label`,
|
|
107
|
+
children: a.map((t) => /* @__PURE__ */ c(
|
|
108
|
+
"label",
|
|
109
|
+
{
|
|
110
|
+
className: "inline-flex cursor-pointer items-center gap-2 text-sm text-slate-700 dark:text-slate-200",
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ r(
|
|
113
|
+
"input",
|
|
114
|
+
{
|
|
115
|
+
type: "radio",
|
|
116
|
+
name: e.id,
|
|
117
|
+
value: t.value,
|
|
118
|
+
checked: n === t.value,
|
|
119
|
+
onChange: () => s(t.value),
|
|
120
|
+
disabled: e.disabled || t.disabled,
|
|
121
|
+
className: "h-4 w-4 border-slate-300 text-brand-600 focus:ring-brand-500 dark:border-slate-600 dark:bg-slate-800"
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
t.label,
|
|
125
|
+
t.description ? /* @__PURE__ */ r("span", { className: "text-xs text-slate-400 dark:text-slate-500", children: t.description }) : null
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
t.value
|
|
129
|
+
))
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
function N({ field: e, value: n, onChange: s }) {
|
|
134
|
+
return /* @__PURE__ */ c("label", { className: "inline-flex cursor-pointer items-center gap-2.5 text-sm text-slate-700 dark:text-slate-200", children: [
|
|
135
|
+
/* @__PURE__ */ r(
|
|
136
|
+
"input",
|
|
137
|
+
{
|
|
138
|
+
type: "checkbox",
|
|
139
|
+
id: e.id,
|
|
140
|
+
name: e.id,
|
|
141
|
+
checked: !!n,
|
|
142
|
+
onChange: (a) => s(a.target.checked),
|
|
143
|
+
disabled: e.disabled,
|
|
144
|
+
className: "h-4 w-4 rounded border-slate-300 text-brand-600 focus:ring-brand-500 dark:border-slate-600 dark:bg-slate-800"
|
|
145
|
+
}
|
|
146
|
+
),
|
|
147
|
+
e.checkboxLabel ?? e.label
|
|
148
|
+
] });
|
|
149
|
+
}
|
|
150
|
+
function F({ field: e, value: n, onChange: s }) {
|
|
151
|
+
const a = Array.isArray(n) ? n : [], d = (e.options ?? []).map(
|
|
152
|
+
(l) => typeof l == "string" ? { value: l, label: l } : l
|
|
153
|
+
), t = e.layout ?? (d.length <= 4 ? "horizontal" : "vertical");
|
|
154
|
+
function o(l) {
|
|
155
|
+
const b = a.includes(l) ? a.filter((h) => h !== l) : [...a, l];
|
|
156
|
+
s(b);
|
|
157
|
+
}
|
|
158
|
+
return /* @__PURE__ */ r(
|
|
159
|
+
"div",
|
|
160
|
+
{
|
|
161
|
+
className: i(
|
|
162
|
+
"flex gap-3",
|
|
163
|
+
t === "vertical" ? "flex-col" : "flex-row flex-wrap"
|
|
164
|
+
),
|
|
165
|
+
children: d.map((l) => /* @__PURE__ */ c(
|
|
166
|
+
"label",
|
|
167
|
+
{
|
|
168
|
+
className: "inline-flex cursor-pointer items-center gap-2 text-sm text-slate-700 dark:text-slate-200",
|
|
169
|
+
children: [
|
|
170
|
+
/* @__PURE__ */ r(
|
|
171
|
+
"input",
|
|
172
|
+
{
|
|
173
|
+
type: "checkbox",
|
|
174
|
+
checked: a.includes(l.value),
|
|
175
|
+
onChange: () => o(l.value),
|
|
176
|
+
disabled: e.disabled || l.disabled,
|
|
177
|
+
className: "h-4 w-4 rounded border-slate-300 text-brand-600 focus:ring-brand-500 dark:border-slate-600 dark:bg-slate-800"
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
l.label
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
l.value
|
|
184
|
+
))
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function C({ field: e, value: n, onChange: s }) {
|
|
189
|
+
const a = !!n;
|
|
190
|
+
return /* @__PURE__ */ c("div", { className: "flex items-center gap-3", children: [
|
|
191
|
+
/* @__PURE__ */ r(
|
|
192
|
+
"button",
|
|
193
|
+
{
|
|
194
|
+
type: "button",
|
|
195
|
+
role: "switch",
|
|
196
|
+
"aria-checked": a,
|
|
197
|
+
onClick: () => s(!a),
|
|
198
|
+
disabled: e.disabled,
|
|
199
|
+
className: i(
|
|
200
|
+
"relative inline-flex h-6 w-11 shrink-0 rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-slate-950",
|
|
201
|
+
a ? "bg-brand-500" : "bg-slate-200 dark:bg-slate-700",
|
|
202
|
+
e.disabled && "cursor-not-allowed opacity-60"
|
|
203
|
+
),
|
|
204
|
+
children: /* @__PURE__ */ r(
|
|
205
|
+
"span",
|
|
206
|
+
{
|
|
207
|
+
"aria-hidden": "true",
|
|
208
|
+
className: i(
|
|
209
|
+
"pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow ring-0 transition-transform",
|
|
210
|
+
a ? "translate-x-5" : "translate-x-0"
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
e.toggleLabel ? /* @__PURE__ */ r("span", { className: "text-sm text-slate-700 dark:text-slate-200", children: e.toggleLabel }) : null
|
|
217
|
+
] });
|
|
218
|
+
}
|
|
219
|
+
const E = {
|
|
220
|
+
text: u,
|
|
221
|
+
email: u,
|
|
222
|
+
url: u,
|
|
223
|
+
number: u,
|
|
224
|
+
date: u,
|
|
225
|
+
textarea: v,
|
|
226
|
+
select: y,
|
|
227
|
+
radio: w,
|
|
228
|
+
checkbox: N,
|
|
229
|
+
checkboxGroup: F,
|
|
230
|
+
toggle: C
|
|
231
|
+
};
|
|
232
|
+
function O({ field: e, value: n, error: s, touched: a, onChange: d, onBlur: t }) {
|
|
233
|
+
const o = E[e.type];
|
|
234
|
+
if (!o) return null;
|
|
235
|
+
const l = s && a, b = e.type === "checkbox";
|
|
236
|
+
return /* @__PURE__ */ c("div", { className: "flex flex-col gap-1.5", children: [
|
|
237
|
+
b ? null : /* @__PURE__ */ r(k, { label: e.label, required: e.required, htmlFor: e.id }),
|
|
238
|
+
e.description && e.type !== "toggle" ? /* @__PURE__ */ r(g, { description: e.description }) : null,
|
|
239
|
+
/* @__PURE__ */ r(
|
|
240
|
+
o,
|
|
241
|
+
{
|
|
242
|
+
field: e,
|
|
243
|
+
value: n,
|
|
244
|
+
onChange: d,
|
|
245
|
+
onBlur: t,
|
|
246
|
+
error: l
|
|
247
|
+
}
|
|
248
|
+
),
|
|
249
|
+
e.type === "toggle" && e.description ? /* @__PURE__ */ r(g, { description: e.description }) : null,
|
|
250
|
+
l ? /* @__PURE__ */ r(f, { error: s }) : null
|
|
251
|
+
] });
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
O as default
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=FormField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormField.js","sources":["../../../../src/components/library/forms/FormField.jsx"],"sourcesContent":["import React from \"react\";\nimport { ChevronDownIcon } from \"@heroicons/react/24/outline\";\n\nconst INPUT_BASE =\n \"h-10 w-full rounded-lg border border-slate-200 bg-white px-3 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500 dark:focus:ring-offset-slate-950\";\n\nconst INPUT_ERROR =\n \"border-red-300 focus:ring-red-500 dark:border-red-700 dark:focus:ring-red-500\";\n\nfunction cx(...classes) {\n return classes.filter(Boolean).join(\" \");\n}\n\nfunction FieldLabel({ label, required, htmlFor }) {\n if (!label) return null;\n return (\n <label htmlFor={htmlFor} className=\"block text-sm font-medium text-slate-700 dark:text-slate-200\">\n {label}\n {required ? <span className=\"ml-0.5 text-red-500\">*</span> : null}\n </label>\n );\n}\n\nfunction FieldError({ error }) {\n if (!error) return null;\n return <p className=\"text-xs text-red-600 dark:text-red-400\">{error}</p>;\n}\n\nfunction FieldDescription({ description }) {\n if (!description) return null;\n return <p className=\"text-xs text-slate-500 dark:text-slate-400\">{description}</p>;\n}\n\n// ─── Individual field renderers ───\n\nfunction TextField({ field, value, onChange, onBlur, error }) {\n const inputType = field.inputType ?? field.type;\n const type = { text: \"text\", email: \"email\", url: \"url\", number: \"number\", date: \"date\" }[inputType] ?? \"text\";\n\n return (\n <input\n id={field.id}\n name={field.id}\n type={type}\n value={value ?? \"\"}\n onChange={(e) => onChange(field.type === \"number\" ? e.target.value : e.target.value)}\n onBlur={onBlur}\n placeholder={field.placeholder}\n disabled={field.disabled}\n readOnly={field.readOnly}\n min={field.min}\n max={field.max}\n step={field.step}\n className={cx(INPUT_BASE, error && INPUT_ERROR)}\n />\n );\n}\n\nfunction TextareaField({ field, value, onChange, onBlur, error }) {\n return (\n <textarea\n id={field.id}\n name={field.id}\n value={value ?? \"\"}\n onChange={(e) => onChange(e.target.value)}\n onBlur={onBlur}\n placeholder={field.placeholder}\n disabled={field.disabled}\n readOnly={field.readOnly}\n rows={field.rows ?? 3}\n className={cx(\n \"w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500 dark:focus:ring-offset-slate-950\",\n error && INPUT_ERROR\n )}\n />\n );\n}\n\nfunction SelectField({ field, value, onChange, onBlur, error }) {\n const options = (field.options ?? []).map((opt) =>\n typeof opt === \"string\" ? { value: opt, label: opt } : opt\n );\n\n return (\n <div className=\"relative\">\n <select\n id={field.id}\n name={field.id}\n value={value ?? \"\"}\n onChange={(e) => onChange(e.target.value)}\n onBlur={onBlur}\n disabled={field.disabled}\n className={cx(\n \"h-10 w-full appearance-none rounded-lg border border-slate-200 bg-white py-0 pl-3 pr-9 text-sm font-medium text-slate-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-200 dark:focus:ring-offset-slate-950\",\n error && INPUT_ERROR\n )}\n >\n {field.placeholder ? (\n <option value=\"\">{field.placeholder}</option>\n ) : null}\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <ChevronDownIcon\n className=\"pointer-events-none absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500\"\n aria-hidden=\"true\"\n />\n </div>\n );\n}\n\nfunction RadioField({ field, value, onChange }) {\n const options = (field.options ?? []).map((opt) =>\n typeof opt === \"string\" ? { value: opt, label: opt } : opt\n );\n const layout = field.layout ?? (options.length <= 4 ? \"horizontal\" : \"vertical\");\n\n return (\n <div\n className={cx(\n \"flex gap-3\",\n layout === \"vertical\" ? \"flex-col\" : \"flex-row flex-wrap\"\n )}\n role=\"radiogroup\"\n aria-labelledby={`${field.id}-label`}\n >\n {options.map((opt) => (\n <label\n key={opt.value}\n className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-slate-700 dark:text-slate-200\"\n >\n <input\n type=\"radio\"\n name={field.id}\n value={opt.value}\n checked={value === opt.value}\n onChange={() => onChange(opt.value)}\n disabled={field.disabled || opt.disabled}\n className=\"h-4 w-4 border-slate-300 text-brand-600 focus:ring-brand-500 dark:border-slate-600 dark:bg-slate-800\"\n />\n {opt.label}\n {opt.description ? (\n <span className=\"text-xs text-slate-400 dark:text-slate-500\">{opt.description}</span>\n ) : null}\n </label>\n ))}\n </div>\n );\n}\n\nfunction CheckboxField({ field, value, onChange }) {\n return (\n <label className=\"inline-flex cursor-pointer items-center gap-2.5 text-sm text-slate-700 dark:text-slate-200\">\n <input\n type=\"checkbox\"\n id={field.id}\n name={field.id}\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n disabled={field.disabled}\n className=\"h-4 w-4 rounded border-slate-300 text-brand-600 focus:ring-brand-500 dark:border-slate-600 dark:bg-slate-800\"\n />\n {field.checkboxLabel ?? field.label}\n </label>\n );\n}\n\nfunction CheckboxGroupField({ field, value, onChange }) {\n const selected = Array.isArray(value) ? value : [];\n const options = (field.options ?? []).map((opt) =>\n typeof opt === \"string\" ? { value: opt, label: opt } : opt\n );\n const layout = field.layout ?? (options.length <= 4 ? \"horizontal\" : \"vertical\");\n\n function toggleValue(optValue) {\n const next = selected.includes(optValue)\n ? selected.filter((v) => v !== optValue)\n : [...selected, optValue];\n onChange(next);\n }\n\n return (\n <div\n className={cx(\n \"flex gap-3\",\n layout === \"vertical\" ? \"flex-col\" : \"flex-row flex-wrap\"\n )}\n >\n {options.map((opt) => (\n <label\n key={opt.value}\n className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-slate-700 dark:text-slate-200\"\n >\n <input\n type=\"checkbox\"\n checked={selected.includes(opt.value)}\n onChange={() => toggleValue(opt.value)}\n disabled={field.disabled || opt.disabled}\n className=\"h-4 w-4 rounded border-slate-300 text-brand-600 focus:ring-brand-500 dark:border-slate-600 dark:bg-slate-800\"\n />\n {opt.label}\n </label>\n ))}\n </div>\n );\n}\n\nfunction ToggleField({ field, value, onChange }) {\n const checked = Boolean(value);\n\n return (\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n onClick={() => onChange(!checked)}\n disabled={field.disabled}\n className={cx(\n \"relative inline-flex h-6 w-11 shrink-0 rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-slate-950\",\n checked ? \"bg-brand-500\" : \"bg-slate-200 dark:bg-slate-700\",\n field.disabled && \"cursor-not-allowed opacity-60\"\n )}\n >\n <span\n aria-hidden=\"true\"\n className={cx(\n \"pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow ring-0 transition-transform\",\n checked ? \"translate-x-5\" : \"translate-x-0\"\n )}\n />\n </button>\n {field.toggleLabel ? (\n <span className=\"text-sm text-slate-700 dark:text-slate-200\">{field.toggleLabel}</span>\n ) : null}\n </div>\n );\n}\n\n// ─── Main FormField ───\n\nconst FIELD_RENDERERS = {\n text: TextField,\n email: TextField,\n url: TextField,\n number: TextField,\n date: TextField,\n textarea: TextareaField,\n select: SelectField,\n radio: RadioField,\n checkbox: CheckboxField,\n checkboxGroup: CheckboxGroupField,\n toggle: ToggleField,\n};\n\n/**\n * Renders a single form field with label, description, error message,\n * and the appropriate input type.\n */\nexport default function FormField({ field, value, error, touched, onChange, onBlur }) {\n const Renderer = FIELD_RENDERERS[field.type];\n if (!Renderer) return null;\n\n const showError = error && touched;\n const noLabel = field.type === \"checkbox\";\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {!noLabel ? (\n <FieldLabel label={field.label} required={field.required} htmlFor={field.id} />\n ) : null}\n {field.description && field.type !== \"toggle\" ? (\n <FieldDescription description={field.description} />\n ) : null}\n <Renderer\n field={field}\n value={value}\n onChange={onChange}\n onBlur={onBlur}\n error={showError}\n />\n {field.type === \"toggle\" && field.description ? (\n <FieldDescription description={field.description} />\n ) : null}\n {showError ? <FieldError error={error} /> : null}\n </div>\n );\n}\n"],"names":["INPUT_BASE","INPUT_ERROR","cx","classes","FieldLabel","label","required","htmlFor","jsxs","jsx","FieldError","error","FieldDescription","description","TextField","field","value","onChange","onBlur","inputType","type","e","TextareaField","SelectField","options","opt","ChevronDownIcon","RadioField","layout","CheckboxField","CheckboxGroupField","selected","toggleValue","optValue","next","v","ToggleField","checked","FIELD_RENDERERS","FormField","touched","Renderer","showError","noLabel"],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { jsx as e, jsxs as r, Fragment as y } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect as m, useCallback as k } from "react";
|
|
3
|
+
import { createPortal as w } from "react-dom";
|
|
4
|
+
import { AnimatePresence as N, motion as c } from "framer-motion";
|
|
5
|
+
import { XMarkIcon as S } from "@heroicons/react/24/outline";
|
|
6
|
+
import E from "../ui/Spinner.js";
|
|
7
|
+
import A from "./FormRenderer.js";
|
|
8
|
+
import j from "./useFormState.js";
|
|
9
|
+
const F = {
|
|
10
|
+
hidden: { opacity: 0 },
|
|
11
|
+
visible: { opacity: 1 }
|
|
12
|
+
}, L = {
|
|
13
|
+
hidden: { opacity: 0, y: 24, scale: 0.97 },
|
|
14
|
+
visible: { opacity: 1, y: 0, scale: 1, transition: { type: "spring", damping: 25, stiffness: 350 } },
|
|
15
|
+
exit: { opacity: 0, y: 16, scale: 0.97, transition: { duration: 0.15 } }
|
|
16
|
+
}, b = {
|
|
17
|
+
sm: "max-w-md",
|
|
18
|
+
md: "max-w-xl",
|
|
19
|
+
lg: "max-w-2xl",
|
|
20
|
+
xl: "max-w-4xl"
|
|
21
|
+
};
|
|
22
|
+
function D({
|
|
23
|
+
isOpen: i = !1,
|
|
24
|
+
onClose: a,
|
|
25
|
+
title: d,
|
|
26
|
+
subtitle: o,
|
|
27
|
+
sections: n = [],
|
|
28
|
+
initialValues: u = {},
|
|
29
|
+
onSubmit: f,
|
|
30
|
+
submitLabel: x = "Save",
|
|
31
|
+
cancelLabel: h = "Cancel",
|
|
32
|
+
size: v = "lg",
|
|
33
|
+
destructive: g = !1,
|
|
34
|
+
minSubmitMs: p
|
|
35
|
+
}) {
|
|
36
|
+
const t = j({
|
|
37
|
+
initialValues: u,
|
|
38
|
+
sections: n,
|
|
39
|
+
minSubmitMs: p,
|
|
40
|
+
onSubmit: async (l) => {
|
|
41
|
+
await f?.(l), a?.();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
m(() => {
|
|
45
|
+
i && t.reset();
|
|
46
|
+
}, [i]);
|
|
47
|
+
const s = k(
|
|
48
|
+
(l) => {
|
|
49
|
+
l.key === "Escape" && a?.();
|
|
50
|
+
},
|
|
51
|
+
[a]
|
|
52
|
+
);
|
|
53
|
+
return m(() => {
|
|
54
|
+
if (i)
|
|
55
|
+
return document.addEventListener("keydown", s), document.body.style.overflow = "hidden", () => {
|
|
56
|
+
document.removeEventListener("keydown", s), document.body.style.overflow = "";
|
|
57
|
+
};
|
|
58
|
+
}, [i, s]), typeof document > "u" ? null : w(
|
|
59
|
+
/* @__PURE__ */ e(N, { children: i ? /* @__PURE__ */ r("div", { className: "fixed inset-0 z-50 flex items-start justify-center px-4 pt-[10vh] sm:pt-[12vh]", children: [
|
|
60
|
+
/* @__PURE__ */ e(
|
|
61
|
+
c.div,
|
|
62
|
+
{
|
|
63
|
+
className: "fixed inset-0 bg-black/40 backdrop-blur-sm",
|
|
64
|
+
variants: F,
|
|
65
|
+
initial: "hidden",
|
|
66
|
+
animate: "visible",
|
|
67
|
+
exit: "hidden",
|
|
68
|
+
transition: { duration: 0.2 },
|
|
69
|
+
onClick: a,
|
|
70
|
+
"aria-hidden": "true"
|
|
71
|
+
},
|
|
72
|
+
"overlay"
|
|
73
|
+
),
|
|
74
|
+
/* @__PURE__ */ r(
|
|
75
|
+
c.div,
|
|
76
|
+
{
|
|
77
|
+
role: "dialog",
|
|
78
|
+
"aria-modal": "true",
|
|
79
|
+
"aria-label": d,
|
|
80
|
+
variants: L,
|
|
81
|
+
initial: "hidden",
|
|
82
|
+
animate: "visible",
|
|
83
|
+
exit: "exit",
|
|
84
|
+
className: [
|
|
85
|
+
"relative z-10 flex w-full flex-col rounded-xl border border-slate-200 bg-white shadow-xl dark:border-slate-800 dark:bg-slate-900",
|
|
86
|
+
b[v] ?? b.lg,
|
|
87
|
+
"max-h-[80vh]"
|
|
88
|
+
].join(" "),
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ r("div", { className: "flex items-start justify-between border-b border-slate-100 px-6 py-4 dark:border-slate-800", children: [
|
|
91
|
+
/* @__PURE__ */ r("div", { children: [
|
|
92
|
+
/* @__PURE__ */ e("h2", { className: "text-base font-semibold text-slate-900 dark:text-slate-50", children: d }),
|
|
93
|
+
o ? /* @__PURE__ */ e("p", { className: "mt-0.5 text-sm text-slate-500 dark:text-slate-400", children: o }) : null
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ e(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
type: "button",
|
|
99
|
+
onClick: a,
|
|
100
|
+
className: "rounded-lg p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-slate-300",
|
|
101
|
+
"aria-label": "Close",
|
|
102
|
+
children: /* @__PURE__ */ e(S, { className: "h-5 w-5" })
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ e("div", { className: "flex-1 overflow-y-auto px-6 py-5", children: /* @__PURE__ */ e(
|
|
107
|
+
A,
|
|
108
|
+
{
|
|
109
|
+
sections: n,
|
|
110
|
+
values: t.values,
|
|
111
|
+
errors: t.errors,
|
|
112
|
+
touched: t.touched,
|
|
113
|
+
onFieldChange: t.setValue,
|
|
114
|
+
onFieldBlur: t.setTouched,
|
|
115
|
+
formError: t.errors._form
|
|
116
|
+
}
|
|
117
|
+
) }),
|
|
118
|
+
/* @__PURE__ */ r("div", { className: "flex items-center justify-between border-t border-slate-100 px-6 py-4 dark:border-slate-800", children: [
|
|
119
|
+
/* @__PURE__ */ e("div", { className: "text-xs text-slate-400 dark:text-slate-500", children: t.isDirty ? "Unsaved changes" : " " }),
|
|
120
|
+
/* @__PURE__ */ r("div", { className: "flex items-center gap-3", children: [
|
|
121
|
+
/* @__PURE__ */ e(
|
|
122
|
+
"button",
|
|
123
|
+
{
|
|
124
|
+
type: "button",
|
|
125
|
+
onClick: a,
|
|
126
|
+
className: "inline-flex h-9 items-center rounded-lg border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700",
|
|
127
|
+
children: h
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
/* @__PURE__ */ e(
|
|
131
|
+
"button",
|
|
132
|
+
{
|
|
133
|
+
type: "button",
|
|
134
|
+
onClick: t.handleSubmit,
|
|
135
|
+
disabled: t.isSubmitting,
|
|
136
|
+
className: [
|
|
137
|
+
"inline-flex h-9 items-center gap-2 rounded-lg border border-transparent px-4 text-sm font-medium text-white shadow-sm transition",
|
|
138
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900",
|
|
139
|
+
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
140
|
+
g ? "bg-red-600 hover:bg-red-500 focus-visible:ring-red-500 dark:bg-red-600 dark:hover:bg-red-500" : "bg-brand-600 hover:bg-brand-500 focus-visible:ring-brand-500 dark:bg-brand-500 dark:hover:bg-brand-400"
|
|
141
|
+
].join(" "),
|
|
142
|
+
children: t.isSubmitting ? /* @__PURE__ */ r(y, { children: [
|
|
143
|
+
/* @__PURE__ */ e(E, { size: "sm", tone: "white", label: "Submitting" }),
|
|
144
|
+
"Saving…"
|
|
145
|
+
] }) : x
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
] })
|
|
149
|
+
] })
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
"panel"
|
|
153
|
+
)
|
|
154
|
+
] }) : null }),
|
|
155
|
+
document.body
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
D as default
|
|
160
|
+
};
|
|
161
|
+
//# sourceMappingURL=FormModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormModal.js","sources":["../../../../src/components/library/forms/FormModal.jsx"],"sourcesContent":["import React, { useEffect, useCallback } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { XMarkIcon } from \"@heroicons/react/24/outline\";\nimport Spinner from \"../ui/Spinner\";\nimport FormRenderer from \"./FormRenderer\";\nimport useFormState from \"./useFormState\";\n\nconst OVERLAY_VARIANTS = {\n hidden: { opacity: 0 },\n visible: { opacity: 1 },\n};\n\nconst PANEL_VARIANTS = {\n hidden: { opacity: 0, y: 24, scale: 0.97 },\n visible: { opacity: 1, y: 0, scale: 1, transition: { type: \"spring\", damping: 25, stiffness: 350 } },\n exit: { opacity: 0, y: 16, scale: 0.97, transition: { duration: 0.15 } },\n};\n\n/**\n * Size → max-width mapping.\n */\nconst SIZE_CLASSES = {\n sm: \"max-w-md\",\n md: \"max-w-xl\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n};\n\n/**\n * Modal dialog for creating or editing records.\n * Wraps FormRenderer + useFormState in an animated overlay.\n *\n * @param {boolean} isOpen — whether the modal is visible\n * @param {Function} onClose — close handler\n * @param {string} title — modal title (e.g. \"Edit Service\", \"New Incident\")\n * @param {string} subtitle — optional subtitle\n * @param {Array} sections — form schema sections\n * @param {Object} initialValues — prefill for editing (empty = create mode)\n * @param {Function} onSubmit — async (values) => void, called on valid submit\n * @param {string} submitLabel — submit button text (default: \"Save\")\n * @param {string} cancelLabel — cancel button text (default: \"Cancel\")\n * @param {string} size — \"sm\" | \"md\" | \"lg\" | \"xl\" (default: \"lg\")\n * @param {boolean} destructive — if true, submit button is red (for delete confirmations)\n */\nexport default function FormModal({\n isOpen = false,\n onClose,\n title,\n subtitle,\n sections = [],\n initialValues = {},\n onSubmit,\n submitLabel = \"Save\",\n cancelLabel = \"Cancel\",\n size = \"lg\",\n destructive = false,\n minSubmitMs,\n}) {\n const form = useFormState({\n initialValues,\n sections,\n minSubmitMs,\n onSubmit: async (vals) => {\n await onSubmit?.(vals);\n onClose?.();\n },\n });\n\n useEffect(() => {\n if (isOpen) form.reset();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n const onKeyDown = useCallback(\n (e) => {\n if (e.key === \"Escape\") onClose?.();\n },\n [onClose]\n );\n\n useEffect(() => {\n if (!isOpen) return;\n document.addEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = \"hidden\";\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = \"\";\n };\n }, [isOpen, onKeyDown]);\n\n if (typeof document === \"undefined\") return null;\n\n return createPortal(\n <AnimatePresence>\n {isOpen ? (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center px-4 pt-[10vh] sm:pt-[12vh]\">\n {/* Backdrop */}\n <motion.div\n key=\"overlay\"\n className=\"fixed inset-0 bg-black/40 backdrop-blur-sm\"\n variants={OVERLAY_VARIANTS}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"hidden\"\n transition={{ duration: 0.2 }}\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Panel */}\n <motion.div\n key=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n variants={PANEL_VARIANTS}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n className={[\n \"relative z-10 flex w-full flex-col rounded-xl border border-slate-200 bg-white shadow-xl dark:border-slate-800 dark:bg-slate-900\",\n SIZE_CLASSES[size] ?? SIZE_CLASSES.lg,\n \"max-h-[80vh]\",\n ].join(\" \")}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between border-b border-slate-100 px-6 py-4 dark:border-slate-800\">\n <div>\n <h2 className=\"text-base font-semibold text-slate-900 dark:text-slate-50\">\n {title}\n </h2>\n {subtitle ? (\n <p className=\"mt-0.5 text-sm text-slate-500 dark:text-slate-400\">{subtitle}</p>\n ) : null}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-slate-300\"\n aria-label=\"Close\"\n >\n <XMarkIcon className=\"h-5 w-5\" />\n </button>\n </div>\n\n {/* Body — scrollable */}\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n <FormRenderer\n sections={sections}\n values={form.values}\n errors={form.errors}\n touched={form.touched}\n onFieldChange={form.setValue}\n onFieldBlur={form.setTouched}\n formError={form.errors._form}\n />\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between border-t border-slate-100 px-6 py-4 dark:border-slate-800\">\n <div className=\"text-xs text-slate-400 dark:text-slate-500\">\n {form.isDirty ? \"Unsaved changes\" : \"\\u00A0\"}\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"inline-flex h-9 items-center rounded-lg border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700\"\n >\n {cancelLabel}\n </button>\n <button\n type=\"button\"\n onClick={form.handleSubmit}\n disabled={form.isSubmitting}\n className={[\n \"inline-flex h-9 items-center gap-2 rounded-lg border border-transparent px-4 text-sm font-medium text-white shadow-sm transition\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n destructive\n ? \"bg-red-600 hover:bg-red-500 focus-visible:ring-red-500 dark:bg-red-600 dark:hover:bg-red-500\"\n : \"bg-brand-600 hover:bg-brand-500 focus-visible:ring-brand-500 dark:bg-brand-500 dark:hover:bg-brand-400\",\n ].join(\" \")}\n >\n {form.isSubmitting ? (\n <>\n <Spinner size=\"sm\" tone=\"white\" label=\"Submitting\" />\n Saving…\n </>\n ) : submitLabel}\n </button>\n </div>\n </div>\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>,\n document.body\n );\n}\n"],"names":["OVERLAY_VARIANTS","PANEL_VARIANTS","SIZE_CLASSES","FormModal","isOpen","onClose","title","subtitle","sections","initialValues","onSubmit","submitLabel","cancelLabel","size","destructive","minSubmitMs","form","useFormState","vals","useEffect","onKeyDown","useCallback","e","createPortal","AnimatePresence","jsxs","jsx","motion","XMarkIcon","FormRenderer","Fragment","Spinner"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsxs as n, jsx as d } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import p from "./FormSection.js";
|
|
4
|
+
function u({
|
|
5
|
+
sections: t = [],
|
|
6
|
+
values: a = {},
|
|
7
|
+
errors: o = {},
|
|
8
|
+
touched: m = {},
|
|
9
|
+
onFieldChange: i,
|
|
10
|
+
onFieldBlur: s,
|
|
11
|
+
formError: r
|
|
12
|
+
}) {
|
|
13
|
+
return /* @__PURE__ */ n("div", { className: "space-y-8", children: [
|
|
14
|
+
r ? /* @__PURE__ */ d("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-950/30 dark:text-red-300", children: r }) : null,
|
|
15
|
+
t.map((e, l) => /* @__PURE__ */ d(
|
|
16
|
+
p,
|
|
17
|
+
{
|
|
18
|
+
section: e,
|
|
19
|
+
values: a,
|
|
20
|
+
errors: o,
|
|
21
|
+
touched: m,
|
|
22
|
+
onFieldChange: i,
|
|
23
|
+
onFieldBlur: s
|
|
24
|
+
},
|
|
25
|
+
e.id ?? l
|
|
26
|
+
))
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
u as default
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=FormRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormRenderer.js","sources":["../../../../src/components/library/forms/FormRenderer.jsx"],"sourcesContent":["import React from \"react\";\nimport FormSection from \"./FormSection\";\n\n/**\n * Renders a complete form from a schema definition.\n * Pairs with useFormState for state management.\n *\n * @param {Array} sections — form schema sections\n * @param {Object} values — current form values\n * @param {Object} errors — current validation errors\n * @param {Object} touched — which fields have been touched\n * @param {Function} onFieldChange — (fieldId, value) => void\n * @param {Function} onFieldBlur — (fieldId) => void\n * @param {string} formError — top-level form error (e.g. submission failure)\n */\nexport default function FormRenderer({\n sections = [],\n values = {},\n errors = {},\n touched = {},\n onFieldChange,\n onFieldBlur,\n formError,\n}) {\n return (\n <div className=\"space-y-8\">\n {formError ? (\n <div className=\"rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-950/30 dark:text-red-300\">\n {formError}\n </div>\n ) : null}\n\n {sections.map((section, idx) => (\n <FormSection\n key={section.id ?? idx}\n section={section}\n values={values}\n errors={errors}\n touched={touched}\n onFieldChange={onFieldChange}\n onFieldBlur={onFieldBlur}\n />\n ))}\n </div>\n );\n}\n"],"names":["FormRenderer","sections","values","errors","touched","onFieldChange","onFieldBlur","formError","jsxs","jsx","section","idx","FormSection"],"mappings":";;;AAeA,SAAwBA,EAAa;AAAA,EACnC,UAAAC,IAAW,CAAA;AAAA,EACX,QAAAC,IAAS,CAAA;AAAA,EACT,QAAAC,IAAS,CAAA;AAAA,EACT,SAAAC,IAAU,CAAA;AAAA,EACV,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AACF,GAAG;AACD,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAU,aACZ,UAAA;AAAA,IAAAD,IACC,gBAAAE,EAAC,OAAA,EAAI,WAAU,sIACZ,aACH,IACE;AAAA,IAEHR,EAAS,IAAI,CAACS,GAASC,MACtB,gBAAAF;AAAA,MAACG;AAAA,MAAA;AAAA,QAEC,SAAAF;AAAA,QACA,QAAAR;AAAA,QACA,QAAAC;AAAA,QACA,SAAAC;AAAA,QACA,eAAAC;AAAA,QACA,aAAAC;AAAA,MAAA;AAAA,MANKI,EAAQ,MAAMC;AAAA,IAAA,CAQtB;AAAA,EAAA,GACH;AAEJ;"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsxs as a, jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import p from "./FormField.js";
|
|
4
|
+
const x = /* @__PURE__ */ new Set([
|
|
5
|
+
"textarea",
|
|
6
|
+
"radio",
|
|
7
|
+
"checkboxGroup"
|
|
8
|
+
]);
|
|
9
|
+
function b({
|
|
10
|
+
section: t,
|
|
11
|
+
values: l,
|
|
12
|
+
errors: d,
|
|
13
|
+
touched: o,
|
|
14
|
+
onFieldChange: n,
|
|
15
|
+
onFieldBlur: i
|
|
16
|
+
}) {
|
|
17
|
+
const s = t.fields ?? [];
|
|
18
|
+
return s.length ? /* @__PURE__ */ a("fieldset", { className: "space-y-4", children: [
|
|
19
|
+
t.title ? /* @__PURE__ */ a("div", { className: "border-b border-slate-100 pb-3 dark:border-slate-800", children: [
|
|
20
|
+
/* @__PURE__ */ r("legend", { className: "text-sm font-semibold text-slate-900 dark:text-slate-50", children: t.title }),
|
|
21
|
+
t.description ? /* @__PURE__ */ r("p", { className: "mt-0.5 text-xs text-slate-500 dark:text-slate-400", children: t.description }) : null
|
|
22
|
+
] }) : null,
|
|
23
|
+
/* @__PURE__ */ r("div", { className: "grid grid-cols-1 gap-x-4 gap-y-5 sm:grid-cols-2", children: s.map((e) => {
|
|
24
|
+
const c = e.colSpan ?? (x.has(e.type) ? 2 : 1);
|
|
25
|
+
return /* @__PURE__ */ r(
|
|
26
|
+
"div",
|
|
27
|
+
{
|
|
28
|
+
className: c === 2 ? "sm:col-span-2" : void 0,
|
|
29
|
+
children: /* @__PURE__ */ r(
|
|
30
|
+
p,
|
|
31
|
+
{
|
|
32
|
+
field: e,
|
|
33
|
+
value: l[e.id],
|
|
34
|
+
error: d[e.id],
|
|
35
|
+
touched: o[e.id],
|
|
36
|
+
onChange: (m) => n(e.id, m),
|
|
37
|
+
onBlur: () => i(e.id)
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
e.id
|
|
42
|
+
);
|
|
43
|
+
}) })
|
|
44
|
+
] }) : null;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
b as default
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=FormSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormSection.js","sources":["../../../../src/components/library/forms/FormSection.jsx"],"sourcesContent":["import React from \"react\";\nimport FormField from \"./FormField\";\n\n/**\n * Fields that naturally span full width by default.\n */\nconst FULL_WIDTH_TYPES = new Set([\n \"textarea\",\n \"radio\",\n \"checkboxGroup\",\n]);\n\n/**\n * A titled section of a form with a 2-column grid layout.\n * Fields default to 1-column (half width) unless the type is naturally\n * full-width or the field specifies `colSpan: 2`.\n */\nexport default function FormSection({\n section,\n values,\n errors,\n touched,\n onFieldChange,\n onFieldBlur,\n}) {\n const fields = section.fields ?? [];\n if (!fields.length) return null;\n\n return (\n <fieldset className=\"space-y-4\">\n {section.title ? (\n <div className=\"border-b border-slate-100 pb-3 dark:border-slate-800\">\n <legend className=\"text-sm font-semibold text-slate-900 dark:text-slate-50\">\n {section.title}\n </legend>\n {section.description ? (\n <p className=\"mt-0.5 text-xs text-slate-500 dark:text-slate-400\">\n {section.description}\n </p>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"grid grid-cols-1 gap-x-4 gap-y-5 sm:grid-cols-2\">\n {fields.map((field) => {\n const span =\n field.colSpan ??\n (FULL_WIDTH_TYPES.has(field.type) ? 2 : 1);\n\n return (\n <div\n key={field.id}\n className={span === 2 ? \"sm:col-span-2\" : undefined}\n >\n <FormField\n field={field}\n value={values[field.id]}\n error={errors[field.id]}\n touched={touched[field.id]}\n onChange={(val) => onFieldChange(field.id, val)}\n onBlur={() => onFieldBlur(field.id)}\n />\n </div>\n );\n })}\n </div>\n </fieldset>\n );\n}\n"],"names":["FULL_WIDTH_TYPES","FormSection","section","values","errors","touched","onFieldChange","onFieldBlur","fields","jsxs","jsx","field","span","FormField","val"],"mappings":";;;AAMA,MAAMA,wBAAuB,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOD,SAAwBC,EAAY;AAAA,EAClC,SAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,eAAAC;AAAA,EACA,aAAAC;AACF,GAAG;AACD,QAAMC,IAASN,EAAQ,UAAU,CAAA;AACjC,SAAKM,EAAO,SAGV,gBAAAC,EAAC,YAAA,EAAS,WAAU,aACjB,UAAA;AAAA,IAAAP,EAAQ,QACP,gBAAAO,EAAC,OAAA,EAAI,WAAU,wDACb,UAAA;AAAA,MAAA,gBAAAC,EAAC,UAAA,EAAO,WAAU,2DACf,UAAAR,EAAQ,OACX;AAAA,MACCA,EAAQ,cACP,gBAAAQ,EAAC,KAAA,EAAE,WAAU,qDACV,UAAAR,EAAQ,aACX,IACE;AAAA,IAAA,EAAA,CACN,IACE;AAAA,sBAEH,OAAA,EAAI,WAAU,mDACZ,UAAAM,EAAO,IAAI,CAACG,MAAU;AACrB,YAAMC,IACJD,EAAM,YACLX,EAAiB,IAAIW,EAAM,IAAI,IAAI,IAAI;AAE1C,aACE,gBAAAD;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,WAAWE,MAAS,IAAI,kBAAkB;AAAA,UAE1C,UAAA,gBAAAF;AAAA,YAACG;AAAA,YAAA;AAAA,cACC,OAAAF;AAAA,cACA,OAAOR,EAAOQ,EAAM,EAAE;AAAA,cACtB,OAAOP,EAAOO,EAAM,EAAE;AAAA,cACtB,SAASN,EAAQM,EAAM,EAAE;AAAA,cACzB,UAAU,CAACG,MAAQR,EAAcK,EAAM,IAAIG,CAAG;AAAA,cAC9C,QAAQ,MAAMP,EAAYI,EAAM,EAAE;AAAA,YAAA;AAAA,UAAA;AAAA,QACpC;AAAA,QAVKA,EAAM;AAAA,MAAA;AAAA,IAajB,CAAC,EAAA,CACH;AAAA,EAAA,GACF,IAxCyB;AA0C7B;"}
|