@dynostack/react-grid 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,8 @@ A single `<DataTable />` component that gives you ag-grid–level functionality
51
51
  - [Selection & bulk actions](#selection--bulk-actions)
52
52
  - [Expandable rows](#expandable-rows)
53
53
  - [Export](#export)
54
+ - [View sheet](#view-sheet)
55
+ - [Delete confirmation](#delete-confirmation)
54
56
  - [Custom row actions](#custom-row-actions)
55
57
  - [Server-side data](#server-side-data)
56
58
  - [API reference](#api-reference)
@@ -75,7 +77,28 @@ yarn add @dynostack/react-grid
75
77
 
76
78
  ## Tailwind setup
77
79
 
78
- The component ships Tailwind class names verbatim. Tell your Tailwind config to scan the package files:
80
+ The component ships Tailwind class names verbatim, so your Tailwind build needs to know two things:
81
+
82
+ 1. **Where to scan** for the class strings inside the bundle.
83
+ 2. **Which semantic color tokens** (`bg-popover`, `bg-card`, `text-foreground`, …) exist.
84
+
85
+ The package's `styles.css` registers the tokens for you via Tailwind v4's `@theme inline`. You only need to wire scanning.
86
+
87
+ ### Tailwind v4 — zero config
88
+
89
+ ```css
90
+ /* your global stylesheet (e.g. src/index.css) */
91
+ @import "tailwindcss";
92
+ @source "../node_modules/@dynostack/react-grid/dist";
93
+ @import "@dynostack/react-grid/styles.css";
94
+ @import "@dynostack/react-grid/page.css"; /* optional: extend tokens to <body> */
95
+ ```
96
+
97
+ That's the whole setup. No `tailwind.config.js`, no `@theme` block to copy-paste, no shadcn install required. Overlay surfaces (popovers, dropdowns, sheets, the row-actions menu) all render correctly out of the box.
98
+
99
+ ### Tailwind v3
100
+
101
+ v3 doesn't read CSS `@theme` directives, so the semantic-color mapping has to live in your `tailwind.config.js`. The shadcn install guide for v3 covers the exact `theme.extend.colors` block you need — copy that, plus add the package's `dist` to your `content` array:
79
102
 
80
103
  ```js
81
104
  // tailwind.config.{js,ts}
@@ -84,10 +107,28 @@ export default {
84
107
  "./src/**/*.{ts,tsx}",
85
108
  "./node_modules/@dynostack/react-grid/dist/**/*.{js,mjs,cjs}",
86
109
  ],
110
+ theme: {
111
+ extend: {
112
+ colors: {
113
+ // copy the shadcn v3 color mapping here
114
+ // (background, foreground, card, popover, primary, secondary,
115
+ // muted, accent, destructive, border, input, ring)
116
+ background: "hsl(var(--background))",
117
+ foreground: "hsl(var(--foreground))",
118
+ // … etc
119
+ },
120
+ },
121
+ },
87
122
  }
88
123
  ```
89
124
 
90
- > Tailwind v4? Add the same path to your `@source` directive instead.
125
+ Then import `styles.css` as usual:
126
+
127
+ ```ts
128
+ import "@dynostack/react-grid/styles.css"
129
+ ```
130
+
131
+ > Starting a new project? **Use Tailwind v4.** The v4 path above is meaningfully simpler — the package handles token registration for you.
91
132
 
92
133
  ## Theme tokens
93
134
 
@@ -102,15 +143,22 @@ The grid is built on **shadcn/ui CSS variables**. It auto-adjusts to whatever th
102
143
  | Custom theme with non-shadcn names | Pass [`theme` prop](#theming) | Per-instance override mapped to shadcn vars. |
103
144
  | Want one grid to ignore the app theme | Pass `isolate` | Grid uses bundled defaults regardless of `:root`. |
104
145
 
105
- **Why this just works.** The bundled `styles.css` declares its defaults inside the `dynostack-grid-defaults` cascade layer. Any unlayered consumer rule (which is where shadcn and most app CSS lives) automatically wins — import order doesn't matter, and you can't accidentally overwrite your app's theme by importing the grid's stylesheet.
146
+ **Why this just works.** The bundled `styles.css`:
106
147
 
107
- ### Minimal install
148
+ 1. **Registers Tailwind v4 utility tokens** via a top-level `@theme inline` block — so `bg-popover`, `text-foreground`, `border-border`, etc. resolve to your tokens without any consumer-side `@theme` block.
149
+ 2. **Declares variable values inside the `dynostack-grid-defaults` cascade layer** — any unlayered consumer rule (which is where shadcn and most app CSS lives) automatically wins, regardless of import order. You can't accidentally overwrite your app's theme by importing the grid's stylesheet.
108
150
 
109
- ```ts
110
- // main.tsx — once per app
111
- import "@dynostack/react-grid/styles.css"
151
+ ### Minimal install (Tailwind v4)
152
+
153
+ ```css
154
+ /* your global stylesheet */
155
+ @import "tailwindcss";
156
+ @source "../node_modules/@dynostack/react-grid/dist";
157
+ @import "@dynostack/react-grid/styles.css";
112
158
  ```
113
159
 
160
+ See the [Tailwind setup](#tailwind-setup) section for v3.
161
+
114
162
  ### Optional: extend the theme to the page
115
163
 
116
164
  By default the grid only styles itself, not the surrounding page. If you want `<body>` to use the same background/foreground as the grid:
@@ -610,6 +658,72 @@ Mark a column non-exportable via `meta.exportable: false`.
610
658
 
611
659
  ---
612
660
 
661
+ ## View sheet
662
+
663
+ Click the row action "View" → a right-side `Sheet` slides in showing every visible column as a `{Label}: {value}` card. The user can switch layout density inline (Compact 1 col / Relaxed 2 col / Comfy 3 col).
664
+
665
+ Works out of the box with no props. Customize via `viewSheet`:
666
+
667
+ ```tsx
668
+ <DataTable
669
+ viewSheet={{
670
+ side: "right", // or "left"
671
+ defaultDensity: "relaxed", // initial column count
672
+ hideDensityTabs: true, // hide the layout picker
673
+ fields: ["name", "email", "role"], // limit / reorder shown columns
674
+ renderField: ({ column, value, row }) => // override how a value renders
675
+ column.id === "phone" ? <a href={`tel:${value}`}>{String(value)}</a> : null,
676
+ renderHeader: (row) => <YourCustomHeader row={row} />,
677
+ labels: {
678
+ title: (row) => `${row.name} (${row.role})`,
679
+ description: (row) => `Joined ${row.joinedAt}`,
680
+ emptyValue: "—",
681
+ density: { compact: "1 col", relaxed: "2 cols", comfy: "3 cols" },
682
+ },
683
+ }}
684
+ onView={(row) => track("user.view", row)} // optional side-effect
685
+ />
686
+ ```
687
+
688
+ Disable the built-in sheet entirely:
689
+
690
+ ```tsx
691
+ <DataTable viewSheet={false} onView={(row) => router.push(`/users/${row.id}`)} />
692
+ ```
693
+
694
+ `onView` fires before the sheet opens, so you can navigate / log / fetch alongside it.
695
+
696
+ ---
697
+
698
+ ## Delete confirmation
699
+
700
+ Both the row-action "Delete" and the toolbar "Bulk delete" open a confirmation `AlertDialog` by default. The user must confirm before `onDelete` or `onBulkDelete` fires.
701
+
702
+ ```tsx
703
+ <DataTable
704
+ onDelete={(row) => api.deleteUser(row.id)}
705
+ onBulkDelete={(rows) => api.bulkDelete(rows.map(r => r.id))}
706
+ confirmDelete={{
707
+ title: ({ rows, source }) =>
708
+ source === "bulk"
709
+ ? `Delete ${rows.length} users?`
710
+ : `Delete ${rows[0].name}?`,
711
+ description: ({ rows }) =>
712
+ `${rows.length === 1 ? "This user" : "These users"} will be permanently removed. This cannot be undone.`,
713
+ confirmLabel: "Yes, delete",
714
+ cancelLabel: "Keep",
715
+ }}
716
+ />
717
+ ```
718
+
719
+ Skip the dialog (fire immediately):
720
+
721
+ ```tsx
722
+ <DataTable confirmDelete={false} onDelete={(row) => softDelete(row)} />
723
+ ```
724
+
725
+ ---
726
+
613
727
  ## Custom row actions
614
728
 
615
729
  ```tsx
@@ -731,6 +845,10 @@ operations and only renders the returned page.
731
845
  | `density` | `"compact" \| "default" \| "comfortable"` | `"default"` | Row density. |
732
846
  | `theme` | `DataTableTheme` | inherits `:root` | Per-instance CSS-variable overrides. Accepts flat tokens **or** `{ light, dark }`. |
733
847
  | `isolate` | `boolean` | `false` | Ignore the app's `:root` and render with bundled defaults. |
848
+ | `onView` | `(row: TData) => void` | — | Side-effect when "View" is clicked. Fires *before* the sheet opens. |
849
+ | `onDelete` | `(row: TData) => void` | — | Single-row delete handler. Fires *after* the confirm modal (or immediately if `confirmDelete={false}`). |
850
+ | `viewSheet` | `ViewSheetConfig<TData> \| false` | enabled | Configure or disable the built-in View sheet. |
851
+ | `confirmDelete` | `ConfirmDeleteConfig<TData> \| boolean` | `true` | Configure or disable the delete confirmation modal (applies to single + bulk). |
734
852
 
735
853
  `TData` must extend `{ id: string \| number }`.
736
854
 
@@ -801,6 +919,43 @@ import {
801
919
  } from "@dynostack/react-grid"
802
920
  ```
803
921
 
922
+ ```ts
923
+ // View sheet types
924
+ type ViewSheetDensity = "compact" | "relaxed" | "comfy"
925
+
926
+ type ViewSheetConfig<TData> = {
927
+ side?: "right" | "left" | "top" | "bottom"
928
+ defaultDensity?: ViewSheetDensity
929
+ hideDensityTabs?: boolean
930
+ fields?: string[]
931
+ renderField?: (args: {
932
+ column: Column<TData, unknown>
933
+ value: unknown
934
+ row: TData
935
+ }) => React.ReactNode
936
+ renderHeader?: (row: TData) => React.ReactNode
937
+ labels?: {
938
+ title?: (row: TData) => React.ReactNode
939
+ description?: (row: TData) => React.ReactNode
940
+ emptyValue?: string
941
+ density?: { compact?: string; relaxed?: string; comfy?: string }
942
+ }
943
+ }
944
+
945
+ // Confirm-delete types
946
+ type ConfirmDeleteContext<TData> = {
947
+ rows: TData[]
948
+ source: "single" | "bulk"
949
+ }
950
+
951
+ type ConfirmDeleteConfig<TData> = {
952
+ title?: (ctx: ConfirmDeleteContext<TData>) => React.ReactNode
953
+ description?: (ctx: ConfirmDeleteContext<TData>) => React.ReactNode
954
+ confirmLabel?: string
955
+ cancelLabel?: string
956
+ }
957
+ ```
958
+
804
959
  ---
805
960
 
806
961
  ## Compatibility