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.
- package/README.md +71 -12
- package/docs/api.md +94 -10
- 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.
|
|
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 entirely — that 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
|
-
|
|
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
|
-
<
|
|
171
|
+
<DetailPanel row={row} zebra={rowIndex % 2 === 0} />
|
|
164
172
|
)}
|
|
165
173
|
```
|
|
166
174
|
|
|
167
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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.
|
|
177
|
-
-
|
|
178
|
-
-
|
|
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
|
|
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
|
-
//
|
|
159
|
+
// rowIndex for position-based logic (zebra detail panels, animation offsets)
|
|
112
160
|
<ResponsiveTable
|
|
113
|
-
data={
|
|
161
|
+
data={employees}
|
|
114
162
|
columnDefinitions={columns}
|
|
115
163
|
expandRowRenderer={(row, rowIndex) => (
|
|
116
|
-
<
|
|
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
|
|
123
|
-
- Expand state is keyed by
|
|
124
|
-
- `rowIndex` is the **display-order** index (post-sort, post-filter). Use the row object's own
|
|
125
|
-
-
|
|
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
|
|
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",
|