@dynostack/react-grid 0.3.1 → 0.3.2

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 CHANGED
@@ -26,6 +26,8 @@ A single `<DataTable />` component that gives you ag-grid–level functionality
26
26
  - **Selection + bulk actions** — pinned `__select` column with select-all, clear, bulk delete
27
27
  - **Expandable rows** — provide a `renderSubRow` panel or use TanStack's nested `getSubRows`
28
28
  - **CSV / Excel export** — selection-aware (export selected vs. all)
29
+ - **Built-in row Details panel** — `View` opens a scoped sheet with compact, relaxed, and comfy field layouts
30
+ - **Scoped delete confirmation** — row and bulk delete confirmations stay inside the table instead of covering the entire app
29
31
  - **Theming that just works** — shadcn-compatible CSS variables, automatic OS dark-mode follow, cascade-layered defaults that never overwrite your app theme, full-repaint moded presets (`violet`, `emerald`, `amber`, `rose`, `sky`, `slate`, …), `buildPreset(hue)` for custom hues, and `isolate` to opt out of inheriting the app theme
30
32
  - **Density** — `compact` · `default` · `comfortable`
31
33
  - **i18n / labels** — every visible string is overridable
@@ -613,7 +615,7 @@ The set filter automatically derives unique values from the visible rows when `s
613
615
  Selection is on by default (`enableSelection: true`). When any row is selected the toolbar swaps in:
614
616
 
615
617
  - A `<count> selected` badge
616
- - `Delete` button → wires to `onBulkDelete?(rows)`
618
+ - `Delete` button → opens the confirmation dialog first, then calls `onBulkDelete?(rows)` after confirm
617
619
  - `Clear` button → resets selection
618
620
 
619
621
  ```tsx
@@ -660,7 +662,15 @@ Mark a column non-exportable via `meta.exportable: false`.
660
662
 
661
663
  ## View sheet
662
664
 
663
- Click the row action "View" → a right-side `Sheet` slides in showing every visible column as a `{Label}: {value}` card. The user can switch layout density inline (Compact 1 col / Relaxed 2 col / Comfy 3 col).
665
+ Click the row action "View" → a right-side `Sheet` slides in showing every visible column as a `{Label}: {value}` card. The user can switch layout density inline:
666
+
667
+ | Option | Layout | Intended use |
668
+ | --- | --- | --- |
669
+ | `Compact` | 3 columns, tighter cards | Scan more fields at once. |
670
+ | `Relaxed` | 2 columns, medium spacing | Balanced default for mixed values. |
671
+ | `Comfy` | 1 column, roomier cards | Read long values without cramped wrapping. |
672
+
673
+ The sheet is responsive and wider on desktop so the multi-column modes have enough room for real row data.
664
674
 
665
675
  Works out of the box with no props. Customize via `viewSheet`:
666
676
 
@@ -668,7 +678,7 @@ Works out of the box with no props. Customize via `viewSheet`:
668
678
  <DataTable
669
679
  viewSheet={{
670
680
  side: "right", // or "left"
671
- defaultDensity: "relaxed", // initial column count
681
+ defaultDensity: "relaxed", // initial layout density
672
682
  hideDensityTabs: true, // hide the layout picker
673
683
  fields: ["name", "email", "role"], // limit / reorder shown columns
674
684
  renderField: ({ column, value, row }) => // override how a value renders
@@ -678,7 +688,7 @@ Works out of the box with no props. Customize via `viewSheet`:
678
688
  title: (row) => `${row.name} (${row.role})`,
679
689
  description: (row) => `Joined ${row.joinedAt}`,
680
690
  emptyValue: "—",
681
- density: { compact: "1 col", relaxed: "2 cols", comfy: "3 cols" },
691
+ density: { compact: "3 cols", relaxed: "2 cols", comfy: "1 col" },
682
692
  },
683
693
  }}
684
694
  onView={(row) => track("user.view", row)} // optional side-effect
@@ -699,6 +709,8 @@ Disable the built-in sheet entirely:
699
709
 
700
710
  Both the row-action "Delete" and the toolbar "Bulk delete" open a confirmation `AlertDialog` by default. The user must confirm before `onDelete` or `onBulkDelete` fires.
701
711
 
712
+ The dialog is mounted inside the DataTable portal container, so its blur / dim overlay covers only that table instance. It does not block or blur the rest of the page.
713
+
702
714
  ```tsx
703
715
  <DataTable
704
716
  onDelete={(row) => api.deleteUser(row.id)}
@@ -716,6 +728,8 @@ Both the row-action "Delete" and the toolbar "Bulk delete" open a confirmation `
716
728
  />
717
729
  ```
718
730
 
731
+ Use `onDelete` for the built-in row delete action. Do not put the actual delete mutation in `onRowAction("delete")`, because the built-in delete action is handled by the confirmation flow.
732
+
719
733
  Skip the dialog (fire immediately):
720
734
 
721
735
  ```tsx
@@ -740,10 +754,10 @@ Skip the dialog (fire immediately):
740
754
  { id: "archive", label: "Archive", icon: <ArchiveIcon /> },
741
755
  ]}
742
756
  onRowAction={(action, row) => {
743
- if (action === "delete") deleteMutation.mutate([row.id])
744
757
  if (action === "suspend") saveMutation.mutate({ ...row, status: "suspended" })
745
758
  // ...
746
759
  }}
760
+ onDelete={(row) => deleteMutation.mutate([row.id])}
747
761
  />
748
762
  ```
749
763
 
package/dist/index.cjs CHANGED
@@ -2216,8 +2216,8 @@ function SheetOverlay({
2216
2216
  );
2217
2217
  }
2218
2218
  var SIDE_CLASS = {
2219
- right: "right-0 top-0 h-full w-3/4 max-w-md border-l data-closed:slide-out-to-right data-open:slide-in-from-right",
2220
- left: "left-0 top-0 h-full w-3/4 max-w-md border-r data-closed:slide-out-to-left data-open:slide-in-from-left",
2219
+ right: "right-0 top-0 h-full w-full border-l sm:w-[min(48rem,calc(100vw-2rem))] data-closed:slide-out-to-right data-open:slide-in-from-right",
2220
+ left: "left-0 top-0 h-full w-full border-r sm:w-[min(48rem,calc(100vw-2rem))] data-closed:slide-out-to-left data-open:slide-in-from-left",
2221
2221
  top: "left-0 top-0 w-full max-h-[80vh] border-b data-closed:slide-out-to-top data-open:slide-in-from-top",
2222
2222
  bottom: "left-0 bottom-0 w-full max-h-[80vh] border-t data-closed:slide-out-to-bottom data-open:slide-in-from-bottom"
2223
2223
  };
@@ -2292,14 +2292,19 @@ function SheetDescription({
2292
2292
  );
2293
2293
  }
2294
2294
  var COLS_CLASS = {
2295
- compact: "grid-cols-1",
2295
+ compact: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
2296
2296
  relaxed: "grid-cols-1 sm:grid-cols-2",
2297
- comfy: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
2297
+ comfy: "grid-cols-1"
2298
+ };
2299
+ var CARD_CLASS = {
2300
+ compact: "gap-0.5 p-2.5",
2301
+ relaxed: "gap-1 p-3",
2302
+ comfy: "gap-1.5 p-4"
2298
2303
  };
2299
2304
  var DENSITY_ICON = {
2300
- compact: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListIcon, { className: "size-3.5" }),
2305
+ compact: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGridIcon, { className: "size-3.5" }),
2301
2306
  relaxed: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rows3Icon, { className: "size-3.5" }),
2302
- comfy: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGridIcon, { className: "size-3.5" })
2307
+ comfy: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListIcon, { className: "size-3.5" })
2303
2308
  };
2304
2309
  function DataTableViewSheet({
2305
2310
  table,
@@ -2372,10 +2377,13 @@ function DataTableViewSheet({
2372
2377
  return /* @__PURE__ */ jsxRuntime.jsxs(
2373
2378
  "div",
2374
2379
  {
2375
- className: "flex flex-col gap-0.5 rounded-md border bg-card p-2.5",
2380
+ className: cn(
2381
+ "flex min-w-0 flex-col rounded-md border bg-card",
2382
+ CARD_CLASS[density]
2383
+ ),
2376
2384
  children: [
2377
2385
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: headerLabel }),
2378
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground break-words", children: rendered })
2386
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "break-words text-sm text-foreground", children: rendered })
2379
2387
  ]
2380
2388
  },
2381
2389
  colId
@@ -2400,11 +2408,15 @@ function AlertDialogContent({
2400
2408
  ...props
2401
2409
  }) {
2402
2410
  const container = usePortalContainer();
2411
+ const positionClass = container ? "absolute" : "fixed";
2403
2412
  return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.AlertDialog.Portal, { container: container ?? void 0, children: [
2404
2413
  /* @__PURE__ */ jsxRuntime.jsx(
2405
2414
  radixUi.AlertDialog.Overlay,
2406
2415
  {
2407
- className: "fixed inset-0 z-50 bg-foreground/30 backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0"
2416
+ className: cn(
2417
+ "inset-0 z-50 bg-foreground/30 backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
2418
+ positionClass
2419
+ )
2408
2420
  }
2409
2421
  ),
2410
2422
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2412,7 +2424,8 @@ function AlertDialogContent({
2412
2424
  {
2413
2425
  "data-slot": "alert-dialog-content",
2414
2426
  className: cn(
2415
- "fixed top-1/2 left-1/2 z-50 grid w-full max-w-md -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border bg-background p-6 shadow-lg outline-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
2427
+ "top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border bg-background p-6 shadow-lg outline-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
2428
+ positionClass,
2416
2429
  className
2417
2430
  ),
2418
2431
  ...props,
@@ -2631,6 +2644,7 @@ function ActionsCellHost({ row }) {
2631
2644
  onView?.(row.original);
2632
2645
  } else if (a === "delete") {
2633
2646
  onRequestDelete?.(row.original);
2647
+ return;
2634
2648
  }
2635
2649
  onRowAction?.(a, row.original);
2636
2650
  }