jattac.libs.web.responsive-table 0.12.0-rc.4 → 0.12.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.
Files changed (3) hide show
  1. package/README.md +71 -12
  2. package/docs/api.md +94 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -144,38 +144,97 @@ For a deep dive into more complex scenarios, see the **[Handling Interactive Ele
144
144
 
145
145
  ## Expandable Rows
146
146
 
147
- Pass `expandRowRenderer` to reveal arbitrary content below any row. Return `null` or `undefined` for rows that should not be expandableonly those rows get a toggle.
147
+ Pass `expandRowRenderer` to reveal arbitrary content below any row. Each expandable row gets a solid chevron toggle (▶ collapsed, ▾ expanded) on a muted blue bar. Returning `null` or `undefined` for a row suppresses its toggle entirelythat row renders flat with no visual affordance.
148
148
 
149
149
  ```tsx
150
150
  <ResponsiveTable
151
151
  data={orders}
152
152
  columnDefinitions={columns}
153
- expandRowRenderer={(order) => (
154
- <OrderLineItems orderId={order.id} />
155
- )}
153
+ expandRowRenderer={(order) => <OrderLineItems orderId={order.id} />}
156
154
  />
157
155
  ```
158
156
 
159
- The renderer receives both the row and its display-order index:
157
+ **Selectively expandable** only rows where the renderer returns content get a toggle:
158
+
159
+ ```tsx
160
+ expandRowRenderer={(order) =>
161
+ order.lineItems.length > 0
162
+ ? <OrderLineItems order={order} />
163
+ : null
164
+ }
165
+ ```
166
+
167
+ **Using `rowIndex`** — the renderer receives both the row object and its current display-order index. Useful for position-based formatting, alternating detail styles, or correlating with a parallel index-aligned array:
160
168
 
161
169
  ```tsx
162
170
  expandRowRenderer={(row, rowIndex) => (
163
- <div>Row {rowIndex}: <pre>{JSON.stringify(row, null, 2)}</pre></div>
171
+ <DetailPanel row={row} zebra={rowIndex % 2 === 0} />
164
172
  )}
165
173
  ```
166
174
 
167
- Selectively expandablerows where the renderer returns `null` render at zero height with no toggle:
175
+ > `rowIndex` is the **display-order** index (post-sort, post-filter) it changes when the user re-sorts. For stable data correlation across renders, use the row's own identifier field (`row.id`, etc.).
176
+
177
+ **Customising the chevron** — override color, size, or any other style via `expandChevronClassName`:
168
178
 
169
179
  ```tsx
170
- expandRowRenderer={(row) =>
171
- row.hasDetails ? <DetailPanel row={row} /> : null
180
+ // Accent color from your design system
181
+ <ResponsiveTable
182
+ expandChevronClassName="my-brand-chevron"
183
+ expandRowRenderer={(row) => <Detail row={row} />}
184
+ ...
185
+ />
186
+ ```
187
+
188
+ ```css
189
+ /* your stylesheet */
190
+ .my-brand-chevron {
191
+ color: #7c3aed; /* purple accent */
192
+ font-size: 2rem;
172
193
  }
173
194
  ```
174
195
 
175
196
  **Recommendations**
176
- - Provide `selectionProps` with a `rowIdKey` when your data has a stable identifier. The expand state is keyed by row ID, so expand/collapse survives re-sorts and filter changes correctly.
177
- - `rowIndex` reflects the current **display order** (post-sort, post-filter). Use `row.id` or equivalent for stable cross-render correlation.
178
- - Detail content is lazy-mounted — the component only renders on first expand, then stays mounted so the collapse animation plays smoothly.
197
+ - Provide `selectionProps` with a `rowIdKey` when your data has a stable identifier. Expand state is keyed by row ID, so open panels survive re-sorts and filter changes.
198
+ - Detail content is **lazy-mounted** — the panel component is not created until first expand, then stays mounted so the collapse animation plays correctly. Heavy components are therefore only instantiated on demand.
199
+ - Expand and `onRowClick` coexist safely — the chevron bar carries `data-rt-ignore-row-click` so tapping the toggle never fires the row click handler.
200
+ - Works identically in both desktop (table row) and mobile (card) layouts.
201
+
202
+ ---
203
+
204
+ ## Row Interaction & Feedback
205
+
206
+ When `onRowClick` or `selectionProps` is provided, the table delivers tactile interaction feedback at every phase of the click:
207
+
208
+ | Phase | Desktop | Mobile |
209
+ | :--- | :--- | :--- |
210
+ | **Hover** | Subtle background lightening | Card lifts 4px with enhanced shadow |
211
+ | **Press (`:active`)** | Background deepens — confirms the press registered | Card compresses back to 1px lift |
212
+ | **Release / Selected** | Background sweeps to the selection tint (150ms transition) | Same tint + primary-color left border |
213
+ | **Keyboard focus** | 2px primary-color outline (`:focus-visible` only — not shown on mouse click) | Same |
214
+
215
+ **Rationale** — each phase is distinct so users receive unambiguous confirmation that their input was received, without animations that feel slow or decorative. The `:active` state is the most critical: it fires in the 80–150ms window of the press itself, before any state change, so even users on slow networks feel an immediate response.
216
+
217
+ **Keyboard navigation** — all clickable rows and cards have `tabIndex=0` and a `:focus-visible` ring. Tab through rows; press Enter or Space to trigger `onRowClick`.
218
+
219
+ **Combining with `onRowClick` and selection:**
220
+
221
+ ```tsx
222
+ <ResponsiveTable
223
+ data={employees}
224
+ columnDefinitions={columns}
225
+ onRowClick={(employee) => navigate(`/employees/${employee.id}`)}
226
+ selectionProps={{
227
+ rowIdKey: 'id',
228
+ mode: 'multiple',
229
+ onSelectionChange: (selected) => setSelected(selected),
230
+ }}
231
+ />
232
+ ```
233
+
234
+ **Best practices**
235
+ - Always pair `onRowClick` with a visible hover state cue (the default cursor change handles this automatically).
236
+ - If rows contain buttons or links, use `data-rt-ignore-row-click` on those elements so inner interactions don't also trigger the row handler.
237
+ - For selection, always provide `rowIdKey` pointing to a stable unique field — this ensures expand state and selection state both survive sort/filter changes.
179
238
 
180
239
  ---
181
240
 
package/docs/api.md CHANGED
@@ -36,6 +36,52 @@ This document contains the exhaustive technical specification for the Responsive
36
36
  | `onPageChange` | `(page: number) => void` | Callback fired when the current page changes. |
37
37
  | `onDataSourceError` | `(error: Error) => void` | Callback fired when a dataSource fetch fails. |
38
38
  | `expandRowRenderer` | `(row: TData, rowIndex: number) => ReactNode` | Renders collapsible detail content below a row. Return `null`/`undefined` for no toggle on that row. |
39
+ | `expandChevronClassName` | `string` | Custom CSS class applied to the chevron icon `<span>`. Use to override color, size, or any other style. |
40
+
41
+ ### Row Interaction & Visual Feedback (`onRowClick`)
42
+
43
+ When `onRowClick` or `selectionProps` is provided, the table applies a full interaction state machine to every clickable row — no additional configuration required.
44
+
45
+ | State | Desktop (`<tr>`) | Mobile (card) |
46
+ | :--- | :--- | :--- |
47
+ | **Default** | Stripe / base background | Card with subtle shadow |
48
+ | **Hover** | Background lightens (`--table-row-hover-bg`) | Card lifts 4px, shadow deepens |
49
+ | **Active (press)** | Background deepens to `#dde3ea` (80ms) | Card compresses to 1px lift, shadow reduces |
50
+ | **Selected** | Background sweeps to `#e7f1ff` (150ms transition) | Same tint + 4px primary-color left border |
51
+ | **Focus (keyboard)** | 2px primary-color `outline` (`:focus-visible` only) | Same |
52
+
53
+ All clickable rows and cards receive `tabIndex=0`, making the table fully keyboard-navigable. Press **Tab** to move between rows, **Enter** or **Space** to trigger `onRowClick`.
54
+
55
+ **Use cases**
56
+ - Navigation: `onRowClick={(row) => navigate(`/detail/${row.id}`)}`
57
+ - Inline drawer or modal trigger
58
+ - Combined with `selectionProps` for multi-select workflows
59
+
60
+ ```tsx
61
+ // Navigation on click
62
+ <ResponsiveTable
63
+ data={invoices}
64
+ columnDefinitions={columns}
65
+ onRowClick={(invoice) => router.push(`/invoices/${invoice.id}`)}
66
+ />
67
+
68
+ // Selection + click
69
+ <ResponsiveTable
70
+ data={employees}
71
+ columnDefinitions={columns}
72
+ selectionProps={{
73
+ rowIdKey: 'id',
74
+ mode: 'multiple',
75
+ onSelectionChange: (selected) => setSelected(selected),
76
+ }}
77
+ onRowClick={(employee) => setPreview(employee)}
78
+ />
79
+ ```
80
+
81
+ **Best practices**
82
+ - Use `data-rt-ignore-row-click` on buttons and links inside cells so inner interactions do not also fire the row handler — see [Handling Interactive Elements](./handling-interactive-elements.md).
83
+ - Always provide `selectionProps.rowIdKey` pointing to a stable unique field when using selection — prevents state drift when rows are sorted or filtered.
84
+ - The `:active` feedback is immediate by design (80ms transition-in). Do not suppress it via CSS — it is the primary signal that the press registered before any async work begins.
39
85
 
40
86
  ### IResponsiveTableColumnDefinition
41
87
  The primary configuration interface for defining column-level behavior and rendering logic.
@@ -83,13 +129,15 @@ interface IResponsiveTableColumnDefinition<TData> {
83
129
  />
84
130
  ```
85
131
 
86
- ### Expandable Rows (`expandRowRenderer`)
132
+ ### Expandable Rows (`expandRowRenderer`, `expandChevronClassName`)
133
+
134
+ #### `expandRowRenderer`
87
135
 
88
136
  ```typescript
89
137
  expandRowRenderer?: (row: TData, rowIndex: number) => ReactNode
90
138
  ```
91
139
 
92
- Attaches a collapsible detail panel below each row. The renderer is called for every row on each render — return `null` or `undefined` to suppress the toggle for that row; return a `ReactNode` to show a `+`/`−` toggle that animates the panel open and closed.
140
+ Attaches a collapsible detail panel below each row. A solid chevron indicator (▶ collapsed, ▾ expanded) sits on a muted blue bar below the row. The renderer is called for every row — return `null` or `undefined` to suppress the toggle entirely for that row.
93
141
 
94
142
  ```tsx
95
143
  // All rows expandable
@@ -99,7 +147,7 @@ Attaches a collapsible detail panel below each row. The renderer is called for e
99
147
  expandRowRenderer={(order) => <OrderLineItems orderId={order.id} />}
100
148
  />
101
149
 
102
- // Selectively expandable
150
+ // Selectively expandable — only rows with line items get a toggle
103
151
  <ResponsiveTable
104
152
  data={orders}
105
153
  columnDefinitions={columns}
@@ -108,21 +156,57 @@ Attaches a collapsible detail panel below each row. The renderer is called for e
108
156
  }
109
157
  />
110
158
 
111
- // Using rowIndex for position-based logic
159
+ // rowIndex for position-based logic (zebra detail panels, animation offsets)
112
160
  <ResponsiveTable
113
- data={rows}
161
+ data={employees}
114
162
  columnDefinitions={columns}
115
163
  expandRowRenderer={(row, rowIndex) => (
116
- <DetailPanel row={row} position={rowIndex} />
164
+ <EmployeeDetail employee={row} zebra={rowIndex % 2 === 0} />
117
165
  )}
118
166
  />
119
167
  ```
120
168
 
169
+ **Use cases**
170
+ - Master–detail layouts (orders → line items, users → permissions, assets → history)
171
+ - Inline editing panels without navigating away
172
+ - Progressive disclosure of verbose data fields that would clutter the main row
173
+ - Nested tables or charts scoped to a single row
174
+
121
175
  **Behaviour**
122
- - Detail content is **lazy-mounted**: the component is not rendered until first expand, then stays mounted so the collapse animation plays correctly.
123
- - Expand state is keyed by row ID (`selectionProps.rowIdKey` when provided, otherwise array index). Providing a stable `rowIdKey` ensures expand state survives re-sorts and filter changes.
124
- - `rowIndex` is the **display-order** index (post-sort, post-filter). Use the row object's own identifier for stable cross-render data correlation.
125
- - Works in both desktop (table) and mobile (card) layouts.
176
+ - Detail content is **lazy-mounted**: the panel component is not created until first expand, then stays mounted so the collapse animation plays correctly. Heavy components are instantiated on demand.
177
+ - Expand state is keyed by `selectionProps.rowIdKey` when provided, otherwise falls back to array index. A stable key survives re-sorts and filter changes.
178
+ - `rowIndex` is the **display-order** index (post-sort, post-filter) — not a stable identifier. Use the row object's own field for cross-render correlation.
179
+ - The chevron bar carries `data-rt-ignore-row-click` tapping the toggle never fires `onRowClick`.
180
+ - Works identically in desktop (table `<tr>`) and mobile (card) layouts.
181
+
182
+ #### `expandChevronClassName`
183
+
184
+ ```typescript
185
+ expandChevronClassName?: string
186
+ ```
187
+
188
+ Custom CSS class applied to the chevron icon `<span>`. The chevron defaults to `2.2rem` in the primary color. Use this prop to override any style without forking the component.
189
+
190
+ ```tsx
191
+ // Brand color override
192
+ <ResponsiveTable
193
+ expandChevronClassName={styles.brandChevron}
194
+ expandRowRenderer={(row) => <Detail row={row} />}
195
+ ...
196
+ />
197
+ ```
198
+
199
+ ```css
200
+ .brandChevron {
201
+ color: #7c3aed; /* your design-system accent */
202
+ font-size: 1.8rem; /* smaller if the default is too prominent */
203
+ }
204
+ ```
205
+
206
+ **Best practices**
207
+ - Override `color` to match your design token rather than using hardcoded hex — keeps theming consistent.
208
+ - Avoid overriding `transform` or `transition` — these drive the collapse animation.
209
+ - Pair with `--primary-color` CSS variable if your app uses it; the default chevron already respects it.
126
210
 
127
211
  ### DataSource Types
128
212
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jattac.libs.web.responsive-table",
3
- "version": "0.12.0-rc.4",
3
+ "version": "0.12.0",
4
4
  "description": "A fully responsive, customizable, and lightweight React table component with a modern, mobile-first design and a powerful plugin system.",
5
5
  "author": {
6
6
  "name": "Nyingi Maina",