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