@almadar/ui 5.9.10 → 5.12.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.
@@ -56,6 +56,8 @@ export interface DataListProps<T extends EntityRow = EntityRow> extends DataDndP
56
56
  columns?: readonly DataListField[];
57
57
  /** Per-item action buttons */
58
58
  itemActions?: readonly DataListItemAction[];
59
+ /** Max inline action buttons before the rest collapse into a "⋯" overflow menu. Omit = all inline. */
60
+ maxInlineActions?: number;
59
61
  /** When set, the whole row is clickable and emits UI:{itemClickEvent} with
60
62
  * { id, row } (action-button clicks stopPropagation so they still win). */
61
63
  itemClickEvent?: EventKey;
@@ -125,7 +127,7 @@ export interface DataListProps<T extends EntityRow = EntityRow> extends DataDndP
125
127
  */
126
128
  look?: "dense" | "spacious" | "striped" | "borderless" | "card-rows";
127
129
  }
128
- export declare function DataList<T extends EntityRow = EntityRow>({ entity, fields, columns, itemActions, itemClickEvent, gap, variant, groupBy, senderField, currentUser, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, renderItem: schemaRenderItem, dragGroup, accepts, sortable: sortableProp, dropEvent, reorderEvent: dndReorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataListProps<T>): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined;
130
+ export declare function DataList<T extends EntityRow = EntityRow>({ entity, fields, columns, itemActions, maxInlineActions, itemClickEvent, gap, variant, groupBy, senderField, currentUser, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, renderItem: schemaRenderItem, dragGroup, accepts, sortable: sortableProp, dropEvent, reorderEvent: dndReorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataListProps<T>): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined;
129
131
  export declare namespace DataList {
130
132
  var displayName: string;
131
133
  }
@@ -59,6 +59,8 @@ export interface TableViewProps<T extends EntityRow = EntityRow> extends DataDnd
59
59
  fields?: readonly TableViewColumn[];
60
60
  /** Per-row action buttons (trailing column). */
61
61
  itemActions?: readonly TableViewItemAction[];
62
+ /** Max inline action buttons before the rest collapse into a "⋯" overflow menu. Omit = all inline. */
63
+ maxInlineActions?: number;
62
64
  /** Render a leading checkbox column. Selection changes emit `selectEvent`. */
63
65
  selectable?: boolean;
64
66
  /** Event emitted on selection change: UI:{selectEvent} with { ids, rows }. */
@@ -101,7 +103,7 @@ export interface TableViewProps<T extends EntityRow = EntityRow> extends DataDnd
101
103
  */
102
104
  look?: 'dense' | 'spacious' | 'striped' | 'borderless' | 'bordered';
103
105
  }
104
- export declare function TableView<T extends EntityRow = EntityRow>({ entity, columns, fields, itemActions, selectable, selectEvent, selectedIds, sortEvent, sortColumn, sortDirection, className, emptyMessage, isLoading, error, groupBy, pageSize, children, renderItem: _schemaRenderItem, look, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, }: TableViewProps<T>): import("react/jsx-runtime").JSX.Element;
106
+ export declare function TableView<T extends EntityRow = EntityRow>({ entity, columns, fields, itemActions, maxInlineActions, selectable, selectEvent, selectedIds, sortEvent, sortColumn, sortDirection, className, emptyMessage, isLoading, error, groupBy, pageSize, children, renderItem: _schemaRenderItem, look, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, }: TableViewProps<T>): import("react/jsx-runtime").JSX.Element;
105
107
  export declare namespace TableView {
106
108
  var displayName: string;
107
109
  }
@@ -23448,6 +23448,8 @@ function DataGrid({
23448
23448
  fields,
23449
23449
  columns,
23450
23450
  itemActions,
23451
+ maxInlineActions,
23452
+ scrollX = false,
23451
23453
  cols,
23452
23454
  gap = "md",
23453
23455
  minCardWidth = 280,
@@ -23587,8 +23589,8 @@ function DataGrid({
23587
23589
  /* @__PURE__ */ jsxRuntime.jsx(
23588
23590
  Box,
23589
23591
  {
23590
- className: cn("grid", gapStyles6[gap], colsClass, lookStyles5[look], className),
23591
- style: gridTemplateColumns ? { gridTemplateColumns } : void 0,
23592
+ className: cn("grid", gapStyles6[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles5[look], className),
23593
+ style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
23592
23594
  children: data.map((item, index) => {
23593
23595
  const itemData = item;
23594
23596
  const id = itemData.id || String(index);
@@ -23715,21 +23717,39 @@ function DataGrid({
23715
23717
  )
23716
23718
  ] }, field.name);
23717
23719
  }) }) }),
23718
- primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "sm", className: "justify-end", children: primaryActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
23719
- Button,
23720
- {
23721
- variant: action.variant === "primary" ? "primary" : "ghost",
23722
- size: "sm",
23723
- onClick: handleActionClick(action, itemData),
23724
- "data-testid": `action-${action.event}`,
23725
- "data-row-id": String(itemData.id),
23726
- children: [
23727
- action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
23728
- action.label
23729
- ]
23730
- },
23731
- idx
23732
- )) }) })
23720
+ primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end", children: [
23721
+ (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
23722
+ Button,
23723
+ {
23724
+ variant: action.variant === "primary" ? "primary" : "ghost",
23725
+ size: "sm",
23726
+ onClick: handleActionClick(action, itemData),
23727
+ "data-testid": `action-${action.event}`,
23728
+ "data-row-id": String(itemData.id),
23729
+ children: [
23730
+ action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
23731
+ action.label
23732
+ ]
23733
+ },
23734
+ idx
23735
+ )),
23736
+ maxInlineActions != null && primaryActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
23737
+ Menu,
23738
+ {
23739
+ position: "bottom-end",
23740
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": "More actions", "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
23741
+ items: primaryActions.slice(maxInlineActions).map((action) => ({
23742
+ label: action.label,
23743
+ icon: action.icon,
23744
+ event: action.event,
23745
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
23746
+ id: itemData.id,
23747
+ row: itemData
23748
+ })
23749
+ }))
23750
+ }
23751
+ )
23752
+ ] }) })
23733
23753
  ]
23734
23754
  },
23735
23755
  id
@@ -23779,6 +23799,7 @@ var init_DataGrid = __esm({
23779
23799
  init_Button();
23780
23800
  init_Icon();
23781
23801
  init_InfiniteScrollSentinel();
23802
+ init_Menu();
23782
23803
  init_useDataDnd();
23783
23804
  dataGridLog = logger.createLogger("almadar:ui:data-grid");
23784
23805
  BADGE_VARIANTS = /* @__PURE__ */ new Set([
@@ -23858,6 +23879,7 @@ function DataList({
23858
23879
  fields,
23859
23880
  columns,
23860
23881
  itemActions,
23882
+ maxInlineActions,
23861
23883
  itemClickEvent,
23862
23884
  gap = "none",
23863
23885
  variant = "default",
@@ -23948,6 +23970,46 @@ function DataList({
23948
23970
  };
23949
23971
  eventBus.emit(`UI:${action.event}`, payload);
23950
23972
  };
23973
+ const renderItemActions = (itemData) => {
23974
+ if (!itemActions || itemActions.length === 0) return null;
23975
+ const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
23976
+ const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
23977
+ return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "flex-shrink-0", children: [
23978
+ inline.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
23979
+ Button,
23980
+ {
23981
+ variant: action.variant ?? "ghost",
23982
+ size: "sm",
23983
+ onClick: handleActionClick(action, itemData),
23984
+ "data-testid": `action-${action.event}`,
23985
+ "data-row-id": String(itemData.id),
23986
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
23987
+ children: [
23988
+ action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
23989
+ action.label
23990
+ ]
23991
+ },
23992
+ idx
23993
+ )),
23994
+ overflow.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
23995
+ Menu,
23996
+ {
23997
+ position: "bottom-end",
23998
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": "More actions", "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
23999
+ items: overflow.map((action) => ({
24000
+ label: action.label,
24001
+ icon: action.icon,
24002
+ event: action.event,
24003
+ variant: action.variant === "danger" ? "danger" : "default",
24004
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
24005
+ id: itemData.id,
24006
+ row: itemData
24007
+ })
24008
+ }))
24009
+ }
24010
+ )
24011
+ ] });
24012
+ };
23951
24013
  const handleRowClick = (itemData) => () => {
23952
24014
  if (!itemClickEvent) return;
23953
24015
  const payload = {
@@ -24037,31 +24099,7 @@ function DataList({
24037
24099
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick: itemClickEvent ? handleRowClick(itemData) : void 0, className: cn(itemClickEvent && "cursor-pointer"), children: [
24038
24100
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "group flex items-stretch gap-2", children: [
24039
24101
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 min-w-0", children: children(itemData, index) }),
24040
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
24041
- HStack,
24042
- {
24043
- gap: "xs",
24044
- className: "flex-shrink-0",
24045
- children: itemActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
24046
- Button,
24047
- {
24048
- variant: action.variant ?? "ghost",
24049
- size: "sm",
24050
- onClick: handleActionClick(action, itemData),
24051
- "data-testid": `action-${action.event}`,
24052
- "data-row-id": String(itemData.id),
24053
- className: cn(
24054
- action.variant === "danger" && "text-error hover:bg-error/10"
24055
- ),
24056
- children: [
24057
- action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
24058
- action.label
24059
- ]
24060
- },
24061
- idx
24062
- ))
24063
- }
24064
- )
24102
+ renderItemActions(itemData)
24065
24103
  ] }),
24066
24104
  isCard && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-6 border-b border-border/40" })
24067
24105
  ] }, id2)
@@ -24132,24 +24170,7 @@ function DataList({
24132
24170
  ] }, field.name);
24133
24171
  })
24134
24172
  ] }),
24135
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: itemActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
24136
- Button,
24137
- {
24138
- variant: action.variant ?? "ghost",
24139
- size: "sm",
24140
- onClick: handleActionClick(action, itemData),
24141
- "data-testid": `action-${action.event}`,
24142
- "data-row-id": String(itemData.id),
24143
- className: cn(
24144
- action.variant === "danger" && "text-error hover:bg-error/10"
24145
- ),
24146
- children: [
24147
- action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
24148
- action.label
24149
- ]
24150
- },
24151
- idx
24152
- )) })
24173
+ renderItemActions(itemData)
24153
24174
  ]
24154
24175
  }
24155
24176
  ),
@@ -24219,6 +24240,7 @@ var init_DataList = __esm({
24219
24240
  init_ProgressBar();
24220
24241
  init_Divider();
24221
24242
  init_InfiniteScrollSentinel();
24243
+ init_Menu();
24222
24244
  init_useDataDnd();
24223
24245
  dataListLog = logger.createLogger("almadar:ui:data-list");
24224
24246
  listLookStyles = {
@@ -29328,6 +29350,7 @@ function TableView({
29328
29350
  columns,
29329
29351
  fields,
29330
29352
  itemActions,
29353
+ maxInlineActions,
29331
29354
  selectable = false,
29332
29355
  selectEvent,
29333
29356
  selectedIds,
@@ -29420,10 +29443,13 @@ function TableView({
29420
29443
  }
29421
29444
  const lk = LOOKS[look];
29422
29445
  const hasActions = Boolean(itemActions && itemActions.length > 0);
29446
+ const inlineActionCount = hasActions ? maxInlineActions != null ? Math.min(itemActions.length, maxInlineActions) : itemActions.length : 0;
29447
+ const hasOverflowActions = hasActions && maxInlineActions != null && itemActions.length > maxInlineActions;
29448
+ const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
29423
29449
  const gridTemplateColumns = [
29424
29450
  selectable ? "auto" : null,
29425
29451
  ...colDefs.map((c) => c.width ?? "minmax(0, 1fr)"),
29426
- hasActions ? "auto" : null
29452
+ actionsTrack
29427
29453
  ].filter(Boolean).join(" ");
29428
29454
  const header = /* @__PURE__ */ jsxRuntime.jsxs(
29429
29455
  Box,
@@ -29510,22 +29536,41 @@ function TableView({
29510
29536
  }
29511
29537
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
29512
29538
  }),
29513
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: itemActions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29514
- Button,
29515
- {
29516
- variant: action.variant ?? "ghost",
29517
- size: "sm",
29518
- onClick: handleActionClick(action, row),
29519
- "data-testid": `action-${action.event}`,
29520
- "data-row-id": String(row.id),
29521
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
29522
- children: [
29523
- action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
29524
- action.label
29525
- ]
29526
- },
29527
- i
29528
- )) })
29539
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
29540
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29541
+ Button,
29542
+ {
29543
+ variant: action.variant ?? "ghost",
29544
+ size: "sm",
29545
+ onClick: handleActionClick(action, row),
29546
+ "data-testid": `action-${action.event}`,
29547
+ "data-row-id": String(row.id),
29548
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
29549
+ children: [
29550
+ action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
29551
+ action.label
29552
+ ]
29553
+ },
29554
+ i
29555
+ )),
29556
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
29557
+ Menu,
29558
+ {
29559
+ position: "bottom-end",
29560
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": "More actions", "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
29561
+ items: itemActions.slice(maxInlineActions).map((action) => ({
29562
+ label: action.label,
29563
+ icon: action.icon,
29564
+ event: action.event,
29565
+ variant: action.variant === "danger" ? "danger" : "default",
29566
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
29567
+ id: row.id,
29568
+ row
29569
+ })
29570
+ }))
29571
+ }
29572
+ )
29573
+ ] })
29529
29574
  ]
29530
29575
  }
29531
29576
  );
@@ -29573,6 +29618,7 @@ var init_TableView = __esm({
29573
29618
  init_Icon();
29574
29619
  init_Checkbox();
29575
29620
  init_Divider();
29621
+ init_Menu();
29576
29622
  init_useDataDnd();
29577
29623
  logger.createLogger("almadar:ui:table-view");
29578
29624
  alignClass = {
@@ -29586,11 +29632,11 @@ var init_TableView = __esm({
29586
29632
  semibold: "font-semibold"
29587
29633
  };
29588
29634
  LOOKS = {
29589
- dense: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: true },
29590
- spacious: { rowPad: "px-5 py-4", headPad: "px-5 py-3", striped: false, divider: true },
29591
- striped: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: true, divider: false },
29592
- borderless: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: false },
29593
- bordered: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: true }
29635
+ dense: { rowPad: "px-card-md py-card-sm", headPad: "px-card-md py-card-sm", striped: false, divider: true },
29636
+ spacious: { rowPad: "px-card-lg py-card-md", headPad: "px-card-lg py-card-sm", striped: false, divider: true },
29637
+ striped: { rowPad: "px-card-md py-card-sm", headPad: "px-card-md py-card-sm", striped: true, divider: false },
29638
+ borderless: { rowPad: "px-card-md py-card-sm", headPad: "px-card-md py-card-sm", striped: false, divider: false },
29639
+ bordered: { rowPad: "px-card-md py-card-sm", headPad: "px-card-md py-card-sm", striped: false, divider: true }
29594
29640
  };
29595
29641
  TableView.displayName = "TableView";
29596
29642
  }