@cfast/ui 0.1.0 → 0.2.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.
@@ -1,486 +0,0 @@
1
- // src/plugin.tsx
2
- import { createContext, useContext } from "react";
3
-
4
- // src/headless-defaults.tsx
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
- var headlessDefaults = {
7
- // Actions
8
- button: ({ children, onClick, disabled, loading, type }) => /* @__PURE__ */ jsx("button", { onClick, disabled: disabled || loading, type: type ?? "button", children: loading ? "Loading..." : children }),
9
- tooltip: ({ children, title }) => /* @__PURE__ */ jsx("span", { title, children }),
10
- confirmDialog: ({ open, onClose, onConfirm, title, description, confirmLabel, cancelLabel }) => open ? /* @__PURE__ */ jsxs("dialog", { open: true, children: [
11
- /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: title }) }),
12
- description ? /* @__PURE__ */ jsx("p", { children: description }) : null,
13
- /* @__PURE__ */ jsxs("div", { children: [
14
- /* @__PURE__ */ jsx("button", { onClick: onClose, children: cancelLabel ?? "Cancel" }),
15
- /* @__PURE__ */ jsx("button", { onClick: onConfirm, children: confirmLabel ?? "Confirm" })
16
- ] })
17
- ] }) : null,
18
- // Data display
19
- table: ({ children }) => /* @__PURE__ */ jsx("table", { children }),
20
- tableHead: ({ children }) => /* @__PURE__ */ jsx("thead", { children }),
21
- tableBody: ({ children }) => /* @__PURE__ */ jsx("tbody", { children }),
22
- tableRow: ({ children, onClick }) => /* @__PURE__ */ jsx("tr", { onClick, children }),
23
- tableCell: ({ children, header, sortable, sortDirection, onSort }) => {
24
- const Tag = header ? "th" : "td";
25
- return /* @__PURE__ */ jsxs(
26
- Tag,
27
- {
28
- onClick: sortable ? onSort : void 0,
29
- style: sortable ? { cursor: "pointer" } : void 0,
30
- children: [
31
- children,
32
- sortable && sortDirection ? sortDirection === "asc" ? " \u2191" : " \u2193" : null
33
- ]
34
- }
35
- );
36
- },
37
- chip: ({ children, size }) => /* @__PURE__ */ jsx(
38
- "span",
39
- {
40
- style: {
41
- display: "inline-block",
42
- padding: size === "sm" ? "1px 6px" : "2px 8px",
43
- borderRadius: "12px",
44
- fontSize: size === "sm" ? "12px" : "14px",
45
- backgroundColor: "#eee"
46
- },
47
- children
48
- }
49
- ),
50
- // Layout
51
- appShell: ({ children, sidebar, header }) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", minHeight: "100vh" }, children: [
52
- sidebar ? /* @__PURE__ */ jsx("nav", { children: sidebar }) : null,
53
- /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
54
- header ?? null,
55
- /* @__PURE__ */ jsx("main", { children })
56
- ] })
57
- ] }),
58
- sidebar: ({ children }) => /* @__PURE__ */ jsx("aside", { style: { width: "240px", borderRight: "1px solid #ddd" }, children }),
59
- pageContainer: ({ children, title, actions }) => /* @__PURE__ */ jsxs("div", { children: [
60
- title || actions ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
61
- title ? /* @__PURE__ */ jsx("h1", { children: title }) : null,
62
- actions ?? null
63
- ] }) : null,
64
- children
65
- ] }),
66
- breadcrumb: ({ items }) => /* @__PURE__ */ jsx("nav", { "aria-label": "breadcrumb", children: items.map((item, i) => /* @__PURE__ */ jsxs("span", { children: [
67
- i > 0 ? " / " : null,
68
- item.to ? /* @__PURE__ */ jsx("a", { href: item.to, children: item.label }) : item.label
69
- ] }, i)) }),
70
- // Feedback
71
- toast: ({ children }) => /* @__PURE__ */ jsx("div", { children }),
72
- alert: ({ children, color }) => /* @__PURE__ */ jsx(
73
- "div",
74
- {
75
- role: "alert",
76
- style: {
77
- padding: "8px 12px",
78
- borderRadius: "4px",
79
- backgroundColor: color === "danger" ? "#fee" : color === "success" ? "#efe" : color === "warning" ? "#ffe" : "#f5f5f5"
80
- },
81
- children
82
- }
83
- ),
84
- // File
85
- dropZone: ({ children, isDragOver, onClick, onDrop, onDragOver, onDragLeave }) => /* @__PURE__ */ jsx(
86
- "div",
87
- {
88
- onClick,
89
- onDrop: (e) => {
90
- e.preventDefault();
91
- onDrop(e.dataTransfer.files);
92
- },
93
- onDragOver: (e) => {
94
- e.preventDefault();
95
- onDragOver(e);
96
- },
97
- onDragLeave,
98
- style: {
99
- border: `2px dashed ${isDragOver ? "#4caf50" : "#ccc"}`,
100
- borderRadius: "8px",
101
- padding: "32px",
102
- textAlign: "center",
103
- cursor: "pointer"
104
- },
105
- children
106
- }
107
- )
108
- };
109
-
110
- // src/plugin.tsx
111
- import { jsx as jsx2 } from "react/jsx-runtime";
112
- var UIPluginContext = createContext(null);
113
- function createUIPlugin(config) {
114
- return { components: config.components };
115
- }
116
- function UIPluginProvider({
117
- plugin,
118
- children
119
- }) {
120
- return /* @__PURE__ */ jsx2(UIPluginContext.Provider, { value: plugin, children });
121
- }
122
- function useUIPlugin() {
123
- return useContext(UIPluginContext);
124
- }
125
- function useComponent(slot) {
126
- const plugin = useUIPlugin();
127
- const merged = { ...headlessDefaults, ...plugin?.components };
128
- return merged[slot];
129
- }
130
-
131
- // src/hooks/use-toast.ts
132
- import { createContext as createContext2, useContext as useContext2, useCallback } from "react";
133
- var ToastContext = createContext2(null);
134
- function useToast() {
135
- const ctx = useContext2(ToastContext);
136
- if (!ctx) {
137
- throw new Error("useToast must be used within a <ToastProvider>");
138
- }
139
- const show = useCallback(
140
- (options) => ctx.show(options),
141
- [ctx]
142
- );
143
- const success = useCallback(
144
- (message, description) => ctx.show({ message, type: "success", description }),
145
- [ctx]
146
- );
147
- const error = useCallback(
148
- (message, description) => ctx.show({ message, type: "error", description }),
149
- [ctx]
150
- );
151
- const info = useCallback(
152
- (message, description) => ctx.show({ message, type: "info", description }),
153
- [ctx]
154
- );
155
- const warning = useCallback(
156
- (message, description) => ctx.show({ message, type: "warning", description }),
157
- [ctx]
158
- );
159
- return { show, success, error, info, warning };
160
- }
161
-
162
- // src/components/permission-gate.tsx
163
- import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
164
- function PermissionGate({
165
- action,
166
- children,
167
- fallback
168
- }) {
169
- if (action.invisible) {
170
- return null;
171
- }
172
- if (!action.permitted) {
173
- return fallback ? /* @__PURE__ */ jsx3(Fragment, { children: fallback }) : null;
174
- }
175
- return /* @__PURE__ */ jsx3(Fragment, { children });
176
- }
177
-
178
- // src/components/avatar-with-initials.tsx
179
- import { jsx as jsx4 } from "react/jsx-runtime";
180
- function getInitials(name) {
181
- return name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
182
- }
183
- var sizeMap = { sm: 32, md: 40, lg: 56 };
184
- function AvatarWithInitials({
185
- src,
186
- name,
187
- size = "md"
188
- }) {
189
- const px = sizeMap[size];
190
- if (src) {
191
- return /* @__PURE__ */ jsx4(
192
- "img",
193
- {
194
- src,
195
- alt: name,
196
- style: {
197
- width: px,
198
- height: px,
199
- borderRadius: "50%",
200
- objectFit: "cover"
201
- }
202
- }
203
- );
204
- }
205
- return /* @__PURE__ */ jsx4(
206
- "span",
207
- {
208
- "aria-label": name,
209
- style: {
210
- display: "inline-flex",
211
- alignItems: "center",
212
- justifyContent: "center",
213
- width: px,
214
- height: px,
215
- borderRadius: "50%",
216
- backgroundColor: "#ddd",
217
- fontSize: px * 0.4,
218
- fontWeight: "bold"
219
- },
220
- children: getInitials(name)
221
- }
222
- );
223
- }
224
-
225
- // src/fields/date-field.tsx
226
- import { jsx as jsx5 } from "react/jsx-runtime";
227
- var rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
228
- function getRelativeTime(date) {
229
- const now = Date.now();
230
- const diffMs = date.getTime() - now;
231
- const diffSec = Math.round(diffMs / 1e3);
232
- const diffMin = Math.round(diffSec / 60);
233
- const diffHour = Math.round(diffMin / 60);
234
- const diffDay = Math.round(diffHour / 24);
235
- if (Math.abs(diffSec) < 60) return rtf.format(diffSec, "second");
236
- if (Math.abs(diffMin) < 60) return rtf.format(diffMin, "minute");
237
- if (Math.abs(diffHour) < 24) return rtf.format(diffHour, "hour");
238
- if (Math.abs(diffDay) < 30) return rtf.format(diffDay, "day");
239
- const diffMonth = Math.round(diffDay / 30);
240
- if (Math.abs(diffMonth) < 12) return rtf.format(diffMonth, "month");
241
- return rtf.format(Math.round(diffDay / 365), "year");
242
- }
243
- function formatDate(date, format, locale) {
244
- switch (format) {
245
- case "relative":
246
- return getRelativeTime(date);
247
- case "long":
248
- return new Intl.DateTimeFormat(locale, {
249
- dateStyle: "long"
250
- }).format(date);
251
- case "datetime":
252
- return new Intl.DateTimeFormat(locale, {
253
- dateStyle: "medium",
254
- timeStyle: "short"
255
- }).format(date);
256
- case "short":
257
- default:
258
- return new Intl.DateTimeFormat(locale, {
259
- dateStyle: "medium"
260
- }).format(date);
261
- }
262
- }
263
- function DateField({
264
- value,
265
- format = "short",
266
- locale = "en"
267
- }) {
268
- if (value == null) {
269
- return /* @__PURE__ */ jsx5("span", { children: "\u2014" });
270
- }
271
- const date = value instanceof Date ? value : typeof value === "string" || typeof value === "number" ? new Date(value) : new Date(String(value));
272
- if (Number.isNaN(date.getTime())) {
273
- return /* @__PURE__ */ jsx5("span", { children: "Invalid date" });
274
- }
275
- return /* @__PURE__ */ jsx5("time", { dateTime: date.toISOString(), children: formatDate(date, format, locale) });
276
- }
277
-
278
- // src/fields/boolean-field.tsx
279
- import { jsx as jsx6 } from "react/jsx-runtime";
280
- function BooleanField({
281
- value,
282
- trueLabel = "Yes",
283
- falseLabel = "No",
284
- trueColor = "success",
285
- falseColor = "neutral"
286
- }) {
287
- const Chip = useComponent("chip");
288
- if (value == null) {
289
- return /* @__PURE__ */ jsx6("span", { children: "\u2014" });
290
- }
291
- const boolValue = Boolean(value);
292
- const color = boolValue ? trueColor : falseColor;
293
- const chipColor = isChipColor(color) ? color : "neutral";
294
- return /* @__PURE__ */ jsx6(
295
- Chip,
296
- {
297
- color: chipColor,
298
- variant: "soft",
299
- size: "sm",
300
- children: boolValue ? trueLabel : falseLabel
301
- }
302
- );
303
- }
304
- var CHIP_COLORS = /* @__PURE__ */ new Set(["success", "neutral", "danger", "primary", "warning"]);
305
- function isChipColor(value) {
306
- return CHIP_COLORS.has(value);
307
- }
308
-
309
- // src/fields/number-field.tsx
310
- import { jsx as jsx7 } from "react/jsx-runtime";
311
- function NumberField({
312
- value,
313
- locale = "en",
314
- currency,
315
- decimals
316
- }) {
317
- if (value == null) {
318
- return /* @__PURE__ */ jsx7("span", { children: "\u2014" });
319
- }
320
- const num = typeof value === "number" ? value : Number(value);
321
- if (Number.isNaN(num)) {
322
- return /* @__PURE__ */ jsx7("span", { children: "\u2014" });
323
- }
324
- const options = {};
325
- if (currency) {
326
- options.style = "currency";
327
- options.currency = currency;
328
- }
329
- if (decimals !== void 0) {
330
- options.minimumFractionDigits = decimals;
331
- options.maximumFractionDigits = decimals;
332
- }
333
- const formatted = new Intl.NumberFormat(locale, options).format(num);
334
- return /* @__PURE__ */ jsx7("span", { children: formatted });
335
- }
336
-
337
- // src/fields/text-field.tsx
338
- import { jsx as jsx8 } from "react/jsx-runtime";
339
- function TextField({
340
- value,
341
- maxLength
342
- }) {
343
- if (value == null) {
344
- return /* @__PURE__ */ jsx8("span", { children: "\u2014" });
345
- }
346
- const text = String(value);
347
- const display = maxLength && text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
348
- if (maxLength && text.length > maxLength) {
349
- return /* @__PURE__ */ jsx8("span", { title: text, children: display });
350
- }
351
- return /* @__PURE__ */ jsx8("span", { children: display });
352
- }
353
-
354
- // src/fields/email-field.tsx
355
- import { jsx as jsx9 } from "react/jsx-runtime";
356
- function EmailField({ value }) {
357
- if (value == null) {
358
- return /* @__PURE__ */ jsx9("span", { children: "\u2014" });
359
- }
360
- const email = String(value);
361
- return /* @__PURE__ */ jsx9("a", { href: `mailto:${email}`, children: email });
362
- }
363
-
364
- // src/fields/json-field.tsx
365
- import { useState } from "react";
366
- import { jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
367
- function JsonField({
368
- value,
369
- collapsed = false
370
- }) {
371
- const [isCollapsed, setIsCollapsed] = useState(collapsed);
372
- if (value == null) {
373
- return /* @__PURE__ */ jsx10("span", { children: "\u2014" });
374
- }
375
- const formatted = JSON.stringify(value, null, 2);
376
- if (isCollapsed) {
377
- const preview = JSON.stringify(value);
378
- const short = preview.length > 60 ? `${preview.slice(0, 60)}\u2026` : preview;
379
- return /* @__PURE__ */ jsxs2("span", { children: [
380
- /* @__PURE__ */ jsx10("code", { children: short }),
381
- " ",
382
- /* @__PURE__ */ jsx10(
383
- "button",
384
- {
385
- onClick: () => setIsCollapsed(false),
386
- style: { border: "none", background: "none", cursor: "pointer", color: "#666", fontSize: "12px" },
387
- children: "expand"
388
- }
389
- )
390
- ] });
391
- }
392
- return /* @__PURE__ */ jsx10(
393
- "pre",
394
- {
395
- style: {
396
- margin: 0,
397
- fontSize: "13px",
398
- backgroundColor: "#f5f5f5",
399
- padding: "8px",
400
- borderRadius: "4px",
401
- overflow: "auto"
402
- },
403
- children: /* @__PURE__ */ jsx10("code", { children: formatted })
404
- }
405
- );
406
- }
407
-
408
- // src/fields/field-for-column.ts
409
- function fieldForColumn(column) {
410
- const { dataType, name } = column;
411
- const dt = dataType.toLowerCase();
412
- if (dt === "boolean" || dt === "integer" && name.startsWith("is")) {
413
- return BooleanField;
414
- }
415
- if (dt.includes("timestamp") || dt.includes("date") || dt.includes("datetime")) {
416
- return DateField;
417
- }
418
- if (dt === "integer" || dt === "real" || dt === "numeric" || dt.includes("int") || dt.includes("float") || dt.includes("double") || dt.includes("decimal")) {
419
- return NumberField;
420
- }
421
- if (name === "email" || name.endsWith("Email")) {
422
- return EmailField;
423
- }
424
- if (dt === "json" || dt === "jsonb" || dt === "blob") {
425
- return JsonField;
426
- }
427
- return TextField;
428
- }
429
- function fieldsForTable(table) {
430
- const result = {};
431
- for (const [key, col] of Object.entries(table)) {
432
- if (isColumnMeta(col)) {
433
- result[key] = fieldForColumn(col);
434
- }
435
- }
436
- return result;
437
- }
438
- function isColumnMeta(value) {
439
- return typeof value === "object" && value !== null && "dataType" in value && typeof value.dataType === "string" && "name" in value && typeof value.name === "string";
440
- }
441
-
442
- // src/record-access.ts
443
- function getField(obj, key) {
444
- if (typeof obj !== "object" || obj === null) {
445
- return void 0;
446
- }
447
- return obj[key];
448
- }
449
- function getRecordId(obj) {
450
- const id = getField(obj, "id");
451
- if (typeof id === "string" || typeof id === "number") {
452
- return id;
453
- }
454
- return 0;
455
- }
456
-
457
- // src/hooks/use-action-status.ts
458
- import { useActions } from "@cfast/actions/client";
459
- function useActionStatus(descriptor) {
460
- const actions = useActions(descriptor);
461
- const name = descriptor.actionNames[0];
462
- return actions[name]();
463
- }
464
-
465
- export {
466
- createUIPlugin,
467
- UIPluginProvider,
468
- useUIPlugin,
469
- useComponent,
470
- ToastContext,
471
- useToast,
472
- PermissionGate,
473
- getInitials,
474
- AvatarWithInitials,
475
- getField,
476
- getRecordId,
477
- DateField,
478
- BooleanField,
479
- NumberField,
480
- TextField,
481
- EmailField,
482
- JsonField,
483
- fieldForColumn,
484
- fieldsForTable,
485
- useActionStatus
486
- };
package/dist/joy.d.ts DELETED
@@ -1,199 +0,0 @@
1
- import { ReactNode, ReactElement } from 'react';
2
- import { ButtonProps } from '@mui/joy/Button';
3
- import { ActionHookResult } from '@cfast/actions/client';
4
- import { a9 as WhenForbidden, s as ConfirmOptions, r as ConfirmDialogSlotProps, M as FormStatusProps, b as EmptyStateProps, d as BreadcrumbItem, e as TabItem, A as AppShellProps, f as NavigationItem, g as UserMenuProps, t as DataTableProps, z as FilterBarProps, n as BulkAction, v as DropZoneProps, O as ImagePreviewProps, y as FileListProps, Q as ListViewProps, u as DetailViewProps, k as AvatarWithInitialsProps, X as RoleBadgeProps, P as ImpersonationBannerProps } from './permission-gate-DVmY42oz.js';
5
- export { V as PermissionGate } from './permission-gate-DVmY42oz.js';
6
- import * as react_jsx_runtime from 'react/jsx-runtime';
7
-
8
- type JoyActionButtonProps = {
9
- action: ActionHookResult;
10
- children: ReactNode;
11
- whenForbidden?: WhenForbidden;
12
- confirmation?: string | ConfirmOptions;
13
- } & Omit<ButtonProps, "children" | "onClick" | "disabled" | "loading" | "action">;
14
- /**
15
- * Joy UI styled ActionButton.
16
- *
17
- * Takes an `ActionHookResult` from `useActions()` — no hooks inside,
18
- * pure presentation component. All Joy Button props (sx, fullWidth, etc.)
19
- * are forwarded to the underlying Button.
20
- */
21
- declare function ActionButton({ action, children, whenForbidden, confirmation: _confirmation, variant, color, size, ...buttonProps }: JoyActionButtonProps): ReactElement | null;
22
-
23
- /**
24
- * Joy UI ConfirmDialog — Modal + ModalDialog based on the example app pattern.
25
- */
26
- declare function ConfirmDialog({ open, onClose, onConfirm, title, description, confirmLabel, cancelLabel, variant, }: ConfirmDialogSlotProps): ReactElement;
27
-
28
- /**
29
- * Joy UI ToastProvider backed by Sonner.
30
- */
31
- declare function ToastProvider({ children }: {
32
- children: ReactNode;
33
- }): ReactElement;
34
-
35
- /**
36
- * Joy UI FormStatus — displays action result feedback as Joy Alerts.
37
- */
38
- declare function FormStatus({ data }: FormStatusProps): ReactElement | null;
39
-
40
- /**
41
- * Joy UI EmptyState — centered layout with optional CTA.
42
- */
43
- declare function EmptyState({ title, description, createAction, createLabel, icon: Icon, }: EmptyStateProps): ReactElement;
44
-
45
- /**
46
- * Joy UI NavigationProgress — LinearProgress with absolute positioning.
47
- */
48
- declare function NavigationProgress(): ReactElement | null;
49
-
50
- type PageContainerProps = {
51
- title?: string;
52
- breadcrumb?: BreadcrumbItem[];
53
- actions?: ReactNode;
54
- tabs?: TabItem[];
55
- children: ReactNode;
56
- };
57
- /**
58
- * Joy UI PageContainer — title + breadcrumb + action toolbar.
59
- */
60
- declare function PageContainer({ title, breadcrumb, actions, tabs: _tabs, children, }: PageContainerProps): ReactElement;
61
-
62
- /**
63
- * Joy UI AppShell — sidebar + header + content layout.
64
- */
65
- declare function AppShell({ children, sidebar, header, }: AppShellProps): ReactElement;
66
- declare namespace AppShell {
67
- var Sidebar: typeof AppShellSidebar;
68
- var Header: typeof AppShellHeader;
69
- }
70
- /**
71
- * Joy UI Sidebar — Sheet with List navigation items.
72
- */
73
- declare function AppShellSidebar({ items }: {
74
- items: NavigationItem[];
75
- }): ReactElement;
76
- /**
77
- * Joy UI AppShell Header — Sheet with flex layout.
78
- */
79
- declare function AppShellHeader({ children, userMenu, }: {
80
- children?: ReactNode;
81
- userMenu?: ReactNode;
82
- }): ReactElement;
83
-
84
- /**
85
- * Joy UI UserMenu — Dropdown with Avatar trigger, user info, and links.
86
- */
87
- declare function UserMenu({ links, onSignOut, }: UserMenuProps): ReactElement | null;
88
-
89
- /**
90
- * Joy UI styled DataTable.
91
- * Renders a MUI Joy Table with sorting, selection, and row actions.
92
- */
93
- declare function DataTable<T = unknown>({ data, columns: columnsProp, selectable, selectedRows: externalSelectedRows, onSelectionChange, onRowClick, getRowId, emptyMessage, }: DataTableProps<T>): ReactElement;
94
-
95
- /**
96
- * Joy UI styled FilterBar — URL-synced filter controls.
97
- */
98
- declare function FilterBar({ filters, searchable, }: FilterBarProps): ReactElement;
99
-
100
- type BulkActionBarProps = {
101
- selectedCount: number;
102
- actions: BulkAction[];
103
- onAction: (action: BulkAction) => void;
104
- onClearSelection: () => void;
105
- };
106
- /**
107
- * Joy UI styled BulkActionBar.
108
- * Shown when rows are selected in a DataTable.
109
- */
110
- declare function BulkActionBar({ selectedCount, actions, onAction, onClearSelection, }: BulkActionBarProps): ReactElement | null;
111
-
112
- /**
113
- * Joy UI styled DropZone — drag-and-drop file upload area.
114
- */
115
- declare function DropZone({ upload, multiple, children, }: DropZoneProps): ReactElement;
116
-
117
- /**
118
- * Joy UI styled ImagePreview.
119
- */
120
- declare function ImagePreview({ fileKey, src, getUrl, width, height, fallback, alt, }: ImagePreviewProps): ReactElement;
121
-
122
- /**
123
- * Joy UI styled FileList.
124
- */
125
- declare function FileList({ files, onDownload, }: FileListProps): ReactElement;
126
-
127
- /**
128
- * Joy UI styled ListView — full page list with filters, table, and pagination.
129
- */
130
- declare function ListView<T = unknown>({ title, data, table: _table, columns, actions: _actions, filters, searchable, createAction, createLabel, selectable, bulkActions, breadcrumb, }: ListViewProps<T>): ReactElement;
131
-
132
- /**
133
- * Joy UI styled DetailView — two-column grid of record fields.
134
- */
135
- declare function DetailView<T = unknown>({ title, table, record, fields: fieldsProp, exclude, breadcrumb, }: DetailViewProps<T>): ReactElement;
136
-
137
- /**
138
- * Joy UI AvatarWithInitials — MUI Joy Avatar with initials fallback.
139
- */
140
- declare function AvatarWithInitials({ src, name, size, }: AvatarWithInitialsProps): ReactElement;
141
-
142
- /**
143
- * Joy UI RoleBadge — MUI Joy Chip with configurable color per role.
144
- */
145
- declare function RoleBadge({ role, colors }: RoleBadgeProps): ReactElement;
146
-
147
- /**
148
- * Joy UI ImpersonationBanner — sticky warning sheet when impersonating.
149
- */
150
- declare function ImpersonationBanner({ stopAction, }: ImpersonationBannerProps): ReactElement | null;
151
-
152
- declare function Layout({ children }: {
153
- children: ReactNode;
154
- }): react_jsx_runtime.JSX.Element;
155
- declare function EmailInput({ value, onChange, }: {
156
- value: string;
157
- onChange: (value: string) => void;
158
- error?: string;
159
- }): react_jsx_runtime.JSX.Element;
160
- declare function MagicLinkButton({ onClick, loading, }: {
161
- onClick: () => void;
162
- loading: boolean;
163
- }): react_jsx_runtime.JSX.Element;
164
- declare function PasskeyButton({ onClick, loading, }: {
165
- onClick: () => void;
166
- loading: boolean;
167
- }): react_jsx_runtime.JSX.Element;
168
- declare function SuccessMessage({ email }: {
169
- email: string;
170
- }): react_jsx_runtime.JSX.Element;
171
- declare function ErrorMessage({ error }: {
172
- error: string;
173
- }): react_jsx_runtime.JSX.Element;
174
- /**
175
- * Joy UI slot components for `<LoginPage>` from `@cfast/auth/client`.
176
- *
177
- * Usage:
178
- * ```tsx
179
- * import { LoginPage } from "@cfast/auth/client";
180
- * import { joyLoginComponents } from "@cfast/ui/joy";
181
- *
182
- * <LoginPage authClient={authClient} components={joyLoginComponents} />
183
- * ```
184
- *
185
- * Override individual slots while keeping the rest:
186
- * ```tsx
187
- * <LoginPage components={{ ...joyLoginComponents, Layout: MyLayout }} />
188
- * ```
189
- */
190
- declare const joyLoginComponents: {
191
- Layout: typeof Layout;
192
- EmailInput: typeof EmailInput;
193
- MagicLinkButton: typeof MagicLinkButton;
194
- PasskeyButton: typeof PasskeyButton;
195
- SuccessMessage: typeof SuccessMessage;
196
- ErrorMessage: typeof ErrorMessage;
197
- };
198
-
199
- export { ActionButton, AppShell, AppShellHeader, AppShellSidebar, AvatarWithInitials, BulkActionBar, ConfirmDialog, DataTable, DetailView, DropZone, EmptyState, FileList, FilterBar, FormStatus, ImagePreview, ImpersonationBanner, ListView, NavigationProgress, PageContainer, RoleBadge, ToastProvider, UserMenu, joyLoginComponents };