@dynostack/react-grid 0.1.3 → 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, 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
  }
@@ -2024,57 +2046,480 @@ var TOKEN_TO_VAR = {
2024
2046
  radius: "--radius",
2025
2047
  fontFamily: "--font-family"
2026
2048
  };
2027
- function themeToStyle(theme) {
2028
- if (!theme) return {};
2049
+ function isModed(theme) {
2050
+ if (!theme) return false;
2051
+ return "light" in theme || "dark" in theme;
2052
+ }
2053
+ function tokensToStyle(tokens) {
2054
+ if (!tokens) return {};
2029
2055
  const out = {};
2030
- for (const [k, v] of Object.entries(theme)) {
2056
+ for (const [k, v] of Object.entries(tokens)) {
2031
2057
  if (v == null) continue;
2032
2058
  const cssVar = TOKEN_TO_VAR[k];
2033
2059
  if (cssVar) out[cssVar] = v;
2034
2060
  }
2035
2061
  return out;
2036
2062
  }
2037
- var themePresets = {
2063
+ function themeToStyle(theme) {
2064
+ if (!theme) return {};
2065
+ if (isModed(theme)) return tokensToStyle(theme.light);
2066
+ return tokensToStyle(theme);
2067
+ }
2068
+ function splitTheme(theme) {
2069
+ if (!theme) return { light: void 0, dark: void 0 };
2070
+ if (isModed(theme)) return { light: theme.light, dark: theme.dark };
2071
+ return { light: theme, dark: void 0 };
2072
+ }
2073
+ function buildPreset(hue, chroma = 0.03) {
2074
+ const c = chroma;
2075
+ const destH = blendTowardRed(hue);
2076
+ return {
2077
+ light: {
2078
+ background: `oklch(0.99 ${c * 0.2} ${hue})`,
2079
+ foreground: `oklch(0.18 ${c * 1.2} ${hue})`,
2080
+ card: `oklch(1 0 0)`,
2081
+ cardForeground: `oklch(0.18 ${c * 1.2} ${hue})`,
2082
+ popover: `oklch(1 0 0)`,
2083
+ popoverForeground: `oklch(0.18 ${c * 1.2} ${hue})`,
2084
+ primary: `oklch(0.55 0.2 ${hue})`,
2085
+ primaryForeground: `oklch(0.99 0 0)`,
2086
+ secondary: `oklch(0.96 ${c} ${hue})`,
2087
+ secondaryForeground: `oklch(0.25 ${c * 2} ${hue})`,
2088
+ muted: `oklch(0.96 ${c} ${hue})`,
2089
+ mutedForeground: `oklch(0.5 ${c * 1.5} ${hue})`,
2090
+ accent: `oklch(0.94 ${c * 1.2} ${hue})`,
2091
+ accentForeground: `oklch(0.3 ${c * 3} ${hue})`,
2092
+ destructive: `oklch(0.577 0.22 ${destH})`,
2093
+ destructiveForeground: `oklch(0.985 0 0)`,
2094
+ border: `oklch(0.9 ${c * 0.8} ${hue})`,
2095
+ input: `oklch(0.9 ${c * 0.8} ${hue})`,
2096
+ ring: `oklch(0.55 0.2 ${hue} / 0.5)`,
2097
+ radius: "0.625rem"
2098
+ },
2099
+ dark: {
2100
+ background: `oklch(0.16 ${c * 0.6} ${hue})`,
2101
+ foreground: `oklch(0.97 ${c * 0.2} ${hue})`,
2102
+ card: `oklch(0.21 ${c * 0.8} ${hue})`,
2103
+ cardForeground: `oklch(0.97 ${c * 0.2} ${hue})`,
2104
+ popover: `oklch(0.21 ${c * 0.8} ${hue})`,
2105
+ popoverForeground: `oklch(0.97 ${c * 0.2} ${hue})`,
2106
+ primary: `oklch(0.7 0.18 ${hue})`,
2107
+ primaryForeground: `oklch(0.16 ${c * 0.6} ${hue})`,
2108
+ secondary: `oklch(0.27 ${c * 0.8} ${hue})`,
2109
+ secondaryForeground: `oklch(0.97 ${c * 0.2} ${hue})`,
2110
+ muted: `oklch(0.27 ${c * 0.8} ${hue})`,
2111
+ mutedForeground: `oklch(0.7 ${c * 0.5} ${hue})`,
2112
+ accent: `oklch(0.3 ${c * 1.5} ${hue})`,
2113
+ accentForeground: `oklch(0.97 ${c * 0.2} ${hue})`,
2114
+ destructive: `oklch(0.65 0.22 ${destH})`,
2115
+ destructiveForeground: `oklch(0.985 0 0)`,
2116
+ border: `oklch(0.27 ${c * 0.8} ${hue})`,
2117
+ input: `oklch(0.27 ${c * 0.8} ${hue})`,
2118
+ ring: `oklch(0.7 0.18 ${hue} / 0.5)`,
2119
+ radius: "0.625rem"
2120
+ }
2121
+ };
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
+ }
2128
+ function tokensToCssBlock(tokens) {
2129
+ if (!tokens) return "";
2130
+ const parts = [];
2131
+ for (const [k, v] of Object.entries(tokens)) {
2132
+ if (v == null) continue;
2133
+ const cssVar = TOKEN_TO_VAR[k];
2134
+ if (cssVar) parts.push(`${cssVar}:${v}`);
2135
+ }
2136
+ return parts.join(";");
2137
+ }
2138
+ var neutral = {
2038
2139
  light: {
2039
2140
  background: "oklch(1 0 0)",
2040
2141
  foreground: "oklch(0.145 0 0)",
2041
2142
  card: "oklch(1 0 0)",
2042
2143
  cardForeground: "oklch(0.145 0 0)",
2144
+ popover: "oklch(1 0 0)",
2145
+ popoverForeground: "oklch(0.145 0 0)",
2043
2146
  primary: "oklch(0.205 0 0)",
2044
2147
  primaryForeground: "oklch(0.985 0 0)",
2045
- border: "oklch(0.922 0 0)"
2148
+ secondary: "oklch(0.97 0 0)",
2149
+ secondaryForeground: "oklch(0.205 0 0)",
2150
+ muted: "oklch(0.97 0 0)",
2151
+ mutedForeground: "oklch(0.556 0 0)",
2152
+ accent: "oklch(0.97 0 0)",
2153
+ accentForeground: "oklch(0.205 0 0)",
2154
+ destructive: "oklch(0.577 0.245 27.325)",
2155
+ destructiveForeground: "oklch(0.985 0 0)",
2156
+ border: "oklch(0.922 0 0)",
2157
+ input: "oklch(0.922 0 0)",
2158
+ ring: "oklch(0.708 0 0)",
2159
+ radius: "0.625rem"
2046
2160
  },
2047
2161
  dark: {
2048
2162
  background: "oklch(0.145 0 0)",
2049
2163
  foreground: "oklch(0.985 0 0)",
2050
2164
  card: "oklch(0.205 0 0)",
2051
2165
  cardForeground: "oklch(0.985 0 0)",
2166
+ popover: "oklch(0.205 0 0)",
2167
+ popoverForeground: "oklch(0.985 0 0)",
2052
2168
  primary: "oklch(0.985 0 0)",
2053
2169
  primaryForeground: "oklch(0.205 0 0)",
2054
- border: "oklch(0.269 0 0)"
2055
- },
2056
- emerald: {
2057
- primary: "oklch(0.66 0.15 162)",
2058
- primaryForeground: "oklch(1 0 0)",
2059
- accent: "oklch(0.94 0.04 162)",
2060
- accentForeground: "oklch(0.3 0.1 162)",
2061
- ring: "oklch(0.66 0.15 162 / 0.5)"
2062
- },
2063
- violet: {
2064
- primary: "oklch(0.55 0.22 285)",
2065
- primaryForeground: "oklch(1 0 0)",
2066
- accent: "oklch(0.94 0.04 285)",
2067
- accentForeground: "oklch(0.3 0.12 285)",
2068
- ring: "oklch(0.55 0.22 285 / 0.5)"
2069
- },
2070
- amber: {
2071
- primary: "oklch(0.7 0.18 65)",
2072
- primaryForeground: "oklch(0.2 0 0)",
2073
- accent: "oklch(0.94 0.05 65)",
2074
- accentForeground: "oklch(0.3 0.12 65)",
2075
- ring: "oklch(0.7 0.18 65 / 0.5)"
2170
+ secondary: "oklch(0.269 0 0)",
2171
+ secondaryForeground: "oklch(0.985 0 0)",
2172
+ muted: "oklch(0.269 0 0)",
2173
+ mutedForeground: "oklch(0.708 0 0)",
2174
+ accent: "oklch(0.269 0 0)",
2175
+ accentForeground: "oklch(0.985 0 0)",
2176
+ destructive: "oklch(0.577 0.245 27.325 / 0.6)",
2177
+ destructiveForeground: "oklch(0.985 0 0)",
2178
+ border: "oklch(0.269 0 0)",
2179
+ input: "oklch(0.269 0 0)",
2180
+ ring: "oklch(0.439 0 0)",
2181
+ radius: "0.625rem"
2076
2182
  }
2077
2183
  };
2184
+ var themePresets = {
2185
+ neutral,
2186
+ light: { light: neutral.light },
2187
+ dark: { light: neutral.dark },
2188
+ violet: buildPreset(285),
2189
+ emerald: buildPreset(162),
2190
+ amber: buildPreset(65),
2191
+ rose: buildPreset(15),
2192
+ sky: buildPreset(235),
2193
+ slate: buildPreset(240, 0.015)
2194
+ };
2195
+ var ISOLATE_LIGHT_TOKENS = neutral.light;
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
+ }
2078
2523
  function getPinningStyles(column) {
2079
2524
  const isPinned = column.getIsPinned();
2080
2525
  if (!isPinned) return {};
@@ -2147,7 +2592,9 @@ function ActionsCellHost({ row }) {
2147
2592
  rowActions,
2148
2593
  customRowActions,
2149
2594
  onRowAction,
2150
- onRowSave
2595
+ onRowSave,
2596
+ onView,
2597
+ onRequestDelete
2151
2598
  } = useEditingContext();
2152
2599
  const rowId = row.id;
2153
2600
  const isRowEdit = editingRowId === rowId;
@@ -2178,6 +2625,11 @@ function ActionsCellHost({ row }) {
2178
2625
  setEditingRowId(rowId);
2179
2626
  return;
2180
2627
  }
2628
+ if (a === "view") {
2629
+ onView?.(row.original);
2630
+ } else if (a === "delete") {
2631
+ onRequestDelete?.(row.original);
2632
+ }
2181
2633
  onRowAction?.(a, row.original);
2182
2634
  }
2183
2635
  }
@@ -2239,7 +2691,12 @@ function DataTable({
2239
2691
  features,
2240
2692
  labels: labelsProp,
2241
2693
  density = "default",
2242
- theme
2694
+ theme,
2695
+ isolate = false,
2696
+ onView,
2697
+ onDelete,
2698
+ viewSheet,
2699
+ confirmDelete = true
2243
2700
  }) {
2244
2701
  const labels = { ...DEFAULT_LABELS, ...labelsProp };
2245
2702
  const f = {
@@ -2255,7 +2712,54 @@ function DataTable({
2255
2712
  reordering: features?.reordering ?? true,
2256
2713
  pinning: features?.pinning ?? true
2257
2714
  };
2258
- const themeStyle = themeToStyle(theme);
2715
+ const rawInstanceId = useId();
2716
+ const instanceId = useMemo(
2717
+ () => `dyno-grid-${rawInstanceId.replace(/[^a-zA-Z0-9]/g, "")}`,
2718
+ [rawInstanceId]
2719
+ );
2720
+ const { lightStyle, darkCss } = useMemo(() => {
2721
+ const { light, dark } = splitTheme(theme);
2722
+ const lightTokens = isolate ? { ...ISOLATE_LIGHT_TOKENS, ...light ?? {} } : light;
2723
+ const darkTokens = isolate ? { ...ISOLATE_DARK_TOKENS, ...dark ?? light ?? {} } : dark;
2724
+ return {
2725
+ lightStyle: tokensToStyle(lightTokens),
2726
+ darkCss: darkTokens ? tokensToCssBlock(darkTokens) : null
2727
+ };
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
+ };
2259
2763
  const [sorting, setSorting] = useState([]);
2260
2764
  const [columnFilters, setColumnFilters] = useState([]);
2261
2765
  const [internalGlobalFilter, setInternalGlobalFilter] = useState("");
@@ -2614,7 +3118,9 @@ function DataTable({
2614
3118
  customRowActions,
2615
3119
  onRowAction,
2616
3120
  onCellEdit,
2617
- onRowSave
3121
+ onRowSave,
3122
+ onView: handleViewRequest,
3123
+ onRequestDelete: handleDeleteRequest
2618
3124
  }),
2619
3125
  [
2620
3126
  editingCell,
@@ -2624,23 +3130,38 @@ function DataTable({
2624
3130
  customRowActions,
2625
3131
  onRowAction,
2626
3132
  onCellEdit,
2627
- 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
2628
3142
  ]
2629
3143
  );
2630
- return /* @__PURE__ */ jsx(
3144
+ return /* @__PURE__ */ jsx(PortalContainerContext.Provider, { value: gridRoot, children: /* @__PURE__ */ jsx(
2631
3145
  DataTableEditingContext.Provider,
2632
3146
  {
2633
3147
  value: editingContextValue,
2634
3148
  children: /* @__PURE__ */ jsxs(
2635
3149
  "div",
2636
3150
  {
3151
+ ref: setGridRoot,
3152
+ "data-dyno-grid": instanceId,
2637
3153
  className: cn(
2638
- "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",
2639
3155
  DENSITY_CLASS[density],
2640
3156
  className
2641
3157
  ),
2642
- style: themeStyle,
3158
+ style: lightStyle,
2643
3159
  children: [
3160
+ darkCss && /* @__PURE__ */ jsxs("style", { children: [
3161
+ `@media (prefers-color-scheme: dark){[data-dyno-grid="${instanceId}"]{${darkCss}}}`,
3162
+ `.dark [data-dyno-grid="${instanceId}"]{${darkCss}}`,
3163
+ `[data-dyno-grid="${instanceId}"].dark{${darkCss}}`
3164
+ ] }),
2644
3165
  /* @__PURE__ */ jsx(
2645
3166
  DataTableToolbar,
2646
3167
  {
@@ -2649,7 +3170,7 @@ function DataTable({
2649
3170
  isFetching: effectiveIsFetching,
2650
3171
  onRefresh: f.refresh ? effectiveOnRefresh : void 0,
2651
3172
  onAddRow: f.addRow ? handleAddRow : void 0,
2652
- onBulkDelete,
3173
+ onBulkDelete: onBulkDelete ? handleBulkDeleteRequest : void 0,
2653
3174
  exportFileName,
2654
3175
  globalFilter,
2655
3176
  onGlobalFilterChange: (v) => {
@@ -2773,12 +3294,32 @@ function DataTable({
2773
3294
  labels.refreshing
2774
3295
  ] })
2775
3296
  ] }),
2776
- 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
+ )
2777
3318
  ]
2778
3319
  }
2779
3320
  )
2780
3321
  }
2781
- );
3322
+ ) });
2782
3323
  }
2783
3324
  function DataTableRow({
2784
3325
  row,
@@ -2827,6 +3368,6 @@ function renderSkeleton(cols, rows) {
2827
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));
2828
3369
  }
2829
3370
 
2830
- export { ColumnFilterPanel, DataTable, DataTableColumnHeader, DataTablePagination, DataTableRowActions, DataTableToolbar, EditableCell, booleanFilterFn, dateFilterFn, exportToCsv, exportToExcel, isFilterActive, numberFilterFn, setFilterFn, textFilterFn, themePresets, themeToStyle };
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 };
2831
3372
  //# sourceMappingURL=index.js.map
2832
3373
  //# sourceMappingURL=index.js.map