@open-mercato/ui 0.7.1-develop.7193.1.910a5b0a1e → 0.7.1-develop.7195.1.b5efd1d51a

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 (49) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/backend/AppShell.js +66 -7
  3. package/dist/backend/AppShell.js.map +2 -2
  4. package/dist/backend/CrudForm.js +14 -8
  5. package/dist/backend/CrudForm.js.map +2 -2
  6. package/dist/backend/DataTable.js +108 -11
  7. package/dist/backend/DataTable.js.map +2 -2
  8. package/dist/backend/forms/ActionsDropdown.js +35 -5
  9. package/dist/backend/forms/ActionsDropdown.js.map +2 -2
  10. package/dist/backend/forms/index.js.map +2 -2
  11. package/dist/backend/icons/lucideRegistry.generated.js +6 -0
  12. package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
  13. package/dist/backend/injection/spotIds.js +2 -0
  14. package/dist/backend/injection/spotIds.js.map +2 -2
  15. package/dist/backend/inputs/DurationInput.js +182 -0
  16. package/dist/backend/inputs/DurationInput.js.map +7 -0
  17. package/dist/backend/inputs/EventSelect.js.map +2 -2
  18. package/dist/backend/inputs/index.js +1 -0
  19. package/dist/backend/inputs/index.js.map +2 -2
  20. package/dist/backend/nav/navBadges.js +38 -0
  21. package/dist/backend/nav/navBadges.js.map +7 -0
  22. package/dist/primitives/segmented-control.js +4 -4
  23. package/dist/primitives/segmented-control.js.map +1 -1
  24. package/dist/primitives/sheet.js +21 -11
  25. package/dist/primitives/sheet.js.map +2 -2
  26. package/dist/primitives/textarea.js +54 -4
  27. package/dist/primitives/textarea.js.map +2 -2
  28. package/package.json +3 -3
  29. package/src/backend/AppShell.tsx +106 -2
  30. package/src/backend/CrudForm.tsx +28 -9
  31. package/src/backend/DataTable.tsx +176 -22
  32. package/src/backend/__tests__/CrudForm.density.test.tsx +113 -0
  33. package/src/backend/__tests__/DataTable.propBulkActions.test.tsx +200 -0
  34. package/src/backend/__tests__/DataTable.render.test.tsx +76 -0
  35. package/src/backend/__tests__/navBadges.test.ts +57 -0
  36. package/src/backend/forms/ActionsDropdown.tsx +73 -6
  37. package/src/backend/forms/index.ts +1 -1
  38. package/src/backend/icons/lucideRegistry.generated.tsx +6 -0
  39. package/src/backend/injection/spotIds.ts +8 -0
  40. package/src/backend/inputs/DurationInput.tsx +207 -0
  41. package/src/backend/inputs/EventSelect.tsx +2 -0
  42. package/src/backend/inputs/__tests__/DurationInput.test.tsx +220 -0
  43. package/src/backend/inputs/index.ts +1 -0
  44. package/src/backend/nav/navBadges.ts +61 -0
  45. package/src/primitives/__tests__/segmented-control.test.tsx +2 -2
  46. package/src/primitives/__tests__/textarea.test.tsx +50 -0
  47. package/src/primitives/segmented-control.tsx +7 -7
  48. package/src/primitives/sheet.tsx +7 -1
  49. package/src/primitives/textarea.tsx +77 -4
@@ -3,11 +3,12 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { useRouter } from "next/navigation";
5
5
  import { flexRender } from "@tanstack/react-table";
6
- import { useLegacyTable, getCoreRowModel, getSortedRowModel } from "@tanstack/react-table/legacy";
6
+ import { useLegacyTable, getCoreRowModel, getSortedRowModel, getExpandedRowModel } from "@tanstack/react-table/legacy";
7
7
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
8
- import { RefreshCw, Loader2, SlidersHorizontal, MoreHorizontal, Circle, Filter, ChevronUp, ChevronDown, ChevronsUpDown, Check, Inbox, Save } from "lucide-react";
8
+ import { RefreshCw, Loader2, SlidersHorizontal, MoreHorizontal, Circle, Filter, ChevronUp, ChevronDown, ChevronRight, ChevronsUpDown, Check, Inbox, Save } from "lucide-react";
9
9
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../primitives/table.js";
10
10
  import { Button } from "../primitives/button.js";
11
+ import { IconButton } from "../primitives/icon-button.js";
11
12
  import { Checkbox } from "../primitives/checkbox.js";
12
13
  import {
13
14
  Select,
@@ -789,7 +790,11 @@ function DataTable({
789
790
  advancedFilter: advancedFilterInput,
790
791
  columnChooser,
791
792
  activeFilterChips,
792
- filterAwareEmptyState
793
+ filterAwareEmptyState,
794
+ getSubRows,
795
+ expandable,
796
+ expanded: expandedProp,
797
+ onExpandedChange
793
798
  }) {
794
799
  const t = useT();
795
800
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
@@ -835,6 +840,7 @@ function DataTable({
835
840
  const queryClient = useQueryClient();
836
841
  const perspectiveConfig = perspective ?? null;
837
842
  const perspectiveTableId = perspectiveConfig?.tableId ?? null;
843
+ const perspectiveAlign = perspectiveConfig?.align ?? "left";
838
844
  const perspectiveEnabled = Boolean(perspectiveTableId);
839
845
  const initialSnapshotRef = React.useRef(null);
840
846
  const snapshotHydratedTableRef = React.useRef(null);
@@ -1192,14 +1198,34 @@ function DataTable({
1192
1198
  const [rowSelection, setRowSelection] = React.useState({});
1193
1199
  const selectionScopeKeyRef = React.useRef(selectionScopeKey);
1194
1200
  const enableClientSorting = sortable && !manualSorting;
1201
+ const expansionEnabled = getSubRows !== void 0 || expandable !== void 0;
1202
+ const [internalExpanded, setInternalExpanded] = React.useState({});
1203
+ const expandedState = expandedProp !== void 0 ? expandedProp : internalExpanded;
1204
+ const handleExpandedChange = React.useCallback((updater) => {
1205
+ if (onExpandedChange) {
1206
+ onExpandedChange(updater);
1207
+ return;
1208
+ }
1209
+ setInternalExpanded((prev) => typeof updater === "function" ? updater(prev) : updater);
1210
+ }, [onExpandedChange]);
1195
1211
  const table = useLegacyTable({
1196
1212
  data: clientFilteredData,
1197
1213
  columns: mergedColumns,
1198
1214
  getCoreRowModel: getCoreRowModel(),
1199
1215
  ...enableClientSorting ? { getSortedRowModel: getSortedRowModel() } : {},
1216
+ ...expansionEnabled ? {
1217
+ getSubRows,
1218
+ getExpandedRowModel: getExpandedRowModel(),
1219
+ getRowCanExpand: (row) => {
1220
+ if (typeof expandable === "function") return expandable(row.original);
1221
+ if (expandable === true) return true;
1222
+ return (row.subRows?.length ?? 0) > 0;
1223
+ },
1224
+ onExpandedChange: handleExpandedChange
1225
+ } : {},
1200
1226
  manualSorting: manualSorting === true,
1201
1227
  getRowId: resolveDataTableRowId,
1202
- state: { sorting, columnVisibility, columnOrder, rowSelection },
1228
+ state: { sorting, columnVisibility, columnOrder, rowSelection, ...expansionEnabled ? { expanded: expandedState } : {} },
1203
1229
  enableRowSelection: hasInjectedBulkActions,
1204
1230
  onSortingChange: (updater) => {
1205
1231
  const next = typeof updater === "function" ? updater(sorting) : updater;
@@ -2200,12 +2226,44 @@ function DataTable({
2200
2226
  );
2201
2227
  const runPropBulkAction = React.useCallback(
2202
2228
  async (action) => {
2203
- const result = await action.onExecute(selectedRows);
2204
- if (result !== false) {
2229
+ try {
2230
+ const result = await action.onExecute(selectedRows);
2231
+ if (result === false) return;
2232
+ if (!result || typeof result !== "object") {
2233
+ setRowSelection({});
2234
+ return;
2235
+ }
2236
+ if (result.ok === false) {
2237
+ if (result.message === void 0) return;
2238
+ flash(result.message, "error");
2239
+ return;
2240
+ }
2205
2241
  setRowSelection({});
2242
+ if (result.progressJobId) {
2243
+ trackedBulkProgressJobIdsRef.current.add(result.progressJobId);
2244
+ flash(
2245
+ result.message ?? t("ui.dataTable.bulkAction.started", "Bulk action started. Track progress in the top bar."),
2246
+ "success"
2247
+ );
2248
+ return;
2249
+ }
2250
+ flash(
2251
+ result.message ?? t("ui.dataTable.bulkAction.success", "Bulk action completed."),
2252
+ "success"
2253
+ );
2254
+ if (refreshButton?.onRefresh) {
2255
+ refreshButton.onRefresh();
2256
+ } else {
2257
+ scheduleRouterRefresh(router);
2258
+ }
2259
+ } catch (error2) {
2260
+ flash(
2261
+ error2 instanceof Error ? error2.message : t("ui.dataTable.bulkAction.error", "Bulk action failed."),
2262
+ "error"
2263
+ );
2206
2264
  }
2207
2265
  },
2208
- [selectedRows]
2266
+ [refreshButton, router, selectedRows, t]
2209
2267
  );
2210
2268
  const builtToolbar = React.useMemo(() => {
2211
2269
  if (toolbar) return toolbar;
@@ -2286,6 +2344,7 @@ function DataTable({
2286
2344
  }
2287
2345
  )
2288
2346
  ] }) : null;
2347
+ const perspectiveButtonLeading = perspectiveAlign === "right" ? null : perspectiveButton;
2289
2348
  const saveViewButton = showSaveViewButton && canUsePerspectives ? /* @__PURE__ */ jsxs(
2290
2349
  Button,
2291
2350
  {
@@ -2305,9 +2364,9 @@ function DataTable({
2305
2364
  ]
2306
2365
  }
2307
2366
  ) : null;
2308
- const leadingItems = advancedFilterButton || perspectiveButton || saveViewButton ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2367
+ const leadingItems = advancedFilterButton || perspectiveButtonLeading || saveViewButton ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2309
2368
  advancedFilterButton,
2310
- perspectiveButton,
2369
+ perspectiveButtonLeading,
2311
2370
  saveViewButton
2312
2371
  ] }) : null;
2313
2372
  const trailingItems = hasBulkButtons ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
@@ -2351,6 +2410,7 @@ function DataTable({
2351
2410
  }) : null
2352
2411
  ] }) : null;
2353
2412
  const searchTrailingNode = searchTrailingInjectionSpotId && onSearchChange ? /* @__PURE__ */ jsx(InjectionSpot, { spotId: searchTrailingInjectionSpotId, context: resolvedInjectionContext }) : null;
2413
+ if (perspectiveAlign === "right" && !anySearch && combined.length === 0 && !advancedFilterButton && !saveViewButton && !trailingItems && !fieldsetSelector) return null;
2354
2414
  return /* @__PURE__ */ jsx(
2355
2415
  FilterBar,
2356
2416
  {
@@ -2383,6 +2443,7 @@ function DataTable({
2383
2443
  onFiltersApply,
2384
2444
  onFiltersClear,
2385
2445
  canUsePerspectives,
2446
+ perspectiveAlign,
2386
2447
  embedded,
2387
2448
  supportsCustomFieldFilterFieldsets,
2388
2449
  resolvedEntityIds,
@@ -2478,7 +2539,18 @@ function DataTable({
2478
2539
  ]
2479
2540
  }
2480
2541
  ) : null,
2481
- canUsePerspectives ? /* @__PURE__ */ jsxs(
2542
+ canUsePerspectives ? perspectiveAlign === "right" ? /* @__PURE__ */ jsx(
2543
+ ViewSwitcherDropdown,
2544
+ {
2545
+ activePerspectiveId,
2546
+ perspectives: perspectiveData?.perspectives ?? [],
2547
+ rolePerspectives: perspectiveData?.rolePerspectives ?? [],
2548
+ onClear: () => applyPerspectiveSettings({}, null),
2549
+ onActivate: handlePerspectiveActivate,
2550
+ onOpenSidebar: () => setPerspectiveOpen(true),
2551
+ t
2552
+ }
2553
+ ) : /* @__PURE__ */ jsxs(
2482
2554
  Button,
2483
2555
  {
2484
2556
  type: "button",
@@ -2679,12 +2751,37 @@ function DataTable({
2679
2751
  const sizedWidth = enableColumnResize && columnId ? columnSizing[columnId] : void 0;
2680
2752
  const effectiveMaxWidth = typeof sizedWidth === "number" ? `${sizedWidth}px` : maxWidth;
2681
2753
  const wrappedContent = shouldTruncate ? /* @__PURE__ */ jsx(TruncatedCell, { maxWidth: effectiveMaxWidth, tooltipContent: tooltipText, children: content }) : content;
2754
+ const expandAffordance = expansionEnabled && cellIndex === 0 ? /* @__PURE__ */ jsx(
2755
+ "span",
2756
+ {
2757
+ className: "inline-flex shrink-0 items-center",
2758
+ style: { paddingLeft: `${row.depth * 1.25}rem` },
2759
+ children: row.getCanExpand() ? /* @__PURE__ */ jsx(
2760
+ IconButton,
2761
+ {
2762
+ type: "button",
2763
+ variant: "ghost",
2764
+ size: "xs",
2765
+ "aria-label": row.getIsExpanded() ? t("ui.dataTable.expand.collapseRow", "Collapse row") : t("ui.dataTable.expand.expandRow", "Expand row"),
2766
+ "aria-expanded": row.getIsExpanded(),
2767
+ onClick: (event) => {
2768
+ event.stopPropagation();
2769
+ row.toggleExpanded();
2770
+ },
2771
+ children: row.getIsExpanded() ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": "true" })
2772
+ }
2773
+ ) : /* @__PURE__ */ jsx("span", { className: "inline-block size-6", "aria-hidden": "true" })
2774
+ }
2775
+ ) : null;
2682
2776
  return /* @__PURE__ */ jsx(
2683
2777
  TableCell,
2684
2778
  {
2685
2779
  className: responsiveClass(priority, columnMeta?.hidden) + (isStickyCell ? ` md:sticky md:left-0 md:z-10 md:bg-background ${STICKY_LEFT_SHADOW_CLASS}` : ""),
2686
2780
  style: typeof sizedWidth === "number" ? { width: sizedWidth, minWidth: sizedWidth, maxWidth: sizedWidth } : void 0,
2687
- children: wrappedContent
2781
+ children: expandAffordance ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
2782
+ expandAffordance,
2783
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children: wrappedContent })
2784
+ ] }) : wrappedContent
2688
2785
  },
2689
2786
  cell.id
2690
2787
  );