@zvndev/yable-react 0.6.0 → 0.6.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 +53 -35
- package/dist/index.cjs +325 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -5
- package/dist/index.d.ts +36 -5
- package/dist/index.js +325 -56
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -73,37 +73,37 @@ const table = useTable({
|
|
|
73
73
|
|
|
74
74
|
### Layout Components
|
|
75
75
|
|
|
76
|
-
| Component
|
|
77
|
-
|
|
78
|
-
| `Table`
|
|
79
|
-
| `TableHeader` | Renders `<thead>` with header groups and sort indicators.
|
|
80
|
-
| `TableBody`
|
|
81
|
-
| `TableCell`
|
|
82
|
-
| `TableFooter` | Renders `<tfoot>` with footer content.
|
|
76
|
+
| Component | Description |
|
|
77
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
78
|
+
| `Table` | Root container -- wraps everything in a `<div>` with a desktop table or adaptive card layout inside. Accepts `table`, `striped`, `bordered`, `compact`, `stickyHeader`, `theme`, `loading`, `emptyMessage`, `footer`, `adaptiveLayout`, and `children` props. |
|
|
79
|
+
| `TableHeader` | Renders `<thead>` with header groups and sort indicators. |
|
|
80
|
+
| `TableBody` | Renders `<tbody>` with rows and cells. |
|
|
81
|
+
| `TableCell` | Renders a single `<td>` with editing support. |
|
|
82
|
+
| `TableFooter` | Renders `<tfoot>` with footer content. |
|
|
83
83
|
|
|
84
84
|
### Interactive Components
|
|
85
85
|
|
|
86
|
-
| Component
|
|
87
|
-
|
|
88
|
-
| `Pagination`
|
|
89
|
-
| `GlobalFilter`
|
|
90
|
-
| `SortIndicator` | Sort direction arrow icon. Props: `direction`, `index` (for multi-sort badge).
|
|
86
|
+
| Component | Description |
|
|
87
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
88
|
+
| `Pagination` | Page navigation with first/last/prev/next buttons, page numbers, and page size selector. Props: `table`, `showPageSize`, `pageSizes`, `showInfo`, `showFirstLast`. |
|
|
89
|
+
| `GlobalFilter` | Debounced search input for the global filter. Props: `table`, `placeholder`, `debounce`, `className`. |
|
|
90
|
+
| `SortIndicator` | Sort direction arrow icon. Props: `direction`, `index` (for multi-sort badge). |
|
|
91
91
|
|
|
92
92
|
### Form Components (In-Cell Editing)
|
|
93
93
|
|
|
94
|
-
| Component
|
|
95
|
-
|
|
96
|
-
| `CellInput`
|
|
97
|
-
| `CellSelect`
|
|
98
|
-
| `CellCheckbox`
|
|
99
|
-
| `CellToggle`
|
|
100
|
-
| `CellDatePicker` | Date input for cell editing.
|
|
94
|
+
| Component | Description |
|
|
95
|
+
| ---------------- | --------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `CellInput` | Text/number input for cell editing. Props: `context`, `type`, `placeholder`, `inline`, `autoFocus`. |
|
|
97
|
+
| `CellSelect` | Dropdown select for cell editing. |
|
|
98
|
+
| `CellCheckbox` | Checkbox for boolean cell values. |
|
|
99
|
+
| `CellToggle` | Toggle switch for boolean cell values. |
|
|
100
|
+
| `CellDatePicker` | Date input for cell editing. |
|
|
101
101
|
|
|
102
102
|
### Context
|
|
103
103
|
|
|
104
|
-
| Export
|
|
105
|
-
|
|
106
|
-
| `TableProvider`
|
|
104
|
+
| Export | Description |
|
|
105
|
+
| ------------------- | ----------------------------------------------------------- |
|
|
106
|
+
| `TableProvider` | React context provider for the table instance. |
|
|
107
107
|
| `useTableContext()` | Hook to access the table instance from any child component. |
|
|
108
108
|
|
|
109
109
|
## Re-exports from @zvndev/yable-core
|
|
@@ -123,23 +123,41 @@ The `<Table>` component accepts these props:
|
|
|
123
123
|
|
|
124
124
|
```typescript
|
|
125
125
|
interface TableProps<TData> {
|
|
126
|
-
table: Table<TData>
|
|
127
|
-
stickyHeader?: boolean
|
|
128
|
-
striped?: boolean
|
|
129
|
-
bordered?: boolean
|
|
130
|
-
compact?: boolean
|
|
131
|
-
theme?: string
|
|
132
|
-
clickableRows?: boolean
|
|
133
|
-
footer?: boolean
|
|
134
|
-
loading?: boolean
|
|
135
|
-
emptyMessage?: string
|
|
136
|
-
renderEmpty?: () => ReactNode
|
|
126
|
+
table: Table<TData> // Required -- the table instance from useTable
|
|
127
|
+
stickyHeader?: boolean // Pin header to top on scroll
|
|
128
|
+
striped?: boolean // Alternate row backgrounds
|
|
129
|
+
bordered?: boolean // Add cell borders
|
|
130
|
+
compact?: boolean // Reduce padding
|
|
131
|
+
theme?: string // Theme variant name
|
|
132
|
+
clickableRows?: boolean // Add pointer cursor + hover to rows
|
|
133
|
+
footer?: boolean // Show table footer
|
|
134
|
+
loading?: boolean // Show loading overlay
|
|
135
|
+
emptyMessage?: string // Text when no rows (default: "No data")
|
|
136
|
+
renderEmpty?: () => ReactNode // Custom empty state
|
|
137
137
|
renderLoading?: () => ReactNode // Custom loading state
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
adaptiveLayout?: boolean | AdaptiveTableLayoutOptions<TData>
|
|
139
|
+
children?: ReactNode // Extra content (e.g. Pagination)
|
|
140
|
+
className?: string // Additional CSS class
|
|
140
141
|
}
|
|
141
142
|
```
|
|
142
143
|
|
|
144
|
+
### Adaptive tablet and mobile layouts
|
|
145
|
+
|
|
146
|
+
Use `adaptiveLayout` when the same table instance should become a structural card layout on narrower containers instead of only scrolling horizontally.
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<Table
|
|
150
|
+
table={table}
|
|
151
|
+
adaptiveLayout={{
|
|
152
|
+
breakpoint: 720,
|
|
153
|
+
primaryColumnId: 'name',
|
|
154
|
+
secondaryColumnIds: ['status', 'owner', 'updatedAt'],
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Set `mode: 'cards'` or `mode: 'table'` to force a surface, or provide `renderCard` for a product-specific mobile layout while still receiving the row, cells, and table instance.
|
|
160
|
+
|
|
143
161
|
## License
|
|
144
162
|
|
|
145
163
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -2218,6 +2218,62 @@ function FillHandle({
|
|
|
2218
2218
|
}
|
|
2219
2219
|
);
|
|
2220
2220
|
}
|
|
2221
|
+
function renderCellContent(cell, table) {
|
|
2222
|
+
const column = cell.column;
|
|
2223
|
+
const isEditing = cell.getIsEditing();
|
|
2224
|
+
const isAlwaysEditable = cell.getIsAlwaysEditable();
|
|
2225
|
+
const cellStatus = table.getCellStatus(cell.row.id, column.id);
|
|
2226
|
+
const overrideValue = cellStatus !== "idle" ? table.getCellRenderValue(cell.row.id, column.id) : void 0;
|
|
2227
|
+
let content;
|
|
2228
|
+
const cellDef = column.columnDef.cell;
|
|
2229
|
+
const cellType = column.columnDef.cellType;
|
|
2230
|
+
if (typeof cellDef === "function") {
|
|
2231
|
+
const ctx = cell.getContext();
|
|
2232
|
+
if (overrideValue !== void 0) {
|
|
2233
|
+
const overriddenCtx = {
|
|
2234
|
+
...ctx,
|
|
2235
|
+
getValue: () => overrideValue,
|
|
2236
|
+
renderValue: () => overrideValue
|
|
2237
|
+
};
|
|
2238
|
+
content = cellDef(overriddenCtx);
|
|
2239
|
+
} else {
|
|
2240
|
+
content = cellDef(ctx);
|
|
2241
|
+
}
|
|
2242
|
+
} else if (cellType && !(isEditing || isAlwaysEditable)) {
|
|
2243
|
+
content = resolveCellType(cellType, cell.getContext(), column.columnDef.cellTypeProps);
|
|
2244
|
+
} else {
|
|
2245
|
+
content = overrideValue !== void 0 ? overrideValue : cell.renderValue();
|
|
2246
|
+
}
|
|
2247
|
+
if (!cell.row.getIsGrouped()) return content;
|
|
2248
|
+
if (column.id === cell.row.groupingColumnId) {
|
|
2249
|
+
const expanded = cell.row.getIsExpanded();
|
|
2250
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "yable-group-cell", style: { paddingLeft: cell.row.depth * 16 }, children: [
|
|
2251
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2252
|
+
"button",
|
|
2253
|
+
{
|
|
2254
|
+
type: "button",
|
|
2255
|
+
className: "yable-group-toggle",
|
|
2256
|
+
"aria-label": expanded ? "Collapse group" : "Expand group",
|
|
2257
|
+
"aria-expanded": expanded,
|
|
2258
|
+
onClick: cell.row.getToggleExpandedHandler(),
|
|
2259
|
+
children: expanded ? "\u25BE" : "\u25B8"
|
|
2260
|
+
}
|
|
2261
|
+
),
|
|
2262
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "yable-group-value", children: String(cell.row.groupingValue ?? "") }),
|
|
2263
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "yable-group-count", children: [
|
|
2264
|
+
"(",
|
|
2265
|
+
cell.row.getLeafRows().length,
|
|
2266
|
+
")"
|
|
2267
|
+
] })
|
|
2268
|
+
] });
|
|
2269
|
+
}
|
|
2270
|
+
const aggDef = column.columnDef.aggregatedCell;
|
|
2271
|
+
if (typeof aggDef === "function") {
|
|
2272
|
+
return aggDef(cell.getContext());
|
|
2273
|
+
}
|
|
2274
|
+
const aggVal = cell.getValue();
|
|
2275
|
+
return aggVal == null ? null : aggVal;
|
|
2276
|
+
}
|
|
2221
2277
|
function TableCell({
|
|
2222
2278
|
cell,
|
|
2223
2279
|
table,
|
|
@@ -2253,60 +2309,8 @@ function TableCell({
|
|
|
2253
2309
|
const selectionEdges = table.getCellSelectionEdges(rowIndex, columnIndex);
|
|
2254
2310
|
const isCellSelected = selectionEdges !== null;
|
|
2255
2311
|
const isMultiCellSelection = selectionRange !== null && (selectionRange.start.rowIndex !== selectionRange.end.rowIndex || selectionRange.start.columnIndex !== selectionRange.end.columnIndex);
|
|
2256
|
-
const overrideValue = cellStatus !== "idle" ? table.getCellRenderValue(cell.row.id, column.id) : void 0;
|
|
2257
|
-
let content;
|
|
2258
|
-
const cellDef = column.columnDef.cell;
|
|
2259
|
-
const cellType = column.columnDef.cellType;
|
|
2260
|
-
if (typeof cellDef === "function") {
|
|
2261
|
-
const ctx = cell.getContext();
|
|
2262
|
-
if (overrideValue !== void 0) {
|
|
2263
|
-
const overriddenCtx = {
|
|
2264
|
-
...ctx,
|
|
2265
|
-
getValue: () => overrideValue,
|
|
2266
|
-
renderValue: () => overrideValue
|
|
2267
|
-
};
|
|
2268
|
-
content = cellDef(overriddenCtx);
|
|
2269
|
-
} else {
|
|
2270
|
-
content = cellDef(ctx);
|
|
2271
|
-
}
|
|
2272
|
-
} else if (cellType && !(isEditing || isAlwaysEditable)) {
|
|
2273
|
-
content = resolveCellType(cellType, cell.getContext(), column.columnDef.cellTypeProps);
|
|
2274
|
-
} else {
|
|
2275
|
-
content = overrideValue !== void 0 ? overrideValue : cell.renderValue();
|
|
2276
|
-
}
|
|
2277
2312
|
const isGroupRow = cell.row.getIsGrouped();
|
|
2278
|
-
|
|
2279
|
-
if (column.id === cell.row.groupingColumnId) {
|
|
2280
|
-
const expanded = cell.row.getIsExpanded();
|
|
2281
|
-
content = /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "yable-group-cell", style: { paddingLeft: cell.row.depth * 16 }, children: [
|
|
2282
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2283
|
-
"button",
|
|
2284
|
-
{
|
|
2285
|
-
type: "button",
|
|
2286
|
-
className: "yable-group-toggle",
|
|
2287
|
-
"aria-label": expanded ? "Collapse group" : "Expand group",
|
|
2288
|
-
"aria-expanded": expanded,
|
|
2289
|
-
onClick: cell.row.getToggleExpandedHandler(),
|
|
2290
|
-
children: expanded ? "\u25BE" : "\u25B8"
|
|
2291
|
-
}
|
|
2292
|
-
),
|
|
2293
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "yable-group-value", children: String(cell.row.groupingValue ?? "") }),
|
|
2294
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "yable-group-count", children: [
|
|
2295
|
-
"(",
|
|
2296
|
-
cell.row.getLeafRows().length,
|
|
2297
|
-
")"
|
|
2298
|
-
] })
|
|
2299
|
-
] });
|
|
2300
|
-
} else {
|
|
2301
|
-
const aggDef = column.columnDef.aggregatedCell;
|
|
2302
|
-
if (typeof aggDef === "function") {
|
|
2303
|
-
content = aggDef(cell.getContext());
|
|
2304
|
-
} else {
|
|
2305
|
-
const aggVal = cell.getValue();
|
|
2306
|
-
content = aggVal == null ? null : aggVal;
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2313
|
+
const content = renderCellContent(cell, table);
|
|
2310
2314
|
const handleClick = React4.useCallback(
|
|
2311
2315
|
(e) => {
|
|
2312
2316
|
if (cell.row.getIsGrouped()) return;
|
|
@@ -2871,6 +2875,202 @@ function isInteractiveClickTarget2(target) {
|
|
|
2871
2875
|
target.closest('input, textarea, select, button, a[href], [contenteditable="true"]')
|
|
2872
2876
|
);
|
|
2873
2877
|
}
|
|
2878
|
+
function AdaptiveTableCards({
|
|
2879
|
+
table,
|
|
2880
|
+
layout,
|
|
2881
|
+
clickableRows
|
|
2882
|
+
}) {
|
|
2883
|
+
const rows = table.getRowModel().rows;
|
|
2884
|
+
const visibleColumns = table.getVisibleLeafColumns();
|
|
2885
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "yable-adaptive-cards", role: "rowgroup", children: rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2886
|
+
AdaptiveTableCard,
|
|
2887
|
+
{
|
|
2888
|
+
row,
|
|
2889
|
+
table,
|
|
2890
|
+
rowIndex,
|
|
2891
|
+
visibleColumns,
|
|
2892
|
+
layout,
|
|
2893
|
+
clickable: clickableRows
|
|
2894
|
+
},
|
|
2895
|
+
row.id
|
|
2896
|
+
)) });
|
|
2897
|
+
}
|
|
2898
|
+
function AdaptiveTableCard({
|
|
2899
|
+
row,
|
|
2900
|
+
table,
|
|
2901
|
+
rowIndex,
|
|
2902
|
+
visibleColumns,
|
|
2903
|
+
layout,
|
|
2904
|
+
clickable
|
|
2905
|
+
}) {
|
|
2906
|
+
const { cells, primaryCell, secondaryCells } = getAdaptiveCells(row, visibleColumns, layout);
|
|
2907
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2908
|
+
"article",
|
|
2909
|
+
{
|
|
2910
|
+
className: "yable-adaptive-card",
|
|
2911
|
+
role: "row",
|
|
2912
|
+
"data-selected": row.getIsSelected() || void 0,
|
|
2913
|
+
"data-expanded": row.getIsExpanded() || void 0,
|
|
2914
|
+
"data-clickable": clickable || void 0,
|
|
2915
|
+
"data-row-id": row.id,
|
|
2916
|
+
"data-row-index": rowIndex,
|
|
2917
|
+
"aria-selected": table.options.enableRowSelection ? row.getIsSelected() : void 0,
|
|
2918
|
+
onClick: (e) => {
|
|
2919
|
+
if (table.options.enableRowClickSelection && row.getCanSelect() && !isInteractiveClickTarget3(e.target)) {
|
|
2920
|
+
row.toggleSelected();
|
|
2921
|
+
}
|
|
2922
|
+
if (clickable) emitRowEvent(table, "row:click", row, e.nativeEvent);
|
|
2923
|
+
},
|
|
2924
|
+
onDoubleClick: (e) => emitRowEvent(table, "row:dblclick", row, e.nativeEvent),
|
|
2925
|
+
onContextMenu: (e) => emitRowEvent(table, "row:contextmenu", row, e.nativeEvent),
|
|
2926
|
+
children: layout.renderCard ? layout.renderCard({
|
|
2927
|
+
table,
|
|
2928
|
+
row,
|
|
2929
|
+
rowIndex,
|
|
2930
|
+
cells,
|
|
2931
|
+
primaryCell,
|
|
2932
|
+
secondaryCells,
|
|
2933
|
+
visibleColumns
|
|
2934
|
+
}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2935
|
+
primaryCell && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "yable-adaptive-card-header", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "yable-adaptive-card-title", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2936
|
+
AdaptiveTableCardCell,
|
|
2937
|
+
{
|
|
2938
|
+
cell: primaryCell,
|
|
2939
|
+
table,
|
|
2940
|
+
rowIndex,
|
|
2941
|
+
columnIndex: 0,
|
|
2942
|
+
primary: true
|
|
2943
|
+
}
|
|
2944
|
+
) }) }),
|
|
2945
|
+
secondaryCells.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "yable-adaptive-card-fields", children: secondaryCells.map((cell, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2946
|
+
AdaptiveTableCardCell,
|
|
2947
|
+
{
|
|
2948
|
+
cell,
|
|
2949
|
+
table,
|
|
2950
|
+
rowIndex,
|
|
2951
|
+
columnIndex: index + 1
|
|
2952
|
+
},
|
|
2953
|
+
cell.id
|
|
2954
|
+
)) })
|
|
2955
|
+
] })
|
|
2956
|
+
}
|
|
2957
|
+
);
|
|
2958
|
+
}
|
|
2959
|
+
function emitRowEvent(table, event, row, originalEvent) {
|
|
2960
|
+
table.events.emit(event, {
|
|
2961
|
+
row,
|
|
2962
|
+
cells: row.getAllCells(),
|
|
2963
|
+
originalEvent
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
function emitCellEvent(table, event, cell, originalEvent) {
|
|
2967
|
+
table.events.emit(event, {
|
|
2968
|
+
cell,
|
|
2969
|
+
row: cell.row,
|
|
2970
|
+
column: cell.column,
|
|
2971
|
+
originalEvent
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
function AdaptiveTableCardCell({
|
|
2975
|
+
cell,
|
|
2976
|
+
table,
|
|
2977
|
+
rowIndex,
|
|
2978
|
+
columnIndex,
|
|
2979
|
+
primary
|
|
2980
|
+
}) {
|
|
2981
|
+
const column = cell.column;
|
|
2982
|
+
const isEditing = cell.getIsEditing();
|
|
2983
|
+
const isAlwaysEditable = cell.getIsAlwaysEditable();
|
|
2984
|
+
const cellStatus = table.getCellStatus(cell.row.id, column.id);
|
|
2985
|
+
const cellErrorMessage = table.getCellErrorMessage(cell.row.id, column.id);
|
|
2986
|
+
const cellConflictWith = table.getCellConflictWith(cell.row.id, column.id);
|
|
2987
|
+
const content = renderCellContent(cell, table);
|
|
2988
|
+
const header = column.columnDef.header;
|
|
2989
|
+
const label = typeof header === "string" ? header : column.id;
|
|
2990
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CellErrorBoundary, { resetKeys: [cell.getValue(), cellStatus], children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2991
|
+
"div",
|
|
2992
|
+
{
|
|
2993
|
+
className: primary ? "yable-adaptive-card-primary" : "yable-adaptive-card-cell",
|
|
2994
|
+
"data-column-id": column.id,
|
|
2995
|
+
"data-cell-status": cellStatus !== "idle" ? cellStatus : void 0,
|
|
2996
|
+
"data-row-index": rowIndex,
|
|
2997
|
+
"data-column-index": columnIndex,
|
|
2998
|
+
role: "gridcell",
|
|
2999
|
+
"aria-colindex": columnIndex + 1,
|
|
3000
|
+
onClick: (e) => {
|
|
3001
|
+
if (cell.row.getIsGrouped()) return;
|
|
3002
|
+
emitCellEvent(table, "cell:click", cell, e.nativeEvent);
|
|
3003
|
+
if (yableCore.canCellEnterEditMode(table, cell.row, column) && !isAlwaysEditable && !isEditing && !e.shiftKey && !e.metaKey && !e.ctrlKey) {
|
|
3004
|
+
table.startEditing(cell.row.id, column.id);
|
|
3005
|
+
}
|
|
3006
|
+
},
|
|
3007
|
+
onDoubleClick: (e) => emitCellEvent(table, "cell:dblclick", cell, e.nativeEvent),
|
|
3008
|
+
onContextMenu: (e) => emitCellEvent(table, "cell:contextmenu", cell, e.nativeEvent),
|
|
3009
|
+
children: [
|
|
3010
|
+
!primary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "yable-adaptive-card-label", children: label }),
|
|
3011
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "yable-adaptive-card-value", children: [
|
|
3012
|
+
content,
|
|
3013
|
+
cellStatus === "error" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3014
|
+
CellStatusBadge,
|
|
3015
|
+
{
|
|
3016
|
+
status: "error",
|
|
3017
|
+
message: cellErrorMessage,
|
|
3018
|
+
onRetry: () => void table.retryCommit(cell.row.id, column.id),
|
|
3019
|
+
onDismiss: () => table.dismissCommit(cell.row.id, column.id)
|
|
3020
|
+
}
|
|
3021
|
+
),
|
|
3022
|
+
cellStatus === "conflict" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3023
|
+
CellStatusBadge,
|
|
3024
|
+
{
|
|
3025
|
+
status: "conflict",
|
|
3026
|
+
conflictWith: cellConflictWith,
|
|
3027
|
+
onRetry: () => void table.retryCommit(cell.row.id, column.id),
|
|
3028
|
+
onDismiss: () => table.dismissCommit(cell.row.id, column.id)
|
|
3029
|
+
}
|
|
3030
|
+
)
|
|
3031
|
+
] })
|
|
3032
|
+
]
|
|
3033
|
+
}
|
|
3034
|
+
) });
|
|
3035
|
+
}
|
|
3036
|
+
function getAdaptiveCells(row, visibleColumns, layout) {
|
|
3037
|
+
const hidden = new Set(layout.hiddenColumnIds ?? []);
|
|
3038
|
+
const allCellsByColumn = new Map(row.getAllCells().map((cell) => [cell.column.id, cell]));
|
|
3039
|
+
const visibleCells = [];
|
|
3040
|
+
for (const column of visibleColumns) {
|
|
3041
|
+
if (hidden.has(column.id)) continue;
|
|
3042
|
+
const cell = allCellsByColumn.get(column.id);
|
|
3043
|
+
if (cell) visibleCells.push(cell);
|
|
3044
|
+
}
|
|
3045
|
+
const cellsByColumn = new Map(visibleCells.map((cell) => [cell.column.id, cell]));
|
|
3046
|
+
const primaryCell = (layout.primaryColumnId ? cellsByColumn.get(layout.primaryColumnId) : void 0) ?? visibleCells[0];
|
|
3047
|
+
const primaryColumnId = primaryCell?.column.id;
|
|
3048
|
+
let secondaryCells;
|
|
3049
|
+
if (layout.secondaryColumnIds) {
|
|
3050
|
+
secondaryCells = [];
|
|
3051
|
+
for (const columnId of layout.secondaryColumnIds) {
|
|
3052
|
+
if (hidden.has(columnId) || columnId === primaryColumnId) continue;
|
|
3053
|
+
const cell = cellsByColumn.get(columnId);
|
|
3054
|
+
if (cell) secondaryCells.push(cell);
|
|
3055
|
+
}
|
|
3056
|
+
} else {
|
|
3057
|
+
secondaryCells = visibleCells.filter((cell) => cell.column.id !== primaryColumnId);
|
|
3058
|
+
if (secondaryCells.length > layout.maxSecondaryColumns) {
|
|
3059
|
+
secondaryCells = secondaryCells.slice(0, layout.maxSecondaryColumns);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
return {
|
|
3063
|
+
cells: visibleCells,
|
|
3064
|
+
primaryCell,
|
|
3065
|
+
secondaryCells
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
function isInteractiveClickTarget3(target) {
|
|
3069
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
3070
|
+
return Boolean(
|
|
3071
|
+
target.closest('input, textarea, select, button, a[href], [contenteditable="true"]')
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
2874
3074
|
function TableFooter({ table }) {
|
|
2875
3075
|
const footerGroups = table.getFooterGroups();
|
|
2876
3076
|
if (!footerGroups.length) return null;
|
|
@@ -4198,6 +4398,7 @@ function Table({
|
|
|
4198
4398
|
floatingFilters,
|
|
4199
4399
|
columnVirtualization,
|
|
4200
4400
|
columnVirtualizationOverscan,
|
|
4401
|
+
adaptiveLayout: adaptiveLayoutProp,
|
|
4201
4402
|
ariaLabel: ariaLabelProp,
|
|
4202
4403
|
...rest
|
|
4203
4404
|
}) {
|
|
@@ -4223,6 +4424,7 @@ function Table({
|
|
|
4223
4424
|
const resolvedFloatingFilters = floatingFilters ?? profileTableProps?.floatingFilters;
|
|
4224
4425
|
const resolvedColumnVirtualization = columnVirtualization ?? profileTableProps?.columnVirtualization;
|
|
4225
4426
|
const resolvedColumnVirtualizationOverscan = columnVirtualizationOverscan ?? profileTableProps?.columnVirtualizationOverscan;
|
|
4427
|
+
const resolvedAdaptiveLayout = adaptiveLayoutProp ?? profileTableProps?.adaptiveLayout ?? providerTableProps?.adaptiveLayout;
|
|
4226
4428
|
const [sidebarOpen, setSidebarOpen] = React4.useState(false);
|
|
4227
4429
|
const [sidebarPanel, setSidebarPanel] = React4.useState(
|
|
4228
4430
|
resolvedDefaultSidebarPanel ?? "columns"
|
|
@@ -4230,6 +4432,11 @@ function Table({
|
|
|
4230
4432
|
const containerRef = React4.useRef(null);
|
|
4231
4433
|
const horizontalScrollRef = React4.useRef(null);
|
|
4232
4434
|
const isRtl = direction === "rtl";
|
|
4435
|
+
const adaptiveLayout = React4.useMemo(
|
|
4436
|
+
() => normalizeAdaptiveLayout(resolvedAdaptiveLayout),
|
|
4437
|
+
[resolvedAdaptiveLayout]
|
|
4438
|
+
);
|
|
4439
|
+
const adaptiveLayoutActive = useAdaptiveLayoutActive(containerRef, adaptiveLayout);
|
|
4233
4440
|
const classNames = [
|
|
4234
4441
|
"yable",
|
|
4235
4442
|
theme && `yable-theme-${theme}`,
|
|
@@ -4240,6 +4447,8 @@ function Table({
|
|
|
4240
4447
|
loading && "yable-loading",
|
|
4241
4448
|
isRtl && "yable--rtl",
|
|
4242
4449
|
sidebarOpen && "yable--sidebar-open",
|
|
4450
|
+
adaptiveLayout && adaptiveLayout.mode !== "table" && "yable--adaptive-layout",
|
|
4451
|
+
adaptiveLayoutActive && "yable--adaptive-cards-active",
|
|
4243
4452
|
className
|
|
4244
4453
|
].filter(Boolean).join(" ");
|
|
4245
4454
|
const rows = table.getRowModel().rows;
|
|
@@ -4249,7 +4458,7 @@ function Table({
|
|
|
4249
4458
|
const allVisibleColumns = table.getVisibleLeafColumns();
|
|
4250
4459
|
const hasPinnedColumns = table.getLeftVisibleLeafColumns().length > 0 || table.getRightVisibleLeafColumns().length > 0;
|
|
4251
4460
|
const hasGroupedHeaders = table.getHeaderGroups().length > 1;
|
|
4252
|
-
const canVirtualizeColumns = Boolean(resolvedColumnVirtualization) && !hasPinnedColumns && !hasGroupedHeaders && allVisibleColumns.length > 0;
|
|
4461
|
+
const canVirtualizeColumns = !adaptiveLayoutActive && Boolean(resolvedColumnVirtualization) && !hasPinnedColumns && !hasGroupedHeaders && allVisibleColumns.length > 0;
|
|
4253
4462
|
const allVisibleColumnSizeSignature = allVisibleColumns.map((column) => `${column.id}:${column.getSize()}`).join("|");
|
|
4254
4463
|
const columnVirtualState = useColumnVirtualization({
|
|
4255
4464
|
containerRef: horizontalScrollRef,
|
|
@@ -4365,7 +4574,7 @@ function Table({
|
|
|
4365
4574
|
columnVirtualState.startOffset,
|
|
4366
4575
|
visibleColumnTotalSize
|
|
4367
4576
|
]);
|
|
4368
|
-
const
|
|
4577
|
+
const desktopTableNode = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4369
4578
|
"table",
|
|
4370
4579
|
{
|
|
4371
4580
|
className: "yable-table",
|
|
@@ -4387,6 +4596,14 @@ function Table({
|
|
|
4387
4596
|
]
|
|
4388
4597
|
}
|
|
4389
4598
|
);
|
|
4599
|
+
const tableNode = adaptiveLayoutActive && adaptiveLayout ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4600
|
+
AdaptiveTableCards,
|
|
4601
|
+
{
|
|
4602
|
+
table,
|
|
4603
|
+
layout: adaptiveLayout,
|
|
4604
|
+
clickableRows: resolvedClickableRows
|
|
4605
|
+
}
|
|
4606
|
+
) : desktopTableNode;
|
|
4390
4607
|
return /* @__PURE__ */ jsxRuntime.jsx(TableProvider, { value: table, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4391
4608
|
"div",
|
|
4392
4609
|
{
|
|
@@ -4402,7 +4619,7 @@ function Table({
|
|
|
4402
4619
|
...rest,
|
|
4403
4620
|
children: [
|
|
4404
4621
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "yable-main", children: [
|
|
4405
|
-
showColumnVirtualizationShell ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4622
|
+
showColumnVirtualizationShell && !adaptiveLayoutActive ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4406
4623
|
"div",
|
|
4407
4624
|
{
|
|
4408
4625
|
ref: horizontalScrollRef,
|
|
@@ -4518,6 +4735,58 @@ function Table({
|
|
|
4518
4735
|
}
|
|
4519
4736
|
) });
|
|
4520
4737
|
}
|
|
4738
|
+
var DEFAULT_ADAPTIVE_BREAKPOINT = 720;
|
|
4739
|
+
var DEFAULT_ADAPTIVE_SECONDARY_COLUMNS = 8;
|
|
4740
|
+
function normalizeAdaptiveLayout(layout) {
|
|
4741
|
+
if (!layout) return null;
|
|
4742
|
+
if (layout === true) {
|
|
4743
|
+
return {
|
|
4744
|
+
mode: "auto",
|
|
4745
|
+
breakpoint: DEFAULT_ADAPTIVE_BREAKPOINT,
|
|
4746
|
+
maxSecondaryColumns: DEFAULT_ADAPTIVE_SECONDARY_COLUMNS
|
|
4747
|
+
};
|
|
4748
|
+
}
|
|
4749
|
+
return {
|
|
4750
|
+
mode: layout.mode ?? "auto",
|
|
4751
|
+
breakpoint: layout.breakpoint ?? DEFAULT_ADAPTIVE_BREAKPOINT,
|
|
4752
|
+
primaryColumnId: layout.primaryColumnId,
|
|
4753
|
+
secondaryColumnIds: layout.secondaryColumnIds,
|
|
4754
|
+
hiddenColumnIds: layout.hiddenColumnIds,
|
|
4755
|
+
maxSecondaryColumns: layout.maxSecondaryColumns ?? DEFAULT_ADAPTIVE_SECONDARY_COLUMNS,
|
|
4756
|
+
renderCard: layout.renderCard
|
|
4757
|
+
};
|
|
4758
|
+
}
|
|
4759
|
+
function useAdaptiveLayoutActive(containerRef, layout) {
|
|
4760
|
+
const [active, setActive] = React4.useState(() => layout?.mode === "cards");
|
|
4761
|
+
React4.useEffect(() => {
|
|
4762
|
+
if (!layout) {
|
|
4763
|
+
setActive(false);
|
|
4764
|
+
return;
|
|
4765
|
+
}
|
|
4766
|
+
if (layout.mode === "cards" || layout.mode === "table") {
|
|
4767
|
+
setActive(layout.mode === "cards");
|
|
4768
|
+
return;
|
|
4769
|
+
}
|
|
4770
|
+
const node = containerRef.current;
|
|
4771
|
+
if (!node) return;
|
|
4772
|
+
const update = (width) => {
|
|
4773
|
+
setActive(width <= layout.breakpoint);
|
|
4774
|
+
};
|
|
4775
|
+
update(node.getBoundingClientRect().width || node.clientWidth);
|
|
4776
|
+
if (typeof ResizeObserver === "undefined") {
|
|
4777
|
+
const handleResize = () => update(node.getBoundingClientRect().width || node.clientWidth);
|
|
4778
|
+
window.addEventListener("resize", handleResize);
|
|
4779
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
4780
|
+
}
|
|
4781
|
+
const observer = new ResizeObserver((entries) => {
|
|
4782
|
+
const width = entries[0]?.contentRect.width ?? node.getBoundingClientRect().width;
|
|
4783
|
+
update(width);
|
|
4784
|
+
});
|
|
4785
|
+
observer.observe(node);
|
|
4786
|
+
return () => observer.disconnect();
|
|
4787
|
+
}, [containerRef, layout]);
|
|
4788
|
+
return active;
|
|
4789
|
+
}
|
|
4521
4790
|
function ChevronLeftIcon() {
|
|
4522
4791
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.5 3L4.5 7L8.5 11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
4523
4792
|
}
|