@dynostack/react-grid 0.2.0 → 0.3.1

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/dist/index.cjs CHANGED
@@ -161,6 +161,10 @@ function Separator({
161
161
  }
162
162
  );
163
163
  }
164
+ var PortalContainerContext = react.createContext(null);
165
+ function usePortalContainer() {
166
+ return react.useContext(PortalContainerContext);
167
+ }
164
168
  function DropdownMenu({
165
169
  ...props
166
170
  }) {
@@ -183,7 +187,8 @@ function DropdownMenuContent({
183
187
  sideOffset = 4,
184
188
  ...props
185
189
  }) {
186
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
190
+ const container = usePortalContainer();
191
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Portal, { container: container ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
187
192
  radixUi.DropdownMenu.Content,
188
193
  {
189
194
  "data-slot": "dropdown-menu-content",
@@ -261,7 +266,8 @@ function PopoverContent({
261
266
  sideOffset = 6,
262
267
  ...props
263
268
  }) {
264
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
269
+ const container = usePortalContainer();
270
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { container: container ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
265
271
  radixUi.Popover.Content,
266
272
  {
267
273
  "data-slot": "popover-content",
@@ -616,7 +622,8 @@ function SelectContent({
616
622
  position = "popper",
617
623
  ...props
618
624
  }) {
619
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
625
+ const container = usePortalContainer();
626
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Portal, { container: container ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
620
627
  radixUi.Select.Content,
621
628
  {
622
629
  "data-slot": "select-content",
@@ -1045,15 +1052,17 @@ function OpSelect({
1045
1052
  value,
1046
1053
  onChange
1047
1054
  }) {
1048
- return /* @__PURE__ */ jsxRuntime.jsx(
1049
- "select",
1050
- {
1051
- value,
1052
- onChange: (e) => onChange(e.target.value),
1053
- className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs outline-none focus-visible:border-ring",
1054
- children: ops.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.v, children: o.l }, o.v))
1055
- }
1056
- );
1055
+ return /* @__PURE__ */ jsxRuntime.jsxs(Select, { value, onValueChange: (v) => onChange(v), children: [
1056
+ /* @__PURE__ */ jsxRuntime.jsx(
1057
+ SelectTrigger,
1058
+ {
1059
+ size: "sm",
1060
+ className: "h-8 w-full justify-between text-xs",
1061
+ children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {})
1062
+ }
1063
+ ),
1064
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: ops.map((o) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: o.v, className: "text-xs", children: o.l }, o.v)) })
1065
+ ] });
1057
1066
  }
1058
1067
  function CombineToggle({
1059
1068
  value,
@@ -1721,14 +1730,23 @@ function DataTableRowActions({
1721
1730
  const [menuPosition, setMenuPosition] = react.useState({ top: 0, left: 0 });
1722
1731
  const triggerRef = react.useRef(null);
1723
1732
  const menuRef = react.useRef(null);
1733
+ const portalContainer = usePortalContainer();
1724
1734
  react.useLayoutEffect(() => {
1725
1735
  if (!open || !triggerRef.current) return;
1726
1736
  const rect = triggerRef.current.getBoundingClientRect();
1727
- setMenuPosition({
1728
- top: rect.bottom + 4,
1729
- left: Math.max(8, rect.right - 160)
1730
- });
1731
- }, [open]);
1737
+ if (portalContainer) {
1738
+ const cRect = portalContainer.getBoundingClientRect();
1739
+ setMenuPosition({
1740
+ top: rect.bottom - cRect.top + 4,
1741
+ left: Math.max(8, rect.right - cRect.left - 160)
1742
+ });
1743
+ } else {
1744
+ setMenuPosition({
1745
+ top: rect.bottom + 4,
1746
+ left: Math.max(8, rect.right - 160)
1747
+ });
1748
+ }
1749
+ }, [open, portalContainer]);
1732
1750
  react.useEffect(() => {
1733
1751
  if (!open) return;
1734
1752
  const onPointerDown = (event) => {
@@ -1790,8 +1808,12 @@ function DataTableRowActions({
1790
1808
  "div",
1791
1809
  {
1792
1810
  ref: menuRef,
1793
- className: "fixed z-[1000] w-40 rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
1794
- style: { top: menuPosition.top, left: menuPosition.left },
1811
+ className: "z-[1000] w-40 rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
1812
+ style: {
1813
+ position: portalContainer ? "absolute" : "fixed",
1814
+ top: menuPosition.top,
1815
+ left: menuPosition.left
1816
+ },
1795
1817
  children: [
1796
1818
  actions.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
1797
1819
  RowActionButton,
@@ -1829,7 +1851,7 @@ function DataTableRowActions({
1829
1851
  ]
1830
1852
  }
1831
1853
  ),
1832
- document.body
1854
+ portalContainer ?? document.body
1833
1855
  )
1834
1856
  ] });
1835
1857
  }
@@ -2052,6 +2074,7 @@ function splitTheme(theme) {
2052
2074
  }
2053
2075
  function buildPreset(hue, chroma = 0.03) {
2054
2076
  const c = chroma;
2077
+ const destH = blendTowardRed(hue);
2055
2078
  return {
2056
2079
  light: {
2057
2080
  background: `oklch(0.99 ${c * 0.2} ${hue})`,
@@ -2068,7 +2091,7 @@ function buildPreset(hue, chroma = 0.03) {
2068
2091
  mutedForeground: `oklch(0.5 ${c * 1.5} ${hue})`,
2069
2092
  accent: `oklch(0.94 ${c * 1.2} ${hue})`,
2070
2093
  accentForeground: `oklch(0.3 ${c * 3} ${hue})`,
2071
- destructive: `oklch(0.577 0.245 27.325)`,
2094
+ destructive: `oklch(0.577 0.22 ${destH})`,
2072
2095
  destructiveForeground: `oklch(0.985 0 0)`,
2073
2096
  border: `oklch(0.9 ${c * 0.8} ${hue})`,
2074
2097
  input: `oklch(0.9 ${c * 0.8} ${hue})`,
@@ -2090,7 +2113,7 @@ function buildPreset(hue, chroma = 0.03) {
2090
2113
  mutedForeground: `oklch(0.7 ${c * 0.5} ${hue})`,
2091
2114
  accent: `oklch(0.3 ${c * 1.5} ${hue})`,
2092
2115
  accentForeground: `oklch(0.97 ${c * 0.2} ${hue})`,
2093
- destructive: `oklch(0.577 0.245 27.325 / 0.6)`,
2116
+ destructive: `oklch(0.65 0.22 ${destH})`,
2094
2117
  destructiveForeground: `oklch(0.985 0 0)`,
2095
2118
  border: `oklch(0.27 ${c * 0.8} ${hue})`,
2096
2119
  input: `oklch(0.27 ${c * 0.8} ${hue})`,
@@ -2099,6 +2122,11 @@ function buildPreset(hue, chroma = 0.03) {
2099
2122
  }
2100
2123
  };
2101
2124
  }
2125
+ function blendTowardRed(hue) {
2126
+ const red = 25;
2127
+ let delta = (hue - red + 540) % 360 - 180;
2128
+ return (red + delta * 0.3 + 360) % 360;
2129
+ }
2102
2130
  function tokensToCssBlock(tokens) {
2103
2131
  if (!tokens) return "";
2104
2132
  const parts = [];
@@ -2168,6 +2196,332 @@ var themePresets = {
2168
2196
  };
2169
2197
  var ISOLATE_LIGHT_TOKENS = neutral.light;
2170
2198
  var ISOLATE_DARK_TOKENS = neutral.dark;
2199
+ function Sheet(props) {
2200
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Root, { "data-slot": "sheet", ...props });
2201
+ }
2202
+ function SheetOverlay({
2203
+ className,
2204
+ ...props
2205
+ }) {
2206
+ return /* @__PURE__ */ jsxRuntime.jsx(
2207
+ radixUi.Dialog.Overlay,
2208
+ {
2209
+ "data-slot": "sheet-overlay",
2210
+ className: cn(
2211
+ "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",
2212
+ className
2213
+ ),
2214
+ ...props
2215
+ }
2216
+ );
2217
+ }
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",
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
+ 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
+ };
2224
+ function SheetContent({
2225
+ className,
2226
+ side = "right",
2227
+ children,
2228
+ ...props
2229
+ }) {
2230
+ const container = usePortalContainer();
2231
+ return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Dialog.Portal, { container: container ?? void 0, children: [
2232
+ /* @__PURE__ */ jsxRuntime.jsx(SheetOverlay, {}),
2233
+ /* @__PURE__ */ jsxRuntime.jsxs(
2234
+ radixUi.Dialog.Content,
2235
+ {
2236
+ "data-slot": "sheet-content",
2237
+ className: cn(
2238
+ "fixed z-50 flex flex-col gap-4 bg-background p-6 shadow-lg outline-none transition data-open:animate-in data-closed:animate-out duration-200",
2239
+ SIDE_CLASS[side],
2240
+ className
2241
+ ),
2242
+ ...props,
2243
+ children: [
2244
+ children,
2245
+ /* @__PURE__ */ jsxRuntime.jsx(
2246
+ radixUi.Dialog.Close,
2247
+ {
2248
+ className: "absolute top-4 right-4 rounded-md p-1 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
2249
+ "aria-label": "Close",
2250
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-4" })
2251
+ }
2252
+ )
2253
+ ]
2254
+ }
2255
+ )
2256
+ ] });
2257
+ }
2258
+ function SheetHeader({ className, ...props }) {
2259
+ return /* @__PURE__ */ jsxRuntime.jsx(
2260
+ "div",
2261
+ {
2262
+ "data-slot": "sheet-header",
2263
+ className: cn("flex flex-col gap-1.5 pr-8", className),
2264
+ ...props
2265
+ }
2266
+ );
2267
+ }
2268
+ function SheetTitle({
2269
+ className,
2270
+ ...props
2271
+ }) {
2272
+ return /* @__PURE__ */ jsxRuntime.jsx(
2273
+ radixUi.Dialog.Title,
2274
+ {
2275
+ "data-slot": "sheet-title",
2276
+ className: cn("text-lg font-semibold leading-none", className),
2277
+ ...props
2278
+ }
2279
+ );
2280
+ }
2281
+ function SheetDescription({
2282
+ className,
2283
+ ...props
2284
+ }) {
2285
+ return /* @__PURE__ */ jsxRuntime.jsx(
2286
+ radixUi.Dialog.Description,
2287
+ {
2288
+ "data-slot": "sheet-description",
2289
+ className: cn("text-sm text-muted-foreground", className),
2290
+ ...props
2291
+ }
2292
+ );
2293
+ }
2294
+ var COLS_CLASS = {
2295
+ compact: "grid-cols-1",
2296
+ relaxed: "grid-cols-1 sm:grid-cols-2",
2297
+ comfy: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
2298
+ };
2299
+ var DENSITY_ICON = {
2300
+ compact: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListIcon, { className: "size-3.5" }),
2301
+ relaxed: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rows3Icon, { className: "size-3.5" }),
2302
+ comfy: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGridIcon, { className: "size-3.5" })
2303
+ };
2304
+ function DataTableViewSheet({
2305
+ table,
2306
+ row,
2307
+ open,
2308
+ onOpenChange,
2309
+ config
2310
+ }) {
2311
+ const [density, setDensity] = react.useState(
2312
+ config?.defaultDensity ?? "compact"
2313
+ );
2314
+ const fields = react.useMemo(() => {
2315
+ if (!row) return [];
2316
+ const wanted = config?.fields;
2317
+ const allCols = table.getAllLeafColumns().filter((c) => {
2318
+ if (c.id === "__select" || c.id === "__actions" || c.id === "__expand")
2319
+ return false;
2320
+ if (wanted) return wanted.includes(c.id);
2321
+ return c.getIsVisible();
2322
+ });
2323
+ if (wanted) {
2324
+ return wanted.map((id) => allCols.find((c) => c.id === id)).filter((c) => Boolean(c));
2325
+ }
2326
+ return allCols;
2327
+ }, [table, row, config?.fields]);
2328
+ if (!row) return null;
2329
+ const labels = config?.labels;
2330
+ const emptyValue = labels?.emptyValue ?? "\u2014";
2331
+ const densityLabels = {
2332
+ compact: labels?.density?.compact ?? "Compact",
2333
+ relaxed: labels?.density?.relaxed ?? "Relaxed",
2334
+ comfy: labels?.density?.comfy ?? "Comfy"
2335
+ };
2336
+ return /* @__PURE__ */ jsxRuntime.jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: config?.side ?? "right", children: [
2337
+ config?.renderHeader ? config.renderHeader(row) : /* @__PURE__ */ jsxRuntime.jsxs(SheetHeader, { children: [
2338
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { children: labels?.title ? labels.title(row) : "Details" }),
2339
+ labels?.description && /* @__PURE__ */ jsxRuntime.jsx(SheetDescription, { children: labels.description(row) })
2340
+ ] }),
2341
+ !config?.hideDensityTabs && /* @__PURE__ */ jsxRuntime.jsx(
2342
+ "div",
2343
+ {
2344
+ role: "tablist",
2345
+ "aria-label": "Layout density",
2346
+ className: "inline-flex w-fit gap-0.5 rounded-md border bg-muted/40 p-0.5 text-xs",
2347
+ children: ["compact", "relaxed", "comfy"].map((d) => /* @__PURE__ */ jsxRuntime.jsxs(
2348
+ "button",
2349
+ {
2350
+ role: "tab",
2351
+ "aria-selected": density === d,
2352
+ onClick: () => setDensity(d),
2353
+ className: cn(
2354
+ "inline-flex items-center gap-1 rounded px-2 py-1 transition-colors",
2355
+ density === d ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
2356
+ ),
2357
+ children: [
2358
+ DENSITY_ICON[d],
2359
+ densityLabels[d]
2360
+ ]
2361
+ },
2362
+ d
2363
+ ))
2364
+ }
2365
+ ),
2366
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("grid gap-3 overflow-y-auto", COLS_CLASS[density]), children: fields.map((col) => {
2367
+ const colId = col.id;
2368
+ const colDef = col.columnDef;
2369
+ const headerLabel = colDef.meta?.label ?? (typeof colDef.header === "string" ? colDef.header : colId);
2370
+ const rawValue = row[colDef.accessorKey ?? colId];
2371
+ const rendered = config?.renderField ? config.renderField({ column: col, value: rawValue, row }) : defaultRenderValue(rawValue, emptyValue);
2372
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2373
+ "div",
2374
+ {
2375
+ className: "flex flex-col gap-0.5 rounded-md border bg-card p-2.5",
2376
+ children: [
2377
+ /* @__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 })
2379
+ ]
2380
+ },
2381
+ colId
2382
+ );
2383
+ }) }),
2384
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: () => onOpenChange(false), children: "Close" }) })
2385
+ ] }) });
2386
+ }
2387
+ function defaultRenderValue(value, empty) {
2388
+ if (value == null || value === "") return empty;
2389
+ if (typeof value === "boolean") return value ? "Yes" : "No";
2390
+ if (value instanceof Date) return value.toLocaleString();
2391
+ if (typeof value === "object") return JSON.stringify(value);
2392
+ return String(value);
2393
+ }
2394
+ function AlertDialog(props) {
2395
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.AlertDialog.Root, { "data-slot": "alert-dialog", ...props });
2396
+ }
2397
+ function AlertDialogContent({
2398
+ className,
2399
+ children,
2400
+ ...props
2401
+ }) {
2402
+ const container = usePortalContainer();
2403
+ return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.AlertDialog.Portal, { container: container ?? void 0, children: [
2404
+ /* @__PURE__ */ jsxRuntime.jsx(
2405
+ radixUi.AlertDialog.Overlay,
2406
+ {
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"
2408
+ }
2409
+ ),
2410
+ /* @__PURE__ */ jsxRuntime.jsx(
2411
+ radixUi.AlertDialog.Content,
2412
+ {
2413
+ "data-slot": "alert-dialog-content",
2414
+ 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",
2416
+ className
2417
+ ),
2418
+ ...props,
2419
+ children
2420
+ }
2421
+ )
2422
+ ] });
2423
+ }
2424
+ function AlertDialogHeader({ className, ...props }) {
2425
+ return /* @__PURE__ */ jsxRuntime.jsx(
2426
+ "div",
2427
+ {
2428
+ "data-slot": "alert-dialog-header",
2429
+ className: cn("flex flex-col gap-1.5", className),
2430
+ ...props
2431
+ }
2432
+ );
2433
+ }
2434
+ function AlertDialogFooter({ className, ...props }) {
2435
+ return /* @__PURE__ */ jsxRuntime.jsx(
2436
+ "div",
2437
+ {
2438
+ "data-slot": "alert-dialog-footer",
2439
+ className: cn(
2440
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
2441
+ className
2442
+ ),
2443
+ ...props
2444
+ }
2445
+ );
2446
+ }
2447
+ function AlertDialogTitle({
2448
+ className,
2449
+ ...props
2450
+ }) {
2451
+ return /* @__PURE__ */ jsxRuntime.jsx(
2452
+ radixUi.AlertDialog.Title,
2453
+ {
2454
+ "data-slot": "alert-dialog-title",
2455
+ className: cn("text-lg font-semibold leading-none", className),
2456
+ ...props
2457
+ }
2458
+ );
2459
+ }
2460
+ function AlertDialogDescription({
2461
+ className,
2462
+ ...props
2463
+ }) {
2464
+ return /* @__PURE__ */ jsxRuntime.jsx(
2465
+ radixUi.AlertDialog.Description,
2466
+ {
2467
+ "data-slot": "alert-dialog-description",
2468
+ className: cn("text-sm text-muted-foreground", className),
2469
+ ...props
2470
+ }
2471
+ );
2472
+ }
2473
+ function AlertDialogAction({
2474
+ className,
2475
+ ...props
2476
+ }) {
2477
+ return /* @__PURE__ */ jsxRuntime.jsx(
2478
+ radixUi.AlertDialog.Action,
2479
+ {
2480
+ className: cn(buttonVariants(), className),
2481
+ ...props
2482
+ }
2483
+ );
2484
+ }
2485
+ function AlertDialogCancel({
2486
+ className,
2487
+ ...props
2488
+ }) {
2489
+ return /* @__PURE__ */ jsxRuntime.jsx(
2490
+ radixUi.AlertDialog.Cancel,
2491
+ {
2492
+ className: cn(buttonVariants({ variant: "outline" }), className),
2493
+ ...props
2494
+ }
2495
+ );
2496
+ }
2497
+ function DataTableConfirmDelete({
2498
+ open,
2499
+ onOpenChange,
2500
+ context,
2501
+ onConfirm,
2502
+ config
2503
+ }) {
2504
+ if (!context) return null;
2505
+ const title = config?.title ? config.title(context) : context.source === "bulk" ? `Delete ${context.rows.length} ${context.rows.length === 1 ? "row" : "rows"}?` : "Delete this row?";
2506
+ const description = config?.description ? config.description(context) : context.source === "bulk" ? `${context.rows.length} ${context.rows.length === 1 ? "row" : "rows"} will be permanently removed. This cannot be undone.` : "This row will be permanently removed. This cannot be undone.";
2507
+ return /* @__PURE__ */ jsxRuntime.jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
2508
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
2509
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTitle, { children: title }),
2510
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogDescription, { children: description })
2511
+ ] }),
2512
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
2513
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { children: config?.cancelLabel ?? "Cancel" }),
2514
+ /* @__PURE__ */ jsxRuntime.jsx(
2515
+ AlertDialogAction,
2516
+ {
2517
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
2518
+ onClick: onConfirm,
2519
+ children: config?.confirmLabel ?? "Delete"
2520
+ }
2521
+ )
2522
+ ] })
2523
+ ] }) });
2524
+ }
2171
2525
  function getPinningStyles(column) {
2172
2526
  const isPinned = column.getIsPinned();
2173
2527
  if (!isPinned) return {};
@@ -2240,7 +2594,9 @@ function ActionsCellHost({ row }) {
2240
2594
  rowActions,
2241
2595
  customRowActions,
2242
2596
  onRowAction,
2243
- onRowSave
2597
+ onRowSave,
2598
+ onView,
2599
+ onRequestDelete
2244
2600
  } = useEditingContext();
2245
2601
  const rowId = row.id;
2246
2602
  const isRowEdit = editingRowId === rowId;
@@ -2271,6 +2627,11 @@ function ActionsCellHost({ row }) {
2271
2627
  setEditingRowId(rowId);
2272
2628
  return;
2273
2629
  }
2630
+ if (a === "view") {
2631
+ onView?.(row.original);
2632
+ } else if (a === "delete") {
2633
+ onRequestDelete?.(row.original);
2634
+ }
2274
2635
  onRowAction?.(a, row.original);
2275
2636
  }
2276
2637
  }
@@ -2333,7 +2694,11 @@ function DataTable({
2333
2694
  labels: labelsProp,
2334
2695
  density = "default",
2335
2696
  theme,
2336
- isolate = false
2697
+ isolate = false,
2698
+ onView,
2699
+ onDelete,
2700
+ viewSheet,
2701
+ confirmDelete = true
2337
2702
  }) {
2338
2703
  const labels = { ...DEFAULT_LABELS, ...labelsProp };
2339
2704
  const f = {
@@ -2363,6 +2728,40 @@ function DataTable({
2363
2728
  darkCss: darkTokens ? tokensToCssBlock(darkTokens) : null
2364
2729
  };
2365
2730
  }, [theme, isolate]);
2731
+ const [gridRoot, setGridRoot] = react.useState(null);
2732
+ const [viewRow, setViewRow] = react.useState(null);
2733
+ const [confirmCtx, setConfirmCtx] = react.useState(null);
2734
+ const confirmEnabled = confirmDelete !== false;
2735
+ const confirmConfig = typeof confirmDelete === "object" ? confirmDelete : void 0;
2736
+ const viewSheetEnabled = viewSheet !== false;
2737
+ const viewSheetConfig = viewSheet && typeof viewSheet === "object" ? viewSheet : void 0;
2738
+ const handleViewRequest = (row) => {
2739
+ onView?.(row);
2740
+ if (viewSheetEnabled) setViewRow(row);
2741
+ };
2742
+ const handleDeleteRequest = (row) => {
2743
+ if (confirmEnabled) {
2744
+ setConfirmCtx({ rows: [row], source: "single" });
2745
+ } else {
2746
+ onDelete?.(row);
2747
+ }
2748
+ };
2749
+ const handleBulkDeleteRequest = (rows2) => {
2750
+ if (confirmEnabled) {
2751
+ setConfirmCtx({ rows: rows2, source: "bulk" });
2752
+ } else {
2753
+ onBulkDelete?.(rows2);
2754
+ }
2755
+ };
2756
+ const handleConfirmDelete = () => {
2757
+ if (!confirmCtx) return;
2758
+ if (confirmCtx.source === "single") {
2759
+ onDelete?.(confirmCtx.rows[0]);
2760
+ } else {
2761
+ onBulkDelete?.(confirmCtx.rows);
2762
+ }
2763
+ setConfirmCtx(null);
2764
+ };
2366
2765
  const [sorting, setSorting] = react.useState([]);
2367
2766
  const [columnFilters, setColumnFilters] = react.useState([]);
2368
2767
  const [internalGlobalFilter, setInternalGlobalFilter] = react.useState("");
@@ -2721,7 +3120,9 @@ function DataTable({
2721
3120
  customRowActions,
2722
3121
  onRowAction,
2723
3122
  onCellEdit,
2724
- onRowSave
3123
+ onRowSave,
3124
+ onView: handleViewRequest,
3125
+ onRequestDelete: handleDeleteRequest
2725
3126
  }),
2726
3127
  [
2727
3128
  editingCell,
@@ -2731,19 +3132,28 @@ function DataTable({
2731
3132
  customRowActions,
2732
3133
  onRowAction,
2733
3134
  onCellEdit,
2734
- onRowSave
3135
+ onRowSave,
3136
+ // intentional: handleViewRequest / handleDeleteRequest close over
3137
+ // viewSheetEnabled / confirmEnabled which are derived from props above.
3138
+ // They're recreated each render; the inputs here capture the prop deps.
3139
+ onView,
3140
+ onDelete,
3141
+ onBulkDelete,
3142
+ viewSheetEnabled,
3143
+ confirmEnabled
2735
3144
  ]
2736
3145
  );
2737
- return /* @__PURE__ */ jsxRuntime.jsx(
3146
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalContainerContext.Provider, { value: gridRoot, children: /* @__PURE__ */ jsxRuntime.jsx(
2738
3147
  DataTableEditingContext.Provider,
2739
3148
  {
2740
3149
  value: editingContextValue,
2741
3150
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2742
3151
  "div",
2743
3152
  {
3153
+ ref: setGridRoot,
2744
3154
  "data-dyno-grid": instanceId,
2745
3155
  className: cn(
2746
- "flex flex-col rounded-lg border bg-card text-card-foreground shadow-sm",
3156
+ "relative flex flex-col rounded-lg border bg-card text-card-foreground shadow-sm",
2747
3157
  DENSITY_CLASS[density],
2748
3158
  className
2749
3159
  ),
@@ -2762,7 +3172,7 @@ function DataTable({
2762
3172
  isFetching: effectiveIsFetching,
2763
3173
  onRefresh: f.refresh ? effectiveOnRefresh : void 0,
2764
3174
  onAddRow: f.addRow ? handleAddRow : void 0,
2765
- onBulkDelete,
3175
+ onBulkDelete: onBulkDelete ? handleBulkDeleteRequest : void 0,
2766
3176
  exportFileName,
2767
3177
  globalFilter,
2768
3178
  onGlobalFilterChange: (v) => {
@@ -2886,12 +3296,32 @@ function DataTable({
2886
3296
  labels.refreshing
2887
3297
  ] })
2888
3298
  ] }),
2889
- f.pagination && /* @__PURE__ */ jsxRuntime.jsx(DataTablePagination, { table, pageSizeOptions })
3299
+ f.pagination && /* @__PURE__ */ jsxRuntime.jsx(DataTablePagination, { table, pageSizeOptions }),
3300
+ /* @__PURE__ */ jsxRuntime.jsx(
3301
+ DataTableViewSheet,
3302
+ {
3303
+ table,
3304
+ row: viewRow,
3305
+ open: viewRow != null,
3306
+ onOpenChange: (o) => !o && setViewRow(null),
3307
+ config: viewSheetConfig
3308
+ }
3309
+ ),
3310
+ /* @__PURE__ */ jsxRuntime.jsx(
3311
+ DataTableConfirmDelete,
3312
+ {
3313
+ open: confirmCtx != null,
3314
+ onOpenChange: (o) => !o && setConfirmCtx(null),
3315
+ context: confirmCtx,
3316
+ onConfirm: handleConfirmDelete,
3317
+ config: confirmConfig
3318
+ }
3319
+ )
2890
3320
  ]
2891
3321
  }
2892
3322
  )
2893
3323
  }
2894
- );
3324
+ ) });
2895
3325
  }
2896
3326
  function DataTableRow({
2897
3327
  row,
@@ -2949,6 +3379,7 @@ exports.DataTableToolbar = DataTableToolbar;
2949
3379
  exports.EditableCell = EditableCell;
2950
3380
  exports.ISOLATE_DARK_TOKENS = ISOLATE_DARK_TOKENS;
2951
3381
  exports.ISOLATE_LIGHT_TOKENS = ISOLATE_LIGHT_TOKENS;
3382
+ exports.PortalContainerContext = PortalContainerContext;
2952
3383
  exports.booleanFilterFn = booleanFilterFn;
2953
3384
  exports.buildPreset = buildPreset;
2954
3385
  exports.dateFilterFn = dateFilterFn;
@@ -2963,5 +3394,6 @@ exports.themePresets = themePresets;
2963
3394
  exports.themeToStyle = themeToStyle;
2964
3395
  exports.tokensToCssBlock = tokensToCssBlock;
2965
3396
  exports.tokensToStyle = tokensToStyle;
3397
+ exports.usePortalContainer = usePortalContainer;
2966
3398
  //# sourceMappingURL=index.cjs.map
2967
3399
  //# sourceMappingURL=index.cjs.map