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