@kreativa/ui 0.1.1 → 0.1.2
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/dist/index.d.mts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +114 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +131 -56
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -142,16 +142,18 @@ declare function FormButtonGroup({ cancelText, submitText, loadingText, isLoadin
|
|
|
142
142
|
interface TableColumn<T> {
|
|
143
143
|
/** Unique key for the column */
|
|
144
144
|
key: string;
|
|
145
|
-
/** Column header text */
|
|
146
|
-
header:
|
|
145
|
+
/** Column header text or React element */
|
|
146
|
+
header: ReactNode;
|
|
147
147
|
/** Function to render cell content */
|
|
148
148
|
render: (item: T) => ReactNode;
|
|
149
149
|
/** Whether this column is sortable */
|
|
150
150
|
sortable?: boolean;
|
|
151
151
|
/** Custom sort function */
|
|
152
152
|
sortFn?: (a: T, b: T) => number;
|
|
153
|
-
/** Column width (e.g., '200px', '20%') */
|
|
153
|
+
/** Column width (e.g., '200px', '20%') - used as initial/min width */
|
|
154
154
|
width?: string;
|
|
155
|
+
/** Minimum column width when resizing */
|
|
156
|
+
minWidth?: number;
|
|
155
157
|
/** Text alignment */
|
|
156
158
|
align?: 'left' | 'center' | 'right';
|
|
157
159
|
}
|
|
@@ -170,18 +172,30 @@ interface TableProps<T> {
|
|
|
170
172
|
emptyMessage?: string;
|
|
171
173
|
/** Additional class names for the table container */
|
|
172
174
|
className?: string;
|
|
175
|
+
/** Enable manual column resizing (default: false) */
|
|
176
|
+
resizable?: boolean;
|
|
177
|
+
/** Use fixed table layout for consistent column widths */
|
|
178
|
+
fixedLayout?: boolean;
|
|
173
179
|
}
|
|
174
180
|
/**
|
|
175
|
-
* Table component for displaying tabular data with sorting
|
|
181
|
+
* Table component for displaying tabular data with sorting and optional resizing.
|
|
182
|
+
*
|
|
183
|
+
* Features:
|
|
184
|
+
* - Responsive by default - shrinks with container
|
|
185
|
+
* - Optional column resizing with drag handles
|
|
186
|
+
* - Sortable columns
|
|
187
|
+
* - Loading and empty states
|
|
176
188
|
*/
|
|
177
|
-
declare function Table<T>({ data, columns, getRowKey, onRowClick, loading, emptyMessage, className, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
189
|
+
declare function Table<T>({ data, columns, getRowKey, onRowClick, loading, emptyMessage, className, resizable, fixedLayout, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
178
190
|
|
|
179
191
|
type FilterType = 'text' | 'select' | 'multiselect' | 'boolean' | 'date' | 'none';
|
|
180
192
|
interface FilterOption {
|
|
181
193
|
value: string;
|
|
182
194
|
label: string;
|
|
183
195
|
}
|
|
184
|
-
interface DataTableColumn<T> extends Omit<TableColumn<T>, 'sortable'> {
|
|
196
|
+
interface DataTableColumn<T> extends Omit<TableColumn<T>, 'sortable' | 'header'> {
|
|
197
|
+
/** Column header text (must be string for filter placeholder) */
|
|
198
|
+
header: string;
|
|
185
199
|
/** Whether this column is sortable (default: true) */
|
|
186
200
|
sortable?: boolean;
|
|
187
201
|
/** Filter type for this column (default: 'text') */
|
package/dist/index.d.ts
CHANGED
|
@@ -142,16 +142,18 @@ declare function FormButtonGroup({ cancelText, submitText, loadingText, isLoadin
|
|
|
142
142
|
interface TableColumn<T> {
|
|
143
143
|
/** Unique key for the column */
|
|
144
144
|
key: string;
|
|
145
|
-
/** Column header text */
|
|
146
|
-
header:
|
|
145
|
+
/** Column header text or React element */
|
|
146
|
+
header: ReactNode;
|
|
147
147
|
/** Function to render cell content */
|
|
148
148
|
render: (item: T) => ReactNode;
|
|
149
149
|
/** Whether this column is sortable */
|
|
150
150
|
sortable?: boolean;
|
|
151
151
|
/** Custom sort function */
|
|
152
152
|
sortFn?: (a: T, b: T) => number;
|
|
153
|
-
/** Column width (e.g., '200px', '20%') */
|
|
153
|
+
/** Column width (e.g., '200px', '20%') - used as initial/min width */
|
|
154
154
|
width?: string;
|
|
155
|
+
/** Minimum column width when resizing */
|
|
156
|
+
minWidth?: number;
|
|
155
157
|
/** Text alignment */
|
|
156
158
|
align?: 'left' | 'center' | 'right';
|
|
157
159
|
}
|
|
@@ -170,18 +172,30 @@ interface TableProps<T> {
|
|
|
170
172
|
emptyMessage?: string;
|
|
171
173
|
/** Additional class names for the table container */
|
|
172
174
|
className?: string;
|
|
175
|
+
/** Enable manual column resizing (default: false) */
|
|
176
|
+
resizable?: boolean;
|
|
177
|
+
/** Use fixed table layout for consistent column widths */
|
|
178
|
+
fixedLayout?: boolean;
|
|
173
179
|
}
|
|
174
180
|
/**
|
|
175
|
-
* Table component for displaying tabular data with sorting
|
|
181
|
+
* Table component for displaying tabular data with sorting and optional resizing.
|
|
182
|
+
*
|
|
183
|
+
* Features:
|
|
184
|
+
* - Responsive by default - shrinks with container
|
|
185
|
+
* - Optional column resizing with drag handles
|
|
186
|
+
* - Sortable columns
|
|
187
|
+
* - Loading and empty states
|
|
176
188
|
*/
|
|
177
|
-
declare function Table<T>({ data, columns, getRowKey, onRowClick, loading, emptyMessage, className, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
189
|
+
declare function Table<T>({ data, columns, getRowKey, onRowClick, loading, emptyMessage, className, resizable, fixedLayout, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
178
190
|
|
|
179
191
|
type FilterType = 'text' | 'select' | 'multiselect' | 'boolean' | 'date' | 'none';
|
|
180
192
|
interface FilterOption {
|
|
181
193
|
value: string;
|
|
182
194
|
label: string;
|
|
183
195
|
}
|
|
184
|
-
interface DataTableColumn<T> extends Omit<TableColumn<T>, 'sortable'> {
|
|
196
|
+
interface DataTableColumn<T> extends Omit<TableColumn<T>, 'sortable' | 'header'> {
|
|
197
|
+
/** Column header text (must be string for filter placeholder) */
|
|
198
|
+
header: string;
|
|
185
199
|
/** Whether this column is sortable (default: true) */
|
|
186
200
|
sortable?: boolean;
|
|
187
201
|
/** Filter type for this column (default: 'text') */
|
package/dist/index.js
CHANGED
|
@@ -340,10 +340,15 @@ function Table({
|
|
|
340
340
|
onRowClick,
|
|
341
341
|
loading = false,
|
|
342
342
|
emptyMessage = "No data available",
|
|
343
|
-
className = ""
|
|
343
|
+
className = "",
|
|
344
|
+
resizable = false,
|
|
345
|
+
fixedLayout = false
|
|
344
346
|
}) {
|
|
345
347
|
const [sortKey, setSortKey] = (0, import_react5.useState)(null);
|
|
346
348
|
const [sortDirection, setSortDirection] = (0, import_react5.useState)(null);
|
|
349
|
+
const [columnWidths, setColumnWidths] = (0, import_react5.useState)({});
|
|
350
|
+
const tableRef = (0, import_react5.useRef)(null);
|
|
351
|
+
const resizingRef = (0, import_react5.useRef)(null);
|
|
347
352
|
const handleHeaderClick = (column) => {
|
|
348
353
|
if (!column.sortable) return;
|
|
349
354
|
if (sortKey === column.key) {
|
|
@@ -372,63 +377,133 @@ function Table({
|
|
|
372
377
|
});
|
|
373
378
|
return sortDirection === "desc" ? sorted.reverse() : sorted;
|
|
374
379
|
}, [data, columns, sortKey, sortDirection]);
|
|
380
|
+
const handleResizeStart = (0, import_react5.useCallback)(
|
|
381
|
+
(e, columnKey) => {
|
|
382
|
+
e.preventDefault();
|
|
383
|
+
e.stopPropagation();
|
|
384
|
+
const headerCell = e.currentTarget.parentElement;
|
|
385
|
+
if (!headerCell) return;
|
|
386
|
+
const startWidth = headerCell.getBoundingClientRect().width;
|
|
387
|
+
resizingRef.current = {
|
|
388
|
+
columnKey,
|
|
389
|
+
startX: e.clientX,
|
|
390
|
+
startWidth
|
|
391
|
+
};
|
|
392
|
+
document.addEventListener("mousemove", handleResizeMove);
|
|
393
|
+
document.addEventListener("mouseup", handleResizeEnd);
|
|
394
|
+
document.body.style.cursor = "col-resize";
|
|
395
|
+
document.body.style.userSelect = "none";
|
|
396
|
+
},
|
|
397
|
+
[]
|
|
398
|
+
);
|
|
399
|
+
const handleResizeMove = (0, import_react5.useCallback)((e) => {
|
|
400
|
+
if (!resizingRef.current) return;
|
|
401
|
+
const { columnKey, startX, startWidth } = resizingRef.current;
|
|
402
|
+
const column = columns.find((c) => c.key === columnKey);
|
|
403
|
+
const minWidth = column?.minWidth ?? 50;
|
|
404
|
+
const newWidth = Math.max(minWidth, startWidth + (e.clientX - startX));
|
|
405
|
+
setColumnWidths((prev) => ({
|
|
406
|
+
...prev,
|
|
407
|
+
[columnKey]: newWidth
|
|
408
|
+
}));
|
|
409
|
+
}, [columns]);
|
|
410
|
+
const handleResizeEnd = (0, import_react5.useCallback)(() => {
|
|
411
|
+
resizingRef.current = null;
|
|
412
|
+
document.removeEventListener("mousemove", handleResizeMove);
|
|
413
|
+
document.removeEventListener("mouseup", handleResizeEnd);
|
|
414
|
+
document.body.style.cursor = "";
|
|
415
|
+
document.body.style.userSelect = "";
|
|
416
|
+
}, [handleResizeMove]);
|
|
375
417
|
const getSortIcon = (column) => {
|
|
376
418
|
if (!column.sortable) return null;
|
|
377
419
|
if (sortKey !== column.key) {
|
|
378
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) });
|
|
420
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-gray-400 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) });
|
|
379
421
|
}
|
|
380
422
|
if (sortDirection === "asc") {
|
|
381
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-primary-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) });
|
|
423
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-primary-600 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) });
|
|
382
424
|
}
|
|
383
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-primary-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) });
|
|
425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-primary-600 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) });
|
|
384
426
|
};
|
|
385
427
|
const alignmentClasses = {
|
|
386
428
|
left: "text-left",
|
|
387
429
|
center: "text-center",
|
|
388
430
|
right: "text-right"
|
|
389
431
|
};
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
432
|
+
const getColumnStyle = (column) => {
|
|
433
|
+
if (columnWidths[column.key]) {
|
|
434
|
+
return { width: columnWidths[column.key], minWidth: columnWidths[column.key] };
|
|
435
|
+
}
|
|
436
|
+
if (column.width) {
|
|
437
|
+
return { width: column.width, minWidth: column.minWidth ?? 50 };
|
|
438
|
+
}
|
|
439
|
+
return { minWidth: column.minWidth ?? 50 };
|
|
440
|
+
};
|
|
441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: `overflow-x-auto ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
442
|
+
"table",
|
|
443
|
+
{
|
|
444
|
+
ref: tableRef,
|
|
445
|
+
className: "w-full border-collapse",
|
|
446
|
+
style: { tableLayout: fixedLayout || resizable ? "fixed" : "auto" },
|
|
447
|
+
children: [
|
|
448
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("tr", { className: "bg-gray-50 border-b border-gray-200", children: columns.map((column, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
449
|
+
"th",
|
|
450
|
+
{
|
|
451
|
+
className: `
|
|
452
|
+
px-4 py-3 text-sm font-semibold text-gray-700 relative
|
|
396
453
|
${alignmentClasses[column.align ?? "left"]}
|
|
397
454
|
${column.sortable ? "cursor-pointer select-none hover:bg-gray-100" : ""}
|
|
398
455
|
`,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
456
|
+
style: getColumnStyle(column),
|
|
457
|
+
onClick: () => handleHeaderClick(column),
|
|
458
|
+
children: [
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
460
|
+
"div",
|
|
461
|
+
{
|
|
462
|
+
className: `flex items-center gap-1 overflow-hidden ${column.align === "right" ? "justify-end" : column.align === "center" ? "justify-center" : ""}`,
|
|
463
|
+
children: [
|
|
464
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate", children: column.header }),
|
|
465
|
+
getSortIcon(column)
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
),
|
|
469
|
+
resizable && index < columns.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
470
|
+
"div",
|
|
471
|
+
{
|
|
472
|
+
className: "absolute top-0 right-0 w-1 h-full cursor-col-resize bg-transparent hover:bg-primary-300 transition-colors",
|
|
473
|
+
onMouseDown: (e) => handleResizeStart(e, column.key),
|
|
474
|
+
onClick: (e) => e.stopPropagation()
|
|
475
|
+
}
|
|
476
|
+
)
|
|
477
|
+
]
|
|
478
|
+
},
|
|
479
|
+
column.key
|
|
480
|
+
)) }) }),
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("tbody", { children: loading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("td", { colSpan: columns.length, className: "px-4 py-8 text-center text-gray-500", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-center gap-2", children: [
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-5 h-5 border-2 border-primary-200 border-t-primary-600 rounded-full animate-spin" }),
|
|
483
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Loading..." })
|
|
484
|
+
] }) }) }) : sortedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("td", { colSpan: columns.length, className: "px-4 py-8 text-center text-gray-500", children: emptyMessage }) }) : sortedData.map((item) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
485
|
+
"tr",
|
|
486
|
+
{
|
|
487
|
+
className: `
|
|
415
488
|
border-b border-gray-100 hover:bg-gray-50 transition-colors
|
|
416
489
|
${onRowClick ? "cursor-pointer" : ""}
|
|
417
490
|
`,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
491
|
+
onClick: () => onRowClick?.(item),
|
|
492
|
+
children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
493
|
+
"td",
|
|
494
|
+
{
|
|
495
|
+
className: `px-4 py-3 text-sm text-gray-800 overflow-hidden ${alignmentClasses[column.align ?? "left"]}`,
|
|
496
|
+
style: getColumnStyle(column),
|
|
497
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "truncate", children: column.render(item) })
|
|
498
|
+
},
|
|
499
|
+
column.key
|
|
500
|
+
))
|
|
425
501
|
},
|
|
426
|
-
|
|
427
|
-
))
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
] }) });
|
|
502
|
+
getRowKey(item)
|
|
503
|
+
)) })
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
) });
|
|
432
507
|
}
|
|
433
508
|
|
|
434
509
|
// src/components/DataTable.tsx
|