@rowakit/table 0.1.0 → 0.2.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 +234 -1
- package/dist/index.cjs +289 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -6
- package/dist/index.d.ts +144 -6
- package/dist/index.js +289 -33
- package/dist/index.js.map +1 -1
- package/package.json +7 -8
- package/src/styles/table.css +104 -0
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,30 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
12
12
|
* @packageDocumentation
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Filter value shape for server-side filtering.
|
|
17
|
+
*/
|
|
18
|
+
type FilterValue = {
|
|
19
|
+
op: 'contains';
|
|
20
|
+
value: string;
|
|
21
|
+
} | {
|
|
22
|
+
op: 'equals';
|
|
23
|
+
value: string | number | boolean | null;
|
|
24
|
+
} | {
|
|
25
|
+
op: 'in';
|
|
26
|
+
value: Array<string | number>;
|
|
27
|
+
} | {
|
|
28
|
+
op: 'range';
|
|
29
|
+
value: {
|
|
30
|
+
from?: string;
|
|
31
|
+
to?: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Filters map: field name → filter value.
|
|
36
|
+
* Undefined values represent cleared filters.
|
|
37
|
+
*/
|
|
38
|
+
type Filters = Record<string, FilterValue | undefined>;
|
|
15
39
|
/**
|
|
16
40
|
* Query parameters passed to the fetcher function.
|
|
17
41
|
*
|
|
@@ -29,8 +53,8 @@ interface FetcherQuery {
|
|
|
29
53
|
/** Sort direction */
|
|
30
54
|
direction: 'asc' | 'desc';
|
|
31
55
|
};
|
|
32
|
-
/** Optional filters (
|
|
33
|
-
filters?:
|
|
56
|
+
/** Optional filters (omitted when empty) */
|
|
57
|
+
filters?: Filters;
|
|
34
58
|
}
|
|
35
59
|
/**
|
|
36
60
|
* Result returned from the fetcher function.
|
|
@@ -66,7 +90,11 @@ type Fetcher<T> = (query: FetcherQuery) => Promise<FetcherResult<T>>;
|
|
|
66
90
|
* These represent the built-in column types that can be created
|
|
67
91
|
* via the `col.*` helper factory.
|
|
68
92
|
*/
|
|
69
|
-
type ColumnKind = 'text' | 'date' | 'boolean' | 'actions' | 'custom';
|
|
93
|
+
type ColumnKind = 'text' | 'date' | 'boolean' | 'actions' | 'custom' | 'badge' | 'number';
|
|
94
|
+
/**
|
|
95
|
+
* Badge tone options for status/enum columns.
|
|
96
|
+
*/
|
|
97
|
+
type BadgeTone = 'neutral' | 'success' | 'warning' | 'danger';
|
|
70
98
|
/**
|
|
71
99
|
* Base column definition properties shared across all column types.
|
|
72
100
|
*/
|
|
@@ -81,6 +109,12 @@ interface BaseColumnDef<T> {
|
|
|
81
109
|
sortable?: boolean;
|
|
82
110
|
/** Optional field name to extract from row data (for sortable columns) */
|
|
83
111
|
field?: keyof T & string;
|
|
112
|
+
/** Column width in pixels */
|
|
113
|
+
width?: number;
|
|
114
|
+
/** Text alignment */
|
|
115
|
+
align?: 'left' | 'center' | 'right';
|
|
116
|
+
/** Enable text truncation with ellipsis */
|
|
117
|
+
truncate?: boolean;
|
|
84
118
|
}
|
|
85
119
|
/**
|
|
86
120
|
* Text column definition.
|
|
@@ -106,6 +140,25 @@ interface BooleanColumnDef<T> extends BaseColumnDef<T> {
|
|
|
106
140
|
field: keyof T & string;
|
|
107
141
|
format?: (value: boolean) => string;
|
|
108
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Badge column definition for status/enum fields.
|
|
145
|
+
*/
|
|
146
|
+
interface BadgeColumnDef<T> extends BaseColumnDef<T> {
|
|
147
|
+
kind: 'badge';
|
|
148
|
+
field: keyof T & string;
|
|
149
|
+
map?: Record<string, {
|
|
150
|
+
label: string;
|
|
151
|
+
tone: BadgeTone;
|
|
152
|
+
}>;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Number column definition.
|
|
156
|
+
*/
|
|
157
|
+
interface NumberColumnDef<T> extends BaseColumnDef<T> {
|
|
158
|
+
kind: 'number';
|
|
159
|
+
field: keyof T & string;
|
|
160
|
+
format?: Intl.NumberFormatOptions | ((value: number, row: T) => string);
|
|
161
|
+
}
|
|
109
162
|
/**
|
|
110
163
|
* Actions column definition.
|
|
111
164
|
*/
|
|
@@ -127,7 +180,7 @@ interface CustomColumnDef<T> extends BaseColumnDef<T> {
|
|
|
127
180
|
/**
|
|
128
181
|
* Union type for all column definitions.
|
|
129
182
|
*/
|
|
130
|
-
type ColumnDef<T> = TextColumnDef<T> | DateColumnDef<T> | BooleanColumnDef<T> | ActionsColumnDef<T> | CustomColumnDef<T>;
|
|
183
|
+
type ColumnDef<T> = TextColumnDef<T> | DateColumnDef<T> | BooleanColumnDef<T> | BadgeColumnDef<T> | NumberColumnDef<T> | ActionsColumnDef<T> | CustomColumnDef<T>;
|
|
131
184
|
/**
|
|
132
185
|
* Row action definition.
|
|
133
186
|
*
|
|
@@ -168,6 +221,12 @@ interface TextOptions {
|
|
|
168
221
|
sortable?: boolean;
|
|
169
222
|
/** Optional formatter function */
|
|
170
223
|
format?: (value: unknown) => string;
|
|
224
|
+
/** Column width in pixels */
|
|
225
|
+
width?: number;
|
|
226
|
+
/** Text alignment */
|
|
227
|
+
align?: 'left' | 'center' | 'right';
|
|
228
|
+
/** Enable text truncation with ellipsis */
|
|
229
|
+
truncate?: boolean;
|
|
171
230
|
}
|
|
172
231
|
interface DateOptions {
|
|
173
232
|
/** Optional custom header label */
|
|
@@ -176,6 +235,12 @@ interface DateOptions {
|
|
|
176
235
|
sortable?: boolean;
|
|
177
236
|
/** Optional date formatter function */
|
|
178
237
|
format?: (value: Date | string | number) => string;
|
|
238
|
+
/** Column width in pixels */
|
|
239
|
+
width?: number;
|
|
240
|
+
/** Text alignment */
|
|
241
|
+
align?: 'left' | 'center' | 'right';
|
|
242
|
+
/** Enable text truncation with ellipsis */
|
|
243
|
+
truncate?: boolean;
|
|
179
244
|
}
|
|
180
245
|
interface BooleanOptions {
|
|
181
246
|
/** Optional custom header label */
|
|
@@ -184,6 +249,43 @@ interface BooleanOptions {
|
|
|
184
249
|
sortable?: boolean;
|
|
185
250
|
/** Optional boolean formatter function */
|
|
186
251
|
format?: (value: boolean) => string;
|
|
252
|
+
/** Column width in pixels */
|
|
253
|
+
width?: number;
|
|
254
|
+
/** Text alignment */
|
|
255
|
+
align?: 'left' | 'center' | 'right';
|
|
256
|
+
/** Enable text truncation with ellipsis */
|
|
257
|
+
truncate?: boolean;
|
|
258
|
+
}
|
|
259
|
+
interface BadgeOptions {
|
|
260
|
+
/** Optional custom header label */
|
|
261
|
+
header?: string;
|
|
262
|
+
/** Enable sorting for this column */
|
|
263
|
+
sortable?: boolean;
|
|
264
|
+
/** Value-to-badge mapping */
|
|
265
|
+
map?: Record<string, {
|
|
266
|
+
label: string;
|
|
267
|
+
tone: BadgeTone;
|
|
268
|
+
}>;
|
|
269
|
+
/** Column width in pixels */
|
|
270
|
+
width?: number;
|
|
271
|
+
/** Text alignment */
|
|
272
|
+
align?: 'left' | 'center' | 'right';
|
|
273
|
+
/** Enable text truncation with ellipsis */
|
|
274
|
+
truncate?: boolean;
|
|
275
|
+
}
|
|
276
|
+
interface NumberOptions<T = unknown> {
|
|
277
|
+
/** Optional custom header label */
|
|
278
|
+
header?: string;
|
|
279
|
+
/** Enable sorting for this column */
|
|
280
|
+
sortable?: boolean;
|
|
281
|
+
/** Formatting options: Intl.NumberFormatOptions or custom formatter */
|
|
282
|
+
format?: Intl.NumberFormatOptions | ((value: number, row: T) => string);
|
|
283
|
+
/** Column width in pixels */
|
|
284
|
+
width?: number;
|
|
285
|
+
/** Text alignment */
|
|
286
|
+
align?: 'left' | 'center' | 'right';
|
|
287
|
+
/** Enable text truncation with ellipsis */
|
|
288
|
+
truncate?: boolean;
|
|
187
289
|
}
|
|
188
290
|
/**
|
|
189
291
|
* Create a text column definition.
|
|
@@ -222,6 +324,37 @@ declare function date<T>(field: keyof T & string, options?: DateOptions): DateCo
|
|
|
222
324
|
* ```
|
|
223
325
|
*/
|
|
224
326
|
declare function boolean<T>(field: keyof T & string, options?: BooleanOptions): BooleanColumnDef<T>;
|
|
327
|
+
/**
|
|
328
|
+
* Create a badge column definition for status/enum fields.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* col.badge('status')
|
|
333
|
+
* col.badge('status', {
|
|
334
|
+
* map: {
|
|
335
|
+
* active: { label: 'Active', tone: 'success' },
|
|
336
|
+
* paused: { label: 'Paused', tone: 'warning' },
|
|
337
|
+
* disabled: { label: 'Disabled', tone: 'danger' }
|
|
338
|
+
* }
|
|
339
|
+
* })
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
declare function badge<T>(field: keyof T & string, options?: BadgeOptions): BadgeColumnDef<T>;
|
|
343
|
+
/**
|
|
344
|
+
* Create a number column definition.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* col.number('amount')
|
|
349
|
+
* col.number('price', {
|
|
350
|
+
* format: { style: 'currency', currency: 'USD' }
|
|
351
|
+
* })
|
|
352
|
+
* col.number('count', {
|
|
353
|
+
* format: (val, row) => `${val} items`
|
|
354
|
+
* })
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
declare function number<T>(field: keyof T & string, options?: NumberOptions<T>): NumberColumnDef<T>;
|
|
225
358
|
/**
|
|
226
359
|
* Create an actions column definition.
|
|
227
360
|
*
|
|
@@ -267,6 +400,7 @@ declare function actions<T>(actions: ActionDef<T>[]): ActionsColumnDef<T>;
|
|
|
267
400
|
* })
|
|
268
401
|
* ```
|
|
269
402
|
*/
|
|
403
|
+
declare function custom<T>(field: keyof T & string, render: (row: T) => ReactNode): CustomColumnDef<T>;
|
|
270
404
|
declare function custom<T>(options: {
|
|
271
405
|
/** Unique column identifier */
|
|
272
406
|
id: string;
|
|
@@ -301,6 +435,8 @@ declare const col: {
|
|
|
301
435
|
readonly text: typeof text;
|
|
302
436
|
readonly date: typeof date;
|
|
303
437
|
readonly boolean: typeof boolean;
|
|
438
|
+
readonly badge: typeof badge;
|
|
439
|
+
readonly number: typeof number;
|
|
304
440
|
readonly actions: typeof actions;
|
|
305
441
|
readonly custom: typeof custom;
|
|
306
442
|
};
|
|
@@ -318,6 +454,8 @@ interface SmartTableProps<T> {
|
|
|
318
454
|
rowKey?: keyof T | ((row: T) => string | number);
|
|
319
455
|
/** Optional CSS class name for the table container */
|
|
320
456
|
className?: string;
|
|
457
|
+
/** Enable filters (default: false) */
|
|
458
|
+
enableFilters?: boolean;
|
|
321
459
|
}
|
|
322
460
|
/**
|
|
323
461
|
* RowaKitTable - Server-side table component for internal/business apps.
|
|
@@ -370,7 +508,7 @@ interface SmartTableProps<T> {
|
|
|
370
508
|
* }
|
|
371
509
|
* ```
|
|
372
510
|
*/
|
|
373
|
-
declare function RowaKitTable<T>({ fetcher, columns, defaultPageSize, pageSizeOptions, rowKey, className, }: SmartTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
511
|
+
declare function RowaKitTable<T>({ fetcher, columns, defaultPageSize, pageSizeOptions, rowKey, className, enableFilters, }: SmartTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
374
512
|
/**
|
|
375
513
|
* @deprecated Use RowaKitTable instead. SmartTable is kept as an alias for backward compatibility.
|
|
376
514
|
*/
|
|
@@ -386,4 +524,4 @@ declare const SmartTable: typeof RowaKitTable;
|
|
|
386
524
|
|
|
387
525
|
declare const VERSION = "0.1.0";
|
|
388
526
|
|
|
389
|
-
export { type ActionDef, type ActionsColumnDef, type BaseColumnDef, type BooleanColumnDef, type ColumnDef, type ColumnKind, type CustomColumnDef, type DateColumnDef, type Fetcher, type FetcherQuery, type FetcherResult, RowaKitTable, SmartTable, type SmartTableProps, type TextColumnDef, VERSION, col };
|
|
527
|
+
export { type ActionDef, type ActionsColumnDef, type BadgeColumnDef, type BadgeTone, type BaseColumnDef, type BooleanColumnDef, type ColumnDef, type ColumnKind, type CustomColumnDef, type DateColumnDef, type Fetcher, type FetcherQuery, type FetcherResult, type FilterValue, type Filters, type NumberColumnDef, RowaKitTable, SmartTable, type SmartTableProps, type TextColumnDef, VERSION, col };
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,10 @@ function text(field, options) {
|
|
|
9
9
|
field,
|
|
10
10
|
header: options?.header,
|
|
11
11
|
sortable: options?.sortable ?? false,
|
|
12
|
-
format: options?.format
|
|
12
|
+
format: options?.format,
|
|
13
|
+
width: options?.width,
|
|
14
|
+
align: options?.align,
|
|
15
|
+
truncate: options?.truncate
|
|
13
16
|
};
|
|
14
17
|
}
|
|
15
18
|
function date(field, options) {
|
|
@@ -19,7 +22,10 @@ function date(field, options) {
|
|
|
19
22
|
field,
|
|
20
23
|
header: options?.header,
|
|
21
24
|
sortable: options?.sortable ?? false,
|
|
22
|
-
format: options?.format
|
|
25
|
+
format: options?.format,
|
|
26
|
+
width: options?.width,
|
|
27
|
+
align: options?.align,
|
|
28
|
+
truncate: options?.truncate
|
|
23
29
|
};
|
|
24
30
|
}
|
|
25
31
|
function boolean(field, options) {
|
|
@@ -29,7 +35,36 @@ function boolean(field, options) {
|
|
|
29
35
|
field,
|
|
30
36
|
header: options?.header,
|
|
31
37
|
sortable: options?.sortable ?? false,
|
|
32
|
-
format: options?.format
|
|
38
|
+
format: options?.format,
|
|
39
|
+
width: options?.width,
|
|
40
|
+
align: options?.align,
|
|
41
|
+
truncate: options?.truncate
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function badge(field, options) {
|
|
45
|
+
return {
|
|
46
|
+
id: field,
|
|
47
|
+
kind: "badge",
|
|
48
|
+
field,
|
|
49
|
+
header: options?.header,
|
|
50
|
+
sortable: options?.sortable ?? false,
|
|
51
|
+
map: options?.map,
|
|
52
|
+
width: options?.width,
|
|
53
|
+
align: options?.align,
|
|
54
|
+
truncate: options?.truncate
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function number(field, options) {
|
|
58
|
+
return {
|
|
59
|
+
id: field,
|
|
60
|
+
kind: "number",
|
|
61
|
+
field,
|
|
62
|
+
header: options?.header,
|
|
63
|
+
sortable: options?.sortable ?? false,
|
|
64
|
+
format: options?.format,
|
|
65
|
+
width: options?.width,
|
|
66
|
+
align: options?.align,
|
|
67
|
+
truncate: options?.truncate
|
|
33
68
|
};
|
|
34
69
|
}
|
|
35
70
|
function actions(actions2) {
|
|
@@ -39,19 +74,32 @@ function actions(actions2) {
|
|
|
39
74
|
actions: actions2
|
|
40
75
|
};
|
|
41
76
|
}
|
|
42
|
-
function custom(
|
|
77
|
+
function custom(arg1, arg2) {
|
|
78
|
+
if (typeof arg1 === "string") {
|
|
79
|
+
if (typeof arg2 !== "function") {
|
|
80
|
+
throw new Error("col.custom(field, render): render must be a function");
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
id: arg1,
|
|
84
|
+
kind: "custom",
|
|
85
|
+
field: arg1,
|
|
86
|
+
render: arg2
|
|
87
|
+
};
|
|
88
|
+
}
|
|
43
89
|
return {
|
|
44
|
-
id:
|
|
90
|
+
id: arg1.id,
|
|
45
91
|
kind: "custom",
|
|
46
|
-
header:
|
|
47
|
-
field:
|
|
48
|
-
render:
|
|
92
|
+
header: arg1.header,
|
|
93
|
+
field: arg1.field,
|
|
94
|
+
render: arg1.render
|
|
49
95
|
};
|
|
50
96
|
}
|
|
51
97
|
var col = {
|
|
52
98
|
text,
|
|
53
99
|
date,
|
|
54
100
|
boolean,
|
|
101
|
+
badge,
|
|
102
|
+
number,
|
|
55
103
|
actions,
|
|
56
104
|
custom
|
|
57
105
|
};
|
|
@@ -99,6 +147,25 @@ function renderCell(column, row, isLoading, setConfirmState) {
|
|
|
99
147
|
}
|
|
100
148
|
return value ? "Yes" : "No";
|
|
101
149
|
}
|
|
150
|
+
case "badge": {
|
|
151
|
+
const value = row[column.field];
|
|
152
|
+
const valueStr = String(value ?? "");
|
|
153
|
+
const mapped = column.map?.[valueStr];
|
|
154
|
+
const label = mapped?.label ?? valueStr;
|
|
155
|
+
const tone = mapped?.tone ?? "neutral";
|
|
156
|
+
return /* @__PURE__ */ jsx("span", { className: `rowakit-badge rowakit-badge-${tone}`, children: label });
|
|
157
|
+
}
|
|
158
|
+
case "number": {
|
|
159
|
+
const value = row[column.field];
|
|
160
|
+
const numValue = Number(value ?? 0);
|
|
161
|
+
if (column.format) {
|
|
162
|
+
if (typeof column.format === "function") {
|
|
163
|
+
return column.format(numValue, row);
|
|
164
|
+
}
|
|
165
|
+
return new Intl.NumberFormat(void 0, column.format).format(numValue);
|
|
166
|
+
}
|
|
167
|
+
return numValue.toLocaleString();
|
|
168
|
+
}
|
|
102
169
|
case "actions": {
|
|
103
170
|
return /* @__PURE__ */ jsx("div", { className: "rowakit-table-actions", children: column.actions.map((action) => {
|
|
104
171
|
const isDisabled = isLoading || action.disabled === true || typeof action.disabled === "function" && action.disabled(row);
|
|
@@ -143,7 +210,8 @@ function RowaKitTable({
|
|
|
143
210
|
defaultPageSize = 20,
|
|
144
211
|
pageSizeOptions = [10, 20, 50],
|
|
145
212
|
rowKey,
|
|
146
|
-
className = ""
|
|
213
|
+
className = "",
|
|
214
|
+
enableFilters = false
|
|
147
215
|
}) {
|
|
148
216
|
const [dataState, setDataState] = useState({
|
|
149
217
|
state: "idle",
|
|
@@ -154,8 +222,27 @@ function RowaKitTable({
|
|
|
154
222
|
page: 1,
|
|
155
223
|
pageSize: defaultPageSize
|
|
156
224
|
});
|
|
225
|
+
const [filters, setFilters] = useState({});
|
|
157
226
|
const [confirmState, setConfirmState] = useState(null);
|
|
158
227
|
const requestIdRef = useRef(0);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (!enableFilters) return;
|
|
230
|
+
const activeFilters = {};
|
|
231
|
+
let hasFilters = false;
|
|
232
|
+
for (const [field, value] of Object.entries(filters)) {
|
|
233
|
+
if (value !== void 0) {
|
|
234
|
+
activeFilters[field] = value;
|
|
235
|
+
hasFilters = true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const filtersToSend = hasFilters ? activeFilters : void 0;
|
|
239
|
+
setQuery((prev) => ({
|
|
240
|
+
...prev,
|
|
241
|
+
filters: filtersToSend,
|
|
242
|
+
page: 1
|
|
243
|
+
// Reset page to 1 when filters change
|
|
244
|
+
}));
|
|
245
|
+
}, [filters, enableFilters]);
|
|
159
246
|
useEffect(() => {
|
|
160
247
|
const currentRequestId = ++requestIdRef.current;
|
|
161
248
|
setDataState((prev) => ({ ...prev, state: "loading" }));
|
|
@@ -223,38 +310,190 @@ function RowaKitTable({
|
|
|
223
310
|
}
|
|
224
311
|
return query.sort.direction === "asc" ? " \u2191" : " \u2193";
|
|
225
312
|
};
|
|
313
|
+
const handleFilterChange = (field, value) => {
|
|
314
|
+
setFilters((prev) => ({
|
|
315
|
+
...prev,
|
|
316
|
+
[field]: value
|
|
317
|
+
}));
|
|
318
|
+
};
|
|
319
|
+
const handleClearFilter = (field) => {
|
|
320
|
+
setFilters((prev) => {
|
|
321
|
+
const newFilters = { ...prev };
|
|
322
|
+
delete newFilters[field];
|
|
323
|
+
return newFilters;
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
const handleClearAllFilters = () => {
|
|
327
|
+
setFilters({});
|
|
328
|
+
};
|
|
226
329
|
const isLoading = dataState.state === "loading";
|
|
227
330
|
const isError = dataState.state === "error";
|
|
228
331
|
const isEmpty = dataState.state === "empty";
|
|
229
332
|
const totalPages = Math.ceil(dataState.total / query.pageSize);
|
|
230
333
|
const canGoPrevious = query.page > 1 && !isLoading;
|
|
231
334
|
const canGoNext = query.page < totalPages && !isLoading;
|
|
335
|
+
const hasActiveFilters = enableFilters && Object.values(filters).some((v) => v !== void 0);
|
|
232
336
|
return /* @__PURE__ */ jsxs("div", { className: `rowakit-table${className ? ` ${className}` : ""}`, children: [
|
|
337
|
+
hasActiveFilters && /* @__PURE__ */ jsx("div", { className: "rowakit-table-filter-controls", children: /* @__PURE__ */ jsx(
|
|
338
|
+
"button",
|
|
339
|
+
{
|
|
340
|
+
onClick: handleClearAllFilters,
|
|
341
|
+
className: "rowakit-button rowakit-button-secondary",
|
|
342
|
+
type: "button",
|
|
343
|
+
children: "Clear all filters"
|
|
344
|
+
}
|
|
345
|
+
) }),
|
|
233
346
|
/* @__PURE__ */ jsxs("table", { children: [
|
|
234
|
-
/* @__PURE__ */
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
e.
|
|
246
|
-
|
|
347
|
+
/* @__PURE__ */ jsxs("thead", { children: [
|
|
348
|
+
/* @__PURE__ */ jsx("tr", { children: columns.map((column) => {
|
|
349
|
+
const isSortable = column.kind !== "actions" && (column.kind === "custom" ? false : column.sortable === true);
|
|
350
|
+
const field = column.kind === "actions" ? "" : column.kind === "custom" ? column.field : column.field;
|
|
351
|
+
return /* @__PURE__ */ jsxs(
|
|
352
|
+
"th",
|
|
353
|
+
{
|
|
354
|
+
onClick: isSortable ? () => handleSort(String(field)) : void 0,
|
|
355
|
+
role: isSortable ? "button" : void 0,
|
|
356
|
+
tabIndex: isSortable ? 0 : void 0,
|
|
357
|
+
onKeyDown: isSortable ? (e) => {
|
|
358
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
handleSort(String(field));
|
|
361
|
+
}
|
|
362
|
+
} : void 0,
|
|
363
|
+
"aria-sort": isSortable && query.sort?.field === String(field) ? query.sort.direction === "asc" ? "ascending" : "descending" : void 0,
|
|
364
|
+
style: {
|
|
365
|
+
width: column.width ? `${column.width}px` : void 0,
|
|
366
|
+
textAlign: column.align
|
|
367
|
+
},
|
|
368
|
+
className: column.truncate ? "rowakit-cell-truncate" : void 0,
|
|
369
|
+
children: [
|
|
370
|
+
getHeaderLabel(column),
|
|
371
|
+
isSortable && getSortIndicator(String(field))
|
|
372
|
+
]
|
|
373
|
+
},
|
|
374
|
+
column.id
|
|
375
|
+
);
|
|
376
|
+
}) }),
|
|
377
|
+
enableFilters && /* @__PURE__ */ jsx("tr", { className: "rowakit-table-filter-row", children: columns.map((column) => {
|
|
378
|
+
const field = column.kind === "actions" || column.kind === "custom" ? "" : String(column.field);
|
|
379
|
+
const canFilter = field && column.kind !== "actions";
|
|
380
|
+
if (!canFilter) {
|
|
381
|
+
return /* @__PURE__ */ jsx("th", {}, column.id);
|
|
382
|
+
}
|
|
383
|
+
const filterValue = filters[field];
|
|
384
|
+
if (column.kind === "badge") {
|
|
385
|
+
const options = column.map ? Object.keys(column.map) : [];
|
|
386
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs(
|
|
387
|
+
"select",
|
|
388
|
+
{
|
|
389
|
+
className: "rowakit-filter-select",
|
|
390
|
+
value: filterValue?.op === "equals" ? String(filterValue.value ?? "") : "",
|
|
391
|
+
onChange: (e) => {
|
|
392
|
+
const value = e.target.value;
|
|
393
|
+
if (value === "") {
|
|
394
|
+
handleClearFilter(field);
|
|
395
|
+
} else {
|
|
396
|
+
handleFilterChange(field, { op: "equals", value });
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
children: [
|
|
400
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All" }),
|
|
401
|
+
options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: opt }, opt))
|
|
402
|
+
]
|
|
247
403
|
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
404
|
+
) }, column.id);
|
|
405
|
+
}
|
|
406
|
+
if (column.kind === "boolean") {
|
|
407
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs(
|
|
408
|
+
"select",
|
|
409
|
+
{
|
|
410
|
+
className: "rowakit-filter-select",
|
|
411
|
+
value: filterValue?.op === "equals" && typeof filterValue.value === "boolean" ? String(filterValue.value) : "",
|
|
412
|
+
onChange: (e) => {
|
|
413
|
+
const value = e.target.value;
|
|
414
|
+
if (value === "") {
|
|
415
|
+
handleClearFilter(field);
|
|
416
|
+
} else {
|
|
417
|
+
handleFilterChange(field, { op: "equals", value: value === "true" });
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
children: [
|
|
421
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All" }),
|
|
422
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: "True" }),
|
|
423
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: "False" })
|
|
424
|
+
]
|
|
425
|
+
}
|
|
426
|
+
) }, column.id);
|
|
427
|
+
}
|
|
428
|
+
if (column.kind === "date") {
|
|
429
|
+
const fromValue = filterValue?.op === "range" ? filterValue.value.from ?? "" : "";
|
|
430
|
+
const toValue = filterValue?.op === "range" ? filterValue.value.to ?? "" : "";
|
|
431
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs("div", { className: "rowakit-filter-date-range", children: [
|
|
432
|
+
/* @__PURE__ */ jsx(
|
|
433
|
+
"input",
|
|
434
|
+
{
|
|
435
|
+
type: "date",
|
|
436
|
+
className: "rowakit-filter-input",
|
|
437
|
+
placeholder: "From",
|
|
438
|
+
value: fromValue,
|
|
439
|
+
onChange: (e) => {
|
|
440
|
+
const from = e.target.value || void 0;
|
|
441
|
+
const to = toValue || void 0;
|
|
442
|
+
if (!from && !to) {
|
|
443
|
+
handleClearFilter(field);
|
|
444
|
+
} else {
|
|
445
|
+
handleFilterChange(field, { op: "range", value: { from, to } });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
),
|
|
450
|
+
/* @__PURE__ */ jsx(
|
|
451
|
+
"input",
|
|
452
|
+
{
|
|
453
|
+
type: "date",
|
|
454
|
+
className: "rowakit-filter-input",
|
|
455
|
+
placeholder: "To",
|
|
456
|
+
value: toValue,
|
|
457
|
+
onChange: (e) => {
|
|
458
|
+
const to = e.target.value || void 0;
|
|
459
|
+
const from = fromValue || void 0;
|
|
460
|
+
if (!from && !to) {
|
|
461
|
+
handleClearFilter(field);
|
|
462
|
+
} else {
|
|
463
|
+
handleFilterChange(field, { op: "range", value: { from, to } });
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
)
|
|
468
|
+
] }) }, column.id);
|
|
469
|
+
}
|
|
470
|
+
const isNumberColumn = column.kind === "number";
|
|
471
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx(
|
|
472
|
+
"input",
|
|
473
|
+
{
|
|
474
|
+
type: isNumberColumn ? "number" : "text",
|
|
475
|
+
className: "rowakit-filter-input",
|
|
476
|
+
placeholder: `Filter ${getHeaderLabel(column)}...`,
|
|
477
|
+
value: filterValue?.op === "contains" ? filterValue.value : filterValue?.op === "equals" && typeof filterValue.value === "string" ? filterValue.value : filterValue?.op === "equals" && typeof filterValue.value === "number" ? String(filterValue.value) : "",
|
|
478
|
+
onChange: (e) => {
|
|
479
|
+
const rawValue = e.target.value;
|
|
480
|
+
if (rawValue === "") {
|
|
481
|
+
handleClearFilter(field);
|
|
482
|
+
} else if (isNumberColumn) {
|
|
483
|
+
const numValue = Number(rawValue);
|
|
484
|
+
if (!isNaN(numValue)) {
|
|
485
|
+
handleFilterChange(field, { op: "equals", value: numValue });
|
|
486
|
+
} else {
|
|
487
|
+
handleClearFilter(field);
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
handleFilterChange(field, { op: "contains", value: rawValue });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
) }, column.id);
|
|
495
|
+
}) })
|
|
496
|
+
] }),
|
|
258
497
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
259
498
|
isLoading && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan: columns.length, className: "rowakit-table-loading", children: [
|
|
260
499
|
/* @__PURE__ */ jsx("div", { className: "rowakit-table-loading-spinner" }),
|
|
@@ -275,7 +514,24 @@ function RowaKitTable({
|
|
|
275
514
|
isEmpty && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: "rowakit-table-empty", children: "No data" }) }),
|
|
276
515
|
dataState.state === "success" && dataState.items.map((row) => {
|
|
277
516
|
const key = getRowKey(row, rowKey);
|
|
278
|
-
return /* @__PURE__ */ jsx("tr", { children: columns.map((column) =>
|
|
517
|
+
return /* @__PURE__ */ jsx("tr", { children: columns.map((column) => {
|
|
518
|
+
const cellClass = [
|
|
519
|
+
column.kind === "number" ? "rowakit-cell-number" : "",
|
|
520
|
+
column.truncate ? "rowakit-cell-truncate" : ""
|
|
521
|
+
].filter(Boolean).join(" ") || void 0;
|
|
522
|
+
return /* @__PURE__ */ jsx(
|
|
523
|
+
"td",
|
|
524
|
+
{
|
|
525
|
+
className: cellClass,
|
|
526
|
+
style: {
|
|
527
|
+
width: column.width ? `${column.width}px` : void 0,
|
|
528
|
+
textAlign: column.align || (column.kind === "number" ? "right" : void 0)
|
|
529
|
+
},
|
|
530
|
+
children: renderCell(column, row, isLoading, setConfirmState)
|
|
531
|
+
},
|
|
532
|
+
column.id
|
|
533
|
+
);
|
|
534
|
+
}) }, key);
|
|
279
535
|
})
|
|
280
536
|
] })
|
|
281
537
|
] }),
|