@efa-one/sdk 1.23.0 → 1.25.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/README.md +19 -0
- package/frontend/ui/Alert.d.ts +4 -1
- package/frontend/ui/Alert.js +13 -2
- package/frontend/ui/DataTable.d.ts +1 -0
- package/frontend/ui/DataTable.js +80 -36
- package/frontend/ui/Dialog.d.ts +4 -1
- package/frontend/ui/Dialog.js +6 -2
- package/frontend/ui/RecordDialog.d.ts +3 -1
- package/frontend/ui/RecordDialog.js +2 -2
- package/frontend/ui/index.d.ts +1 -1
- package/frontend/ui/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -262,6 +262,25 @@ Excel export for such data. No backend or kernel change.
|
|
|
262
262
|
permission lookups time out after 5 s. `isFromPlatformParent` also checks `event.origin`.
|
|
263
263
|
- Peer range `i18next-http-backend >=4.0.2`; DataTable bulk bar says „Auswahl aufheben".
|
|
264
264
|
|
|
265
|
+
**1.24.0 — wider dialogs.** No kernel or backend change.
|
|
266
|
+
- `Dialog` defaults to `size="3xl"` (768 px) instead of `md` (448 px), exported as
|
|
267
|
+
`DEFAULT_DIALOG_SIZE`; on small screens it keeps 1 rem margin on each side.
|
|
268
|
+
- `RecordDialog` takes `size` and uses the same default.
|
|
269
|
+
- After the bump every dialog without `size` is 768 px wide. Give short confirmations
|
|
270
|
+
("Wirklich löschen?") `size="sm"`.
|
|
271
|
+
|
|
272
|
+
**1.25.0 — dismissible banners, top scrollbar.** No kernel or backend change.
|
|
273
|
+
- Every `Alert` shows a close „×" top right by default. With `onDismiss` nothing
|
|
274
|
+
changes. Without it the banner hides itself and shows again when its text changes.
|
|
275
|
+
`dismissible={false}` removes the „×" for notices that must stay.
|
|
276
|
+
- Messages from app state (errors, results) need `onDismiss`: without it, a closed
|
|
277
|
+
banner stays hidden when the app sets the same text again in one handler or when
|
|
278
|
+
the content is JSX. After the bump, add `onDismiss` (and `onErrorDismiss` on
|
|
279
|
+
`RecordDialog`) wherever such banners lack it.
|
|
280
|
+
- `DataTable`: when the grid is wider than its container, a second horizontal
|
|
281
|
+
scrollbar sits above the table and scrolls in sync with the bottom one. Not on
|
|
282
|
+
mobile. The border moved to an outer wrapper.
|
|
283
|
+
|
|
265
284
|
**Next minor (planned 1.23.0, not yet released) — exchange, cookie and S2S hardening.**
|
|
266
285
|
Needs a kernel with efaware/efa_kernel#106 and #107 first (gateway exchange token
|
|
267
286
|
`gw: 'app'`, provenance `jti`, cookie filter). Then:
|
package/frontend/ui/Alert.d.ts
CHANGED
|
@@ -4,7 +4,10 @@ interface AlertProps {
|
|
|
4
4
|
variant?: AlertVariant;
|
|
5
5
|
children: React.ReactNode;
|
|
6
6
|
onDismiss?: () => void;
|
|
7
|
+
dismissible?: boolean;
|
|
7
8
|
className?: string;
|
|
8
9
|
}
|
|
9
|
-
export declare function
|
|
10
|
+
export declare function alertContentKey(children: React.ReactNode): string | null;
|
|
11
|
+
export declare function isAlertHidden(dismissedKey: string | null | undefined, contentKey: string | null): boolean;
|
|
12
|
+
export declare function Alert({ variant, children, onDismiss, dismissible, className }: AlertProps): React.JSX.Element | null;
|
|
10
13
|
export {};
|
package/frontend/ui/Alert.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { X } from 'lucide-react';
|
|
3
|
-
export function
|
|
4
|
-
return
|
|
4
|
+
export function alertContentKey(children) {
|
|
5
|
+
return typeof children === 'string' || typeof children === 'number' ? String(children) : null;
|
|
6
|
+
}
|
|
7
|
+
export function isAlertHidden(dismissedKey, contentKey) {
|
|
8
|
+
return dismissedKey !== undefined && dismissedKey === contentKey;
|
|
9
|
+
}
|
|
10
|
+
export function Alert({ variant = 'error', children, onDismiss, dismissible = true, className = '' }) {
|
|
11
|
+
const [dismissedKey, setDismissedKey] = useState(undefined);
|
|
12
|
+
const contentKey = alertContentKey(children);
|
|
13
|
+
if (!onDismiss && isAlertHidden(dismissedKey, contentKey))
|
|
14
|
+
return null;
|
|
15
|
+
return (_jsxs("div", { role: "alert", className: `shrink-0 flex items-start gap-2 border rounded-md px-4 py-3 text-sm font-medium alert-${variant} ${className}`, children: [_jsx("span", { className: "flex-1 min-w-0", children: children }), dismissible && (_jsx("button", { type: "button", onClick: onDismiss ?? (() => setDismissedKey(contentKey)), "aria-label": "Meldung schlie\u00DFen", className: "shrink-0 -mr-1 p-0.5 rounded hover:bg-current/10 transition-colors", children: _jsx(X, { className: "w-4 h-4" }) }))] }));
|
|
5
16
|
}
|
|
@@ -89,4 +89,5 @@ interface RenderItem<T> {
|
|
|
89
89
|
export declare function buildRenderItems<T>(rows: T[], groupBy: string[], columnsById: Map<string, ColumnDef<T>>, expandedGroups: Set<string> | 'all'): RenderItem<T>[];
|
|
90
90
|
export declare function collectAllGroupKeys<T>(rows: T[], groupBy: string[], columnsById: Map<string, ColumnDef<T>>): string[];
|
|
91
91
|
export declare function DataTable<T, K extends string | number>({ listId, rows, columns, rowKey, onRowClick, selection, initialPrefs, footer, groupsDefaultExpanded, persistence, exportable, exportFileName, }: DataTableProps<T, K>): React.ReactElement;
|
|
92
|
+
export declare function topScrollbarWidth(gridWidth: number, viewportWidth: number): number | null;
|
|
92
93
|
export {};
|
package/frontend/ui/DataTable.js
CHANGED
|
@@ -241,6 +241,7 @@ export function DataTable({ listId, rows, columns, rowKey, onRowClick, selection
|
|
|
241
241
|
const [dragColumnId, setDragColumnId] = useState(null);
|
|
242
242
|
const [dropTarget, setDropTarget] = useState(null);
|
|
243
243
|
const containerRef = useRef(null);
|
|
244
|
+
const headerRef = useRef(null);
|
|
244
245
|
const effectiveOrder = useMemo(() => reconcileOrder(columns, prefs.columnOrder), [columns, prefs.columnOrder]);
|
|
245
246
|
const seenGroupKeysRef = useRef(new Set());
|
|
246
247
|
useEffect(() => {
|
|
@@ -433,42 +434,85 @@ export function DataTable({ listId, rows, columns, rowKey, onRowClick, selection
|
|
|
433
434
|
hasColumns: visibleColumns.some((c) => c.exportable !== false),
|
|
434
435
|
run: runExport,
|
|
435
436
|
} : undefined, columns: columns, prefs: prefs, setColumnVisible: setColumnVisible, moveColumn: moveColumn, reset: reset, groupBy: groupBy, addGroupBy: addGroupBy, removeGroupBy: removeGroupBy, moveGroupBy: moveGroupBy, clearGroupBy: clearGroupBy }));
|
|
436
|
-
return (_jsxs("div", {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
437
|
+
return (_jsxs("div", { className: "border border-[var(--color-border)]", children: [_jsx(TopScrollbar, { targetRef: containerRef, measureRef: headerRef, enabled: !isMobile }), _jsxs("div", { ref: containerRef, className: "overflow-x-auto", style: { '--dt-cols': gridTemplate }, children: [selection && selection.selected.size > 0 && (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm border-b border-[var(--color-border)]", style: { background: 'var(--color-surface-raised)' }, children: [_jsxs("span", { children: [selection.selected.size, " ausgew\u00E4hlt"] }), _jsx("button", { type: "button", className: "text-xs underline text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]", onClick: () => selection.onChange(new Set()), children: "Auswahl aufheben" }), _jsx("div", { className: "flex-1" }), selection.bulkActions] })), isMobile ? (_jsxs("div", { className: "flex items-center gap-3 px-3 py-2 text-xs font-medium border-b border-[var(--color-border)]", style: { background: 'var(--color-surface-raised)' }, children: [selection && (_jsxs("label", { className: "flex items-center gap-2 py-2 pr-2 cursor-pointer", children: [_jsx("input", { type: "checkbox", className: "w-5 h-5", checked: allSelected, onChange: toggleAll, "aria-label": "Alle ausw\u00E4hlen" }), _jsx("span", { className: "text-[var(--color-text-muted)]", children: "Alle" })] })), _jsx("div", { className: "flex-1" }), gearButton] })) : (_jsxs("div", { ref: headerRef, className: "grid items-center min-w-min px-3 py-2 text-xs font-medium border-b border-[var(--color-border)]", style: { gridTemplateColumns: 'var(--dt-cols)', background: 'var(--color-surface-raised)' }, children: [selection && (_jsx("input", { type: "checkbox", checked: allSelected, onChange: toggleAll, "aria-label": "Alle ausw\u00E4hlen" })), visibleColumns.map((col) => {
|
|
438
|
+
const activeSort = prefs.sort?.columnId === col.id ? prefs.sort.direction : null;
|
|
439
|
+
const filterValue = prefs.filters[col.id];
|
|
440
|
+
const isFiltered = filterValue != null;
|
|
441
|
+
return (_jsx(HeaderCell, { columnId: col.id, label: col.label, dragColumnId: dragColumnId, dropSide: dropTarget?.id === col.id ? dropTarget.side : null, onDragStart: () => setDragColumnId(col.id), onDragEnd: () => { setDragColumnId(null); setDropTarget(null); }, onDragOverSide: (side) => {
|
|
442
|
+
if (dropTarget?.id !== col.id || dropTarget.side !== side)
|
|
443
|
+
setDropTarget({ id: col.id, side });
|
|
444
|
+
}, onDrop: (sourceId, side) => {
|
|
445
|
+
dropColumn(sourceId, col.id, side);
|
|
446
|
+
setDragColumnId(null);
|
|
447
|
+
setDropTarget(null);
|
|
448
|
+
}, onResizePreview: (px) => previewColumnWidth(col.id, px), onResizeCommit: (px) => setColumnWidth(col.id, px), onResizeCancel: () => containerRef.current?.style.setProperty('--dt-cols', gridTemplate), hasCustomWidth: columnWidths[col.id] != null, children: _jsxs(DropdownMenu.Root, { children: [_jsx(DropdownMenu.Trigger, { asChild: true, children: _jsxs("button", { type: "button", className: "flex items-center gap-1 min-w-0 px-1 py-0.5 hover:bg-[var(--color-surface)] text-left", style: { borderRadius: 'var(--border-radius-md)' }, children: [_jsx("span", { className: "min-w-0 truncate", children: col.label }), activeSort === 'asc' && _jsx(ChevronUp, { className: "w-3 h-3" }), activeSort === 'desc' && _jsx(ChevronDown, { className: "w-3 h-3" }), isFiltered && _jsx(Filter, { className: "w-3 h-3 signal-text-primary" })] }) }), _jsxs(DropdownMenu.Content, { className: "min-w-[220px] p-1", children: [col.sortable !== false && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenu.Item, { onSelect: () => setSort(col.id, 'asc'), children: [_jsx(ArrowUp, { className: "w-4 h-4" }), "Sortieren A\u2192Z"] }), _jsxs(DropdownMenu.Item, { onSelect: () => setSort(col.id, 'desc'), children: [_jsx(ArrowDown, { className: "w-4 h-4" }), "Sortieren Z\u2192A"] }), activeSort && (_jsx(DropdownMenu.Item, { onSelect: clearSort, children: "Sortierung zur\u00FCcksetzen" })), _jsx(DropdownMenu.Separator, {})] })), groupBy.includes(col.id) ? (_jsxs(DropdownMenu.Item, { onSelect: () => removeGroupBy(col.id), children: [_jsx(Layers, { className: "w-4 h-4" }), "Gruppierung aufheben"] })) : (_jsxs(DropdownMenu.Item, { onSelect: () => addGroupBy(col.id), children: [_jsx(Layers, { className: "w-4 h-4" }), "Nach dieser Spalte gruppieren"] })), _jsx(DropdownMenu.Separator, {}), col.filter && (_jsx(FilterEditor, { column: col, value: filterValue, onChange: (v) => setFilter(col.id, v) })), columnWidths[col.id] != null && (_jsxs(DropdownMenu.Item, { onSelect: () => setColumnWidth(col.id, null), children: [_jsx(RotateCcw, { className: "w-4 h-4" }), "Standardbreite"] })), _jsxs(DropdownMenu.Item, { onSelect: () => setColumnVisible(col.id, false), variant: "danger", children: [_jsx(EyeOff, { className: "w-4 h-4" }), "Spalte ausblenden"] })] })] }) }, col.id));
|
|
449
|
+
}), gearButton] })), sortedFilteredRows.length === 0 ? (_jsx("div", { className: "px-3 py-8 text-center text-sm text-[var(--color-text-muted)]", children: "Keine Eintr\u00E4ge" })) : (buildRenderItems(sortedFilteredRows, groupBy, columnsById, expandedGroups).map((item, idx) => {
|
|
450
|
+
if (item.kind === 'group') {
|
|
451
|
+
const level = item.level ?? 0;
|
|
452
|
+
const groupColLabel = columnsById.get(groupBy[level])?.label ?? '';
|
|
453
|
+
return (_jsx("div", { className: "px-3 py-1.5 text-xs border-b border-[var(--color-border)] cursor-pointer select-none hover:bg-[var(--color-surface-raised)]", style: {
|
|
454
|
+
background: 'var(--color-surface-raised)',
|
|
455
|
+
paddingLeft: 12 + level * 18,
|
|
456
|
+
}, onClick: () => toggleGroup(item.key), title: item.expanded ? 'Gruppe einklappen' : 'Gruppe ausklappen', children: _jsxs("div", { className: "flex items-center gap-2", children: [item.expanded
|
|
457
|
+
? _jsx(ChevronDown, { className: "w-3.5 h-3.5 text-[var(--color-text-muted)]" })
|
|
458
|
+
: _jsx(ChevronRight, { className: "w-3.5 h-3.5 text-[var(--color-text-muted)]" }), _jsx("span", { className: "text-[var(--color-text-muted)] uppercase text-[10px]", children: groupColLabel }), _jsx("span", { className: "font-medium text-[var(--color-text-primary)]", children: item.label }), _jsxs("span", { className: "text-[var(--color-text-muted)]", children: ["(", item.count, ")"] })] }) }, `g:${item.key}`));
|
|
459
|
+
}
|
|
460
|
+
const row = item.row;
|
|
461
|
+
const key = rowKey(row);
|
|
462
|
+
const isChecked = selection?.selected.has(key) ?? false;
|
|
463
|
+
const rowIndent = groupBy.length > 0 ? 12 + groupBy.length * 18 : undefined;
|
|
464
|
+
if (isMobile) {
|
|
465
|
+
return (_jsxs("div", { className: `flex flex-col gap-1.5 px-3 py-3 border-b border-[var(--color-border)] ${onRowClick ? 'cursor-pointer hover:bg-[var(--color-surface-raised)]' : ''}`, style: { background: isChecked ? 'rgba(99,102,241,0.07)' : undefined, paddingLeft: rowIndent }, onClick: () => onRowClick?.(row), children: [selection && (_jsxs("label", { className: "flex items-center gap-2 self-start py-1 pr-2 cursor-pointer", onClick: (e) => e.stopPropagation(), children: [_jsx("input", { type: "checkbox", className: "w-5 h-5", checked: isChecked, onChange: () => toggleOne(key), "aria-label": "Zeile ausw\u00E4hlen" }), _jsx("span", { className: "text-xs text-[var(--color-text-muted)]", children: "Ausw\u00E4hlen" })] })), visibleColumns.map((col) => (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: col.label }), _jsx("div", { className: "min-w-0 break-words text-sm", children: col.cell ? col.cell(row) : formatCellValue(col, row) })] }, col.id)))] }, `r:${String(key)}:${idx}`));
|
|
466
|
+
}
|
|
467
|
+
return (_jsxs("div", { className: `grid items-center min-w-min px-3 py-2 text-sm border-b border-[var(--color-border)] ${onRowClick ? 'cursor-pointer hover:bg-[var(--color-surface-raised)]' : ''}`, style: {
|
|
468
|
+
gridTemplateColumns: 'var(--dt-cols)',
|
|
469
|
+
background: isChecked ? 'rgba(99,102,241,0.07)' : undefined,
|
|
470
|
+
paddingLeft: rowIndent,
|
|
471
|
+
}, onClick: () => onRowClick?.(row), children: [selection && (_jsx("input", { type: "checkbox", checked: isChecked, onChange: () => toggleOne(key), onClick: (e) => e.stopPropagation(), "aria-label": "Zeile ausw\u00E4hlen" })), visibleColumns.map((col) => (_jsx("div", { className: "min-w-0 truncate", children: col.cell ? col.cell(row) : formatCellValue(col, row) }, col.id))), _jsx("div", {})] }, `r:${String(key)}:${idx}`));
|
|
472
|
+
})), footer && (_jsx("div", { className: "px-3 py-2 text-xs text-[var(--color-text-muted)] border-t border-[var(--color-border)]", children: footer }))] })] }));
|
|
473
|
+
}
|
|
474
|
+
export function topScrollbarWidth(gridWidth, viewportWidth) {
|
|
475
|
+
return gridWidth > viewportWidth + 1 ? gridWidth : null;
|
|
476
|
+
}
|
|
477
|
+
function syncScroll(from, to) {
|
|
478
|
+
if (Math.abs(to.scrollLeft - from.scrollLeft) > 1)
|
|
479
|
+
to.scrollLeft = from.scrollLeft;
|
|
480
|
+
}
|
|
481
|
+
function TopScrollbar({ targetRef, measureRef, enabled }) {
|
|
482
|
+
const barRef = useRef(null);
|
|
483
|
+
const [width, setWidth] = useState(null);
|
|
484
|
+
useEffect(() => {
|
|
485
|
+
const target = targetRef.current;
|
|
486
|
+
const measured = measureRef.current;
|
|
487
|
+
if (!enabled || !target || !measured || typeof ResizeObserver === 'undefined') {
|
|
488
|
+
setWidth(null);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const update = () => setWidth(topScrollbarWidth(target.scrollWidth, target.clientWidth));
|
|
492
|
+
const observer = new ResizeObserver(update);
|
|
493
|
+
observer.observe(target);
|
|
494
|
+
observer.observe(measured);
|
|
495
|
+
update();
|
|
496
|
+
const follow = () => {
|
|
497
|
+
if (barRef.current)
|
|
498
|
+
syncScroll(target, barRef.current);
|
|
499
|
+
};
|
|
500
|
+
target.addEventListener('scroll', follow, { passive: true });
|
|
501
|
+
return () => {
|
|
502
|
+
observer.disconnect();
|
|
503
|
+
target.removeEventListener('scroll', follow);
|
|
504
|
+
};
|
|
505
|
+
}, [enabled, targetRef, measureRef]);
|
|
506
|
+
useEffect(() => {
|
|
507
|
+
if (barRef.current && targetRef.current)
|
|
508
|
+
barRef.current.scrollLeft = targetRef.current.scrollLeft;
|
|
509
|
+
}, [width, targetRef]);
|
|
510
|
+
if (width == null)
|
|
511
|
+
return null;
|
|
512
|
+
return (_jsx("div", { ref: barRef, "aria-hidden": "true", tabIndex: -1, style: { overflowX: 'auto', overflowY: 'hidden', minHeight: 12 }, onScroll: () => {
|
|
513
|
+
if (targetRef.current && barRef.current)
|
|
514
|
+
syncScroll(barRef.current, targetRef.current);
|
|
515
|
+
}, children: _jsx("div", { style: { width, height: 1 } }) }));
|
|
472
516
|
}
|
|
473
517
|
const DRAG_MIME = 'application/x-efa-datatable-column';
|
|
474
518
|
function HeaderCell({ columnId, label, dragColumnId, dropSide, hasCustomWidth, onDragStart, onDragEnd, onDragOverSide, onDrop, onResizePreview, onResizeCommit, onResizeCancel, children, }) {
|
package/frontend/ui/Dialog.d.ts
CHANGED
|
@@ -9,8 +9,11 @@ interface DialogProps {
|
|
|
9
9
|
children: React.ReactNode;
|
|
10
10
|
banner?: React.ReactNode;
|
|
11
11
|
footer?: React.ReactNode;
|
|
12
|
-
size?:
|
|
12
|
+
size?: DialogSize;
|
|
13
13
|
}
|
|
14
|
+
export type DialogSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl';
|
|
15
|
+
export declare const DEFAULT_DIALOG_SIZE: DialogSize;
|
|
16
|
+
export declare function dialogWidthClass(size?: DialogSize): string;
|
|
14
17
|
export declare function Dialog({ open, onOpenChange, trigger, title, description, children, banner, footer, size }: DialogProps): React.JSX.Element;
|
|
15
18
|
export declare const DialogClose: React.ForwardRefExoticComponent<RadixDialog.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
|
|
16
19
|
export {};
|
package/frontend/ui/Dialog.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
3
3
|
import { X } from 'lucide-react';
|
|
4
|
+
export const DEFAULT_DIALOG_SIZE = '3xl';
|
|
4
5
|
const SIZE_CLASS = {
|
|
5
6
|
sm: 'max-w-sm', md: 'max-w-md', lg: 'max-w-lg',
|
|
6
7
|
xl: 'max-w-xl', '2xl': 'max-w-2xl', '3xl': 'max-w-3xl',
|
|
7
8
|
'4xl': 'max-w-4xl', '5xl': 'max-w-5xl',
|
|
8
9
|
};
|
|
9
|
-
export function
|
|
10
|
-
return
|
|
10
|
+
export function dialogWidthClass(size = DEFAULT_DIALOG_SIZE) {
|
|
11
|
+
return `w-[calc(100%-2rem)] ${SIZE_CLASS[size]}`;
|
|
12
|
+
}
|
|
13
|
+
export function Dialog({ open, onOpenChange, trigger, title, description, children, banner, footer, size = DEFAULT_DIALOG_SIZE }) {
|
|
14
|
+
return (_jsxs(RadixDialog.Root, { open: open, onOpenChange: onOpenChange, children: [trigger && _jsx(RadixDialog.Trigger, { asChild: true, children: trigger }), _jsxs(RadixDialog.Portal, { children: [_jsx(RadixDialog.Overlay, { className: "fixed inset-0 bg-black/40 z-40 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" }), _jsxs(RadixDialog.Content, { className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 ${dialogWidthClass(size)} flex flex-col max-h-[90vh] rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] shadow-lg focus:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95`, children: [_jsxs("div", { className: "shrink-0 flex items-start justify-between p-5 border-b border-[var(--color-border)]", children: [_jsxs("div", { children: [_jsx(RadixDialog.Title, { className: "text-base font-semibold text-[var(--color-text-primary)]", children: title }), description && (_jsx(RadixDialog.Description, { className: "text-sm text-[var(--color-text-muted)] mt-0.5", children: description }))] }), _jsx(RadixDialog.Close, { className: "ml-4 p-1 rounded-full text-[var(--color-text-muted)] hover:bg-[var(--color-surface-raised)] hover:text-[var(--color-text-primary)] transition-colors", children: _jsx(X, { className: "w-4 h-4" }) })] }), banner && _jsx("div", { className: "shrink-0 px-5 pt-5", children: banner }), _jsx("div", { className: "min-h-0 overflow-y-auto p-5", children: children }), footer && (_jsx("div", { className: "shrink-0 flex items-center justify-end gap-2 px-5 pb-5", children: footer }))] })] })] }));
|
|
11
15
|
}
|
|
12
16
|
export const DialogClose = RadixDialog.Close;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { type DialogSize } from './Dialog.js';
|
|
2
3
|
export type RecordDialogMode = 'read' | 'edit' | 'create';
|
|
3
4
|
interface RecordDialogProps {
|
|
4
5
|
open: {
|
|
@@ -19,6 +20,7 @@ interface RecordDialogProps {
|
|
|
19
20
|
saveDisabled?: boolean;
|
|
20
21
|
error?: string;
|
|
21
22
|
onErrorDismiss?: () => void;
|
|
23
|
+
size?: DialogSize;
|
|
22
24
|
labels?: {
|
|
23
25
|
close?: string;
|
|
24
26
|
cancel?: string;
|
|
@@ -27,5 +29,5 @@ interface RecordDialogProps {
|
|
|
27
29
|
edit?: string;
|
|
28
30
|
};
|
|
29
31
|
}
|
|
30
|
-
export declare function RecordDialog({ open, onOpenChange, mode: controlledMode, onModeChange, title, readContent, editContent, onSave, saving, saveDisabled, error, onErrorDismiss, labels, }: RecordDialogProps): React.JSX.Element;
|
|
32
|
+
export declare function RecordDialog({ open, onOpenChange, mode: controlledMode, onModeChange, title, readContent, editContent, onSave, saving, saveDisabled, error, onErrorDismiss, size, labels, }: RecordDialogProps): React.JSX.Element;
|
|
31
33
|
export {};
|
|
@@ -4,7 +4,7 @@ import { Pencil } from 'lucide-react';
|
|
|
4
4
|
import { Dialog, DialogClose } from './Dialog.js';
|
|
5
5
|
import { Button } from './Button.js';
|
|
6
6
|
import { Alert } from './Alert.js';
|
|
7
|
-
export function RecordDialog({ open, onOpenChange, mode: controlledMode, onModeChange, title, readContent, editContent, onSave, saving, saveDisabled, error, onErrorDismiss, labels, }) {
|
|
7
|
+
export function RecordDialog({ open, onOpenChange, mode: controlledMode, onModeChange, title, readContent, editContent, onSave, saving, saveDisabled, error, onErrorDismiss, size, labels, }) {
|
|
8
8
|
const [internalMode, setInternalMode] = useState('read');
|
|
9
9
|
const mode = controlledMode ?? internalMode;
|
|
10
10
|
useEffect(() => {
|
|
@@ -34,7 +34,7 @@ export function RecordDialog({ open, onOpenChange, mode: controlledMode, onModeC
|
|
|
34
34
|
const createLabel = labels?.create ?? 'Erstellen';
|
|
35
35
|
const editLabel = labels?.edit ?? 'Bearbeiten';
|
|
36
36
|
return (_jsx(Dialog, { open: open !== null, onOpenChange: (o) => { if (!o)
|
|
37
|
-
handleClose(); }, title: title, banner: mode !== 'read' && error
|
|
37
|
+
handleClose(); }, title: title, size: size, banner: mode !== 'read' && error
|
|
38
38
|
? _jsx(Alert, { variant: "error", onDismiss: onErrorDismiss, children: error })
|
|
39
39
|
: undefined, footer: mode === 'read' ? (_jsxs(_Fragment, { children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "secondary", children: closeLabel }) }), _jsxs(Button, { onClick: () => setMode('edit'), children: [_jsx(Pencil, { className: "w-4 h-4" }), editLabel] })] })) : (_jsxs(_Fragment, { children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "secondary", children: cancelLabel }) }), _jsx(Button, { onClick: handleSave, loading: !!saving, disabled: !!saveDisabled, children: mode === 'create' ? createLabel : saveLabel })] })), children: mode === 'read' ? (_jsxs("div", { children: [_jsx("div", { className: "flex justify-end mb-2 -mt-1", children: _jsx("button", { onClick: () => setMode('edit'), className: "p-1.5 rounded-md text-[var(--color-text-muted)] hover:text-[var(--color-primary-text)] hover:bg-[var(--color-surface-raised)] transition-colors", title: editLabel, "aria-label": editLabel, children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), readContent] })) : (_jsx("div", { className: "flex flex-col gap-4", children: editContent })) }));
|
|
40
40
|
}
|
package/frontend/ui/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { Button } from './Button.js';
|
|
|
2
2
|
export { Input } from './Input.js';
|
|
3
3
|
export { Badge } from './Badge.js';
|
|
4
4
|
export { Alert, type AlertVariant } from './Alert.js';
|
|
5
|
-
export { Dialog, DialogClose } from './Dialog.js';
|
|
5
|
+
export { Dialog, DialogClose, DEFAULT_DIALOG_SIZE, type DialogSize } from './Dialog.js';
|
|
6
6
|
export * as DropdownMenu from './DropdownMenu.js';
|
|
7
7
|
export { Tooltip, TooltipProvider } from './Tooltip.js';
|
|
8
8
|
export { EmptyState } from './EmptyState.js';
|
package/frontend/ui/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { Button } from './Button.js';
|
|
|
2
2
|
export { Input } from './Input.js';
|
|
3
3
|
export { Badge } from './Badge.js';
|
|
4
4
|
export { Alert } from './Alert.js';
|
|
5
|
-
export { Dialog, DialogClose } from './Dialog.js';
|
|
5
|
+
export { Dialog, DialogClose, DEFAULT_DIALOG_SIZE } from './Dialog.js';
|
|
6
6
|
export * as DropdownMenu from './DropdownMenu.js';
|
|
7
7
|
export { Tooltip, TooltipProvider } from './Tooltip.js';
|
|
8
8
|
export { EmptyState } from './EmptyState.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@efa-one/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "Offizielles SDK für efa-one-Apps: Auth/Session, Service-Discovery, Gateway-Client, Audit/Reporting/Mail/Notifications, Permission- und Capability-Registrierung sowie das Frontend-IPC-/i18n-Toolkit.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|