@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,297 @@
1
+ import React from "react";
2
+ import {
3
+ BaseCard,
4
+ MetricCard,
5
+ TableCard,
6
+ ChartCard,
7
+ D3Chart,
8
+ D3ChartTemplates,
9
+ StatusCard,
10
+ ListCard,
11
+ UIButton,
12
+ UIChip,
13
+ UIText
14
+ } from "@/components/library";
15
+
16
+ const BUILTIN_COMPONENTS = {
17
+ NarrativeSummary({ summary, title }) {
18
+ return (
19
+ <div className="text-sm text-slate-600 dark:text-slate-300">
20
+ {title && <div className="mb-1 font-medium text-slate-900 dark:text-slate-50">{title}</div>}
21
+ {summary}
22
+ </div>
23
+ );
24
+ },
25
+
26
+ MetricsStrip({ metrics = [], title, collapsible = false, collapsed: initialCollapsed = false }) {
27
+ const [collapsed, setCollapsed] = React.useState(initialCollapsed);
28
+
29
+ const items = metrics.length ? metrics : [
30
+ { label: "Metric A", value: "—", trend: null },
31
+ { label: "Metric B", value: "—", trend: null },
32
+ { label: "Metric C", value: "—", trend: null }
33
+ ];
34
+
35
+ if (collapsible && collapsed) {
36
+ return (
37
+ <button
38
+ type="button"
39
+ onClick={() => setCollapsed(false)}
40
+ className="flex w-full items-center justify-between rounded-lg border border-slate-200 bg-white px-3 py-2 text-left text-xs text-slate-500 hover:bg-slate-50 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400 dark:hover:bg-slate-800"
41
+ >
42
+ <span>{title ?? "Metrics"}: {items.map(m => `${m.label} ${m.value}`).join(" · ")}</span>
43
+ <span>▸</span>
44
+ </button>
45
+ );
46
+ }
47
+
48
+ return (
49
+ <div className="rounded-xl border border-slate-200 bg-white p-3 dark:border-slate-800 dark:bg-slate-900">
50
+ {(title || collapsible) && (
51
+ <div className="mb-2 flex items-center justify-between">
52
+ <span className="text-xs font-medium text-slate-500 dark:text-slate-400">{title ?? "Metrics"}</span>
53
+ {collapsible && (
54
+ <button
55
+ type="button"
56
+ onClick={() => setCollapsed(true)}
57
+ className="text-xs text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300"
58
+ >
59
+ Collapse
60
+ </button>
61
+ )}
62
+ </div>
63
+ )}
64
+ <div className="flex flex-wrap gap-4">
65
+ {items.map((m) => (
66
+ <div key={m.label} className="min-w-[80px]">
67
+ <div className="text-xs text-slate-400 dark:text-slate-500">{m.label}</div>
68
+ <div className="flex items-baseline gap-1.5">
69
+ <span className="text-sm font-semibold text-slate-700 dark:text-slate-200">{m.value}</span>
70
+ {m.trend && (
71
+ <span className={`text-xs ${String(m.trend).startsWith?.("+") || m.trend > 0 ? "text-red-500" : "text-emerald-500"}`}>
72
+ {typeof m.trend === "number" ? (m.trend > 0 ? `+${m.trend}` : m.trend) : m.trend}
73
+ </span>
74
+ )}
75
+ </div>
76
+ </div>
77
+ ))}
78
+ </div>
79
+ </div>
80
+ );
81
+ },
82
+
83
+ ItemList({ items = [], title, onItemClick }) {
84
+ const [expanded, setExpanded] = React.useState(null);
85
+ if (!items.length) {
86
+ return <div className="text-sm text-slate-500 dark:text-slate-400">No items.</div>;
87
+ }
88
+ return (
89
+ <div className="space-y-2">
90
+ {title && (
91
+ <div className="flex items-center justify-between">
92
+ <span className="text-sm font-medium text-slate-900 dark:text-slate-50">{title}</span>
93
+ <span className="text-xs text-slate-400 dark:text-slate-500">{items.length} items</span>
94
+ </div>
95
+ )}
96
+ <div className="space-y-1.5">
97
+ {items.map((item, idx) => (
98
+ <div key={item.id ?? idx} className="rounded-xl border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900">
99
+ <button
100
+ type="button"
101
+ onClick={() => {
102
+ setExpanded(expanded === (item.id ?? idx) ? null : (item.id ?? idx));
103
+ onItemClick?.(item);
104
+ }}
105
+ className="flex w-full items-start justify-between gap-3 p-3 text-left"
106
+ >
107
+ <div className="min-w-0 flex-1">
108
+ <div className="flex items-start justify-between gap-2">
109
+ <div className="text-sm font-medium text-slate-900 dark:text-slate-50">
110
+ {item.title ?? item.name ?? `Item ${idx + 1}`}
111
+ </div>
112
+ {item.status && (
113
+ <UIChip tone={item.status === "critical" ? "danger" : item.status === "warning" ? "warning" : "neutral"} className="shrink-0">
114
+ {item.status}
115
+ </UIChip>
116
+ )}
117
+ </div>
118
+ {item.description && (
119
+ <div className="mt-1 text-xs text-slate-500 dark:text-slate-400">{item.description}</div>
120
+ )}
121
+ </div>
122
+ </button>
123
+ {expanded === (item.id ?? idx) && item.actions && (
124
+ <div className="border-t border-slate-100 px-3 py-2 dark:border-slate-800">
125
+ <div className="flex flex-wrap gap-1.5">
126
+ {item.actions.map((action, i) => (
127
+ <button
128
+ key={typeof action === "string" ? action : action.label}
129
+ type="button"
130
+ className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
131
+ i === 0
132
+ ? "bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-200"
133
+ : "bg-slate-100 text-slate-700 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
134
+ }`}
135
+ >
136
+ {typeof action === "string" ? action : action.label}
137
+ </button>
138
+ ))}
139
+ </div>
140
+ </div>
141
+ )}
142
+ </div>
143
+ ))}
144
+ </div>
145
+ </div>
146
+ );
147
+ },
148
+
149
+ DataTable({ title, subtitle, columns = [], data = [], rows, searchable = true, sortable = true, paginated = true, pageSize = 5 }) {
150
+ const tableData = data.length ? data : (rows ?? []);
151
+ const cols = columns.length ? columns : [
152
+ { key: "name", label: "Name" },
153
+ { key: "status", label: "Status" },
154
+ { key: "value", label: "Value" }
155
+ ];
156
+ return (
157
+ <TableCard
158
+ title={title ?? "Data"}
159
+ subtitle={subtitle}
160
+ columns={cols}
161
+ data={tableData}
162
+ searchable={searchable}
163
+ sortable={sortable}
164
+ paginated={paginated}
165
+ pageSize={pageSize}
166
+ />
167
+ );
168
+ },
169
+
170
+ DataChart({ title, subtitle, chartType = "line", data = [], height = 200 }) {
171
+ const series = data.length ? data : Array.from({ length: 12 }, (_, i) => ({ x: i, y: Math.random() * 100 }));
172
+ return (
173
+ <ChartCard
174
+ title={title ?? "Trend"}
175
+ subtitle={subtitle ?? "Data visualization"}
176
+ chartType={chartType}
177
+ height={height}
178
+ chart={
179
+ <D3Chart
180
+ data={series}
181
+ responsive
182
+ height={height}
183
+ ariaLabel={title}
184
+ renderChart={(svg, d, dims, opts) => D3ChartTemplates.lineChart(svg, d, dims, opts)}
185
+ options={{ xKey: "x", yKey: "y", showGrid: true, showAxes: true }}
186
+ />
187
+ }
188
+ />
189
+ );
190
+ },
191
+
192
+ MetricCard({ title, label, value, trend, change, changeType, color, icon, description }) {
193
+ return (
194
+ <MetricCard
195
+ title={title ?? label}
196
+ value={value ?? "—"}
197
+ trend={trend}
198
+ change={change}
199
+ changeType={changeType}
200
+ color={color}
201
+ icon={icon}
202
+ description={description}
203
+ />
204
+ );
205
+ },
206
+
207
+ StatusCard({ title, subtitle, status, items, layout, showProgress, showTimestamp }) {
208
+ return (
209
+ <StatusCard
210
+ title={title}
211
+ subtitle={subtitle}
212
+ status={status}
213
+ items={items}
214
+ layout={layout}
215
+ showProgress={showProgress}
216
+ showTimestamp={showTimestamp}
217
+ />
218
+ );
219
+ },
220
+
221
+ ActionList({ actions = [], title, onAction }) {
222
+ return (
223
+ <div className="rounded-2xl border border-slate-200 bg-white p-4 dark:border-slate-800 dark:bg-slate-900">
224
+ {title && <div className="mb-3 text-sm font-medium text-slate-900 dark:text-slate-50">{title}</div>}
225
+ <div className="flex flex-wrap gap-2">
226
+ {actions.map((action, i) => (
227
+ <UIButton
228
+ key={i}
229
+ size="sm"
230
+ variant={i === 0 ? "primary" : "outline"}
231
+ onClick={() => onAction?.(action)}
232
+ >
233
+ {typeof action === "string" ? action : action.label}
234
+ </UIButton>
235
+ ))}
236
+ </div>
237
+ </div>
238
+ );
239
+ },
240
+
241
+ CalloutCard({ title, message, tone = "neutral" }) {
242
+ const toneClasses = {
243
+ neutral: "border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-800 dark:bg-slate-950/30 dark:text-slate-200",
244
+ success: "border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-900/40 dark:bg-emerald-950/20 dark:text-emerald-200",
245
+ warning: "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-900/40 dark:bg-amber-950/20 dark:text-amber-200",
246
+ danger: "border-rose-200 bg-rose-50 text-rose-800 dark:border-rose-900/40 dark:bg-rose-950/20 dark:text-rose-200"
247
+ };
248
+ return (
249
+ <div className={`rounded-xl border p-4 ${toneClasses[tone] ?? toneClasses.neutral}`}>
250
+ {title && <div className="mb-1 text-sm font-semibold">{title}</div>}
251
+ <div className="text-sm">{message}</div>
252
+ </div>
253
+ );
254
+ },
255
+
256
+ Divider() {
257
+ return <div className="h-px bg-slate-200 dark:bg-slate-800" />;
258
+ },
259
+
260
+ Spacer({ size = "md" }) {
261
+ const heights = { sm: "h-2", md: "h-4", lg: "h-6" };
262
+ return <div className={heights[size] ?? heights.md} />;
263
+ }
264
+ };
265
+
266
+ let _registry = { ...BUILTIN_COMPONENTS };
267
+
268
+ export function getComponentRegistry() {
269
+ return _registry;
270
+ }
271
+
272
+ export function registerComponent(type, Component) {
273
+ _registry = { ..._registry, [type]: Component };
274
+ }
275
+
276
+ export function registerComponents(map) {
277
+ _registry = { ..._registry, ...map };
278
+ }
279
+
280
+ export function renderSchemaComponent(component, index) {
281
+ const registry = getComponentRegistry();
282
+ const Component = registry[component.type];
283
+ if (!Component) {
284
+ return (
285
+ <div key={index} className="rounded-lg border border-dashed border-slate-300 p-3 text-xs text-slate-500 dark:border-slate-700">
286
+ Unknown component: {component.type}
287
+ </div>
288
+ );
289
+ }
290
+ return <Component key={component.id ?? index} {...(component.props ?? {})} />;
291
+ }
292
+
293
+ export function renderSchema(components = []) {
294
+ return components.map((c, i) => renderSchemaComponent(c, i));
295
+ }
296
+
297
+ export default BUILTIN_COMPONENTS;
@@ -0,0 +1 @@
1
+ export * from './utils';
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }