jattac.libs.web.responsive-table 0.12.0-rc.4 → 0.13.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 CHANGED
@@ -18,6 +18,19 @@ npm install jattac.libs.web.responsive-table jattac.libs.web.zest-textbox react-
18
18
 
19
19
  ---
20
20
 
21
+ ## Table of Contents
22
+
23
+ * [Built-in Filter](#built-in-filter)
24
+ * [Delightful Data Fetching: Smart Data Source](#delightful-data-fetching-smart-data-source)
25
+ * [Basic Implementation](#basic-implementation)
26
+ * [Handling Interactive Elements](#handling-interactive-elements)
27
+ * [Expandable Rows](#expandable-rows)
28
+ * [Row Interaction & Feedback](#row-interaction--feedback)
29
+ * [Loading States & Animations](#loading-states--animations)
30
+ * [Documentation Directory](#documentation-directory)
31
+
32
+ ---
33
+
21
34
  ## Built-in Filter
22
35
 
23
36
  Enable the search box with one prop. A clear (×) button appears automatically when the field has text.
@@ -144,38 +157,102 @@ For a deep dive into more complex scenarios, see the **[Handling Interactive Ele
144
157
 
145
158
  ## Expandable Rows
146
159
 
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.
160
+ Pass `expandRowRenderer` to reveal arbitrary content below any row. Each expandable row gets a chevron in a dedicated left column (▶ collapsed, ▾ expanded). Returning `null` or `undefined` for a row suppresses its toggle entirelythat row renders flat with no visual affordance.
148
161
 
149
162
  ```tsx
150
163
  <ResponsiveTable
151
164
  data={orders}
152
165
  columnDefinitions={columns}
153
- expandRowRenderer={(order) => (
154
- <OrderLineItems orderId={order.id} />
155
- )}
166
+ expandRowRenderer={(order) => <OrderLineItems orderId={order.id} />}
156
167
  />
157
168
  ```
158
169
 
159
- The renderer receives both the row and its display-order index:
170
+ **Selectively expandable** only rows where the renderer returns content get a toggle:
171
+
172
+ ```tsx
173
+ expandRowRenderer={(order) =>
174
+ order.lineItems.length > 0
175
+ ? <OrderLineItems order={order} />
176
+ : null
177
+ }
178
+ ```
179
+
180
+ **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
181
 
161
182
  ```tsx
162
183
  expandRowRenderer={(row, rowIndex) => (
163
- <div>Row {rowIndex}: <pre>{JSON.stringify(row, null, 2)}</pre></div>
184
+ <DetailPanel row={row} zebra={rowIndex % 2 === 0} />
164
185
  )}
165
186
  ```
166
187
 
167
- Selectively expandablerows where the renderer returns `null` render at zero height with no toggle:
188
+ > `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.).
189
+
190
+ **Customising the chevron** — override color, size, or any other style via `expandChevronClassName`. Do not override `transform` or `transition` — these drive the rotation animation.
168
191
 
169
192
  ```tsx
170
- expandRowRenderer={(row) =>
171
- row.hasDetails ? <DetailPanel row={row} /> : null
193
+ // Accent color from your design system
194
+ <ResponsiveTable
195
+ expandChevronClassName="my-brand-chevron"
196
+ expandRowRenderer={(row) => <Detail row={row} />}
197
+ ...
198
+ />
199
+ ```
200
+
201
+ ```css
202
+ /* your stylesheet */
203
+ .my-brand-chevron {
204
+ color: #7c3aed; /* purple accent */
205
+ font-size: 2rem;
172
206
  }
173
207
  ```
174
208
 
175
- **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.
209
+ **Behaviour**
210
+ - Chevron sits in a dedicated 2rem left column not as an overlay or pseudo-element keeping the data columns aligned.
211
+ - **Idle:** chevron at 25% opacity, rotates right (`-90deg`). Quiet, non-intrusive.
212
+ - **Hover:** chevron smooths to 60% opacity, faint border accent on the row.
213
+ - **Expanded:** chevron rotates down (`0deg`), full opacity. A toggle bar slides in (0→2rem) at the top of the detail pane.
214
+ - **Greeting animation:** on mount, chevrons pop in with a staggered multi-pulse wave (2.2s), then settle to idle. Plays once per component lifecycle.
215
+ - Expand and `onRowClick` coexist safely — the chevron carries `data-rt-ignore-row-click`.
216
+ - Works identically in both desktop (table row) and mobile (card) layouts.
217
+
218
+ For the full feature reference — expansion state keying, lazy mounting, keyboard accessibility, `dataSource` compatibility, common patterns, CSS customization, and pitfalls — see the **[Row Expansion and Collapse Guide](./docs/expand-collapse.md)**.
219
+
220
+ ---
221
+
222
+ ## Row Interaction & Feedback
223
+
224
+ When `onRowClick` or `selectionProps` is provided, the table delivers tactile interaction feedback at every phase of the click:
225
+
226
+ | Phase | Desktop | Mobile |
227
+ | :--- | :--- | :--- |
228
+ | **Hover** | Subtle background lightening | Card lifts 4px with enhanced shadow |
229
+ | **Press (`:active`)** | Background deepens — confirms the press registered | Card compresses back to 1px lift |
230
+ | **Release / Selected** | Background sweeps to the selection tint (150ms transition) | Same tint + primary-color left border |
231
+ | **Keyboard focus** | 2px primary-color outline (`:focus-visible` only — not shown on mouse click) | Same |
232
+
233
+ **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.
234
+
235
+ **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`.
236
+
237
+ **Combining with `onRowClick` and selection:**
238
+
239
+ ```tsx
240
+ <ResponsiveTable
241
+ data={employees}
242
+ columnDefinitions={columns}
243
+ onRowClick={(employee) => navigate(`/employees/${employee.id}`)}
244
+ selectionProps={{
245
+ rowIdKey: 'id',
246
+ mode: 'multiple',
247
+ onSelectionChange: (selected) => setSelected(selected),
248
+ }}
249
+ />
250
+ ```
251
+
252
+ **Best practices**
253
+ - Always pair `onRowClick` with a visible hover state cue (the default cursor change handles this automatically).
254
+ - 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.
255
+ - For selection, always provide `rowIdKey` pointing to a stable unique field — this ensures expand state and selection state both survive sort/filter changes.
179
256
 
180
257
  ---
181
258
 
@@ -208,6 +285,8 @@ The following technical documentation provides comprehensive implementation guid
208
285
  4. **[Configuration Specification](./docs/configuration.md)** - Detailed guidance on performance tuning and UI customization.
209
286
  5. **[Architecture and Contribution Guide](./docs/development.md)** - Internal system design and development environment setup.
210
287
  6. **[Handling Interactive Elements](./docs/handling-interactive-elements.md)** - Guide on preventing row click bubbling for buttons and custom components.
288
+ 7. **[Row Expansion and Collapse](./docs/expand-collapse.md)** - Comprehensive guide for expandable row detail panels.
289
+ 8. **[Recommendations and Pitfalls](./docs/recommendations.md)** - Best practices, anti-patterns, and ESM/CJS module compatibility guide.
211
290
 
212
291
  ---
213
292
  **Next Step:** [Review the Technical Implementation Guide](./docs/examples.md)
@@ -1,12 +1,5 @@
1
- import { ReactNode } from 'react';
2
1
  import { IResponsiveTablePlugin } from '../Plugins/IResponsiveTablePlugin';
3
2
  import { IResponsiveTableColumnDefinition, SortDirection } from '../Data/IResponsiveTableColumnDefinition';
4
- interface IInfiniteScrollProps<TData> {
5
- onLoadMore: (currentData: TData[]) => Promise<TData[] | null>;
6
- hasMore?: boolean;
7
- loadingMoreComponent?: ReactNode;
8
- noMoreDataComponent?: ReactNode;
9
- }
10
3
  interface UseTablePluginsProps<TData> {
11
4
  data: TData[];
12
5
  plugins?: IResponsiveTablePlugin<TData>[];
@@ -28,7 +21,6 @@ interface UseTablePluginsProps<TData> {
28
21
  };
29
22
  columnDefinitions: (IResponsiveTableColumnDefinition<TData> | ((data: TData, rowIndex?: number) => IResponsiveTableColumnDefinition<TData>))[];
30
23
  getScrollableElement: () => HTMLElement | null;
31
- infiniteScrollProps?: IInfiniteScrollProps<TData>;
32
24
  onFilterChange?: (filterText: string) => void;
33
25
  }
34
26
  interface UseTablePluginsReturn<TData> {
@@ -18,12 +18,6 @@ export interface IPluginAPI<TData> {
18
18
  forceUpdate: () => void;
19
19
  columnDefinitions: ColumnDefinition<TData>[];
20
20
  getScrollableElement?: () => HTMLElement | null;
21
- infiniteScrollProps?: {
22
- onLoadMore: (currentData: TData[]) => Promise<TData[] | null>;
23
- hasMore?: boolean;
24
- loadingMoreComponent?: ReactNode;
25
- noMoreDataComponent?: ReactNode;
26
- };
27
21
  filterProps?: {
28
22
  showFilter?: boolean;
29
23
  filterPlaceholder?: string;
@@ -6,7 +6,6 @@ interface DetailRowProps<TData> {
6
6
  expandRowRenderer: (row: TData, rowIndex: number) => React.ReactNode;
7
7
  isExpanded: boolean;
8
8
  onToggle: () => void;
9
- expandChevronClassName?: string;
10
9
  }
11
- export declare function DetailRow<TData>({ row, rowIndex, colSpan, expandRowRenderer, isExpanded, onToggle, expandChevronClassName }: DetailRowProps<TData>): React.JSX.Element;
10
+ export declare function DetailRow<TData>({ row, rowIndex, colSpan, expandRowRenderer, isExpanded, onToggle }: DetailRowProps<TData>): React.JSX.Element;
12
11
  export {};
@@ -10,12 +10,6 @@ export interface ResponsiveTableHandle<TData> {
10
10
  resetAndFetch: () => Promise<void>;
11
11
  getState: () => DataSourceState<TData>;
12
12
  }
13
- interface IInfiniteScrollProps<TData> {
14
- onLoadMore: (currentData: TData[]) => Promise<TData[] | null>;
15
- hasMore?: boolean;
16
- loadingMoreComponent?: ReactNode;
17
- noMoreDataComponent?: ReactNode;
18
- }
19
13
  interface ISortProps {
20
14
  initialSortColumn?: string;
21
15
  initialSortDirection?: SortDirection;
@@ -62,11 +56,6 @@ interface IProps<TData> {
62
56
  plugins?: IResponsiveTablePlugin<TData>[];
63
57
  /** If true, the header will stick to the top of the page when scrolling. */
64
58
  enablePageLevelStickyHeader?: boolean;
65
- /**
66
- * Props for manual infinite scroll handling.
67
- * NOTE: Prefer using `dataSource` for a more seamless experience.
68
- */
69
- infiniteScrollProps?: IInfiniteScrollProps<TData>;
70
59
  /** Configuration for the built-in filter plugin. */
71
60
  filterProps?: {
72
61
  showFilter?: boolean;
@@ -32,6 +32,12 @@ interface TableBodyRowProps<TData> {
32
32
  isLoading?: boolean;
33
33
  animateOnLoad?: boolean;
34
34
  };
35
+ /** Optional expand chevron cell rendered as the first column */
36
+ expandCell?: React.ReactNode;
37
+ /** Mouse enter handler for hover tracking */
38
+ onMouseEnter?: () => void;
39
+ /** Mouse leave handler for hover tracking */
40
+ onMouseLeave?: () => void;
35
41
  }
36
42
  export declare function TableBodyRow<TData>(props: TableBodyRowProps<TData>): React.JSX.Element;
37
43
  export {};
package/dist/index.d.ts CHANGED
@@ -5,9 +5,8 @@ import ResponsiveTable, { ResponsiveTableHandle } from './UI/ResponsiveTable';
5
5
  import { ColumnDefinition, DataSource, IDataSourceParams, DataSourceResult, useTableContext } from './Context/TableContext';
6
6
  import { DataSourceState } from './Hooks/useTableDataSource';
7
7
  import { FilterPlugin } from './Plugins/FilterPlugin';
8
- import { InfiniteScrollPlugin } from './Plugins/InfiniteScrollPlugin';
9
8
  import { IResponsiveTablePlugin } from './Plugins/IResponsiveTablePlugin';
10
9
  import { SortPlugin } from './Plugins/SortPlugin';
11
10
  import { SelectionPlugin } from './Plugins/SelectionPlugin';
12
- export { SortDirection, IResponsiveTableColumnDefinition, ColumnDefinition, DataSource, IDataSourceParams, DataSourceResult, IFooterColumnDefinition, IFooterRowDefinition, FilterPlugin, InfiniteScrollPlugin, IResponsiveTablePlugin, SortPlugin, SelectionPlugin, ResponsiveTableHandle, DataSourceState, useTableContext, };
11
+ export { SortDirection, IResponsiveTableColumnDefinition, ColumnDefinition, DataSource, IDataSourceParams, DataSourceResult, IFooterColumnDefinition, IFooterRowDefinition, FilterPlugin, IResponsiveTablePlugin, SortPlugin, SelectionPlugin, ResponsiveTableHandle, DataSourceState, useTableContext, };
13
12
  export default ResponsiveTable;