@kreativa/ui 0.1.1 → 0.1.3

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.mjs CHANGED
@@ -286,7 +286,7 @@ function FormButtonGroup({
286
286
  }
287
287
 
288
288
  // src/components/Table.tsx
289
- import { useState, useMemo } from "react";
289
+ import { useState, useMemo, useRef, useCallback } from "react";
290
290
  import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
291
291
  function Table({
292
292
  data,
@@ -295,10 +295,15 @@ function Table({
295
295
  onRowClick,
296
296
  loading = false,
297
297
  emptyMessage = "No data available",
298
- className = ""
298
+ className = "",
299
+ resizable = false,
300
+ fixedLayout = false
299
301
  }) {
300
302
  const [sortKey, setSortKey] = useState(null);
301
303
  const [sortDirection, setSortDirection] = useState(null);
304
+ const [columnWidths, setColumnWidths] = useState({});
305
+ const tableRef = useRef(null);
306
+ const resizingRef = useRef(null);
302
307
  const handleHeaderClick = (column) => {
303
308
  if (!column.sortable) return;
304
309
  if (sortKey === column.key) {
@@ -327,70 +332,140 @@ function Table({
327
332
  });
328
333
  return sortDirection === "desc" ? sorted.reverse() : sorted;
329
334
  }, [data, columns, sortKey, sortDirection]);
335
+ const handleResizeStart = useCallback(
336
+ (e, columnKey) => {
337
+ e.preventDefault();
338
+ e.stopPropagation();
339
+ const headerCell = e.currentTarget.parentElement;
340
+ if (!headerCell) return;
341
+ const startWidth = headerCell.getBoundingClientRect().width;
342
+ resizingRef.current = {
343
+ columnKey,
344
+ startX: e.clientX,
345
+ startWidth
346
+ };
347
+ document.addEventListener("mousemove", handleResizeMove);
348
+ document.addEventListener("mouseup", handleResizeEnd);
349
+ document.body.style.cursor = "col-resize";
350
+ document.body.style.userSelect = "none";
351
+ },
352
+ []
353
+ );
354
+ const handleResizeMove = useCallback((e) => {
355
+ if (!resizingRef.current) return;
356
+ const { columnKey, startX, startWidth } = resizingRef.current;
357
+ const column = columns.find((c) => c.key === columnKey);
358
+ const minWidth = column?.minWidth ?? 50;
359
+ const newWidth = Math.max(minWidth, startWidth + (e.clientX - startX));
360
+ setColumnWidths((prev) => ({
361
+ ...prev,
362
+ [columnKey]: newWidth
363
+ }));
364
+ }, [columns]);
365
+ const handleResizeEnd = useCallback(() => {
366
+ resizingRef.current = null;
367
+ document.removeEventListener("mousemove", handleResizeMove);
368
+ document.removeEventListener("mouseup", handleResizeEnd);
369
+ document.body.style.cursor = "";
370
+ document.body.style.userSelect = "";
371
+ }, [handleResizeMove]);
330
372
  const getSortIcon = (column) => {
331
373
  if (!column.sortable) return null;
332
374
  if (sortKey !== column.key) {
333
- return /* @__PURE__ */ jsx13("svg", { className: "w-4 h-4 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) });
375
+ return /* @__PURE__ */ jsx13("svg", { className: "w-4 h-4 text-gray-400 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) });
334
376
  }
335
377
  if (sortDirection === "asc") {
336
- return /* @__PURE__ */ jsx13("svg", { className: "w-4 h-4 text-primary-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) });
378
+ return /* @__PURE__ */ jsx13("svg", { className: "w-4 h-4 text-primary-600 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) });
337
379
  }
338
- return /* @__PURE__ */ jsx13("svg", { className: "w-4 h-4 text-primary-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) });
380
+ return /* @__PURE__ */ jsx13("svg", { className: "w-4 h-4 text-primary-600 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) });
339
381
  };
340
382
  const alignmentClasses = {
341
383
  left: "text-left",
342
384
  center: "text-center",
343
385
  right: "text-right"
344
386
  };
345
- return /* @__PURE__ */ jsx13("div", { className: `overflow-x-auto ${className}`, children: /* @__PURE__ */ jsxs9("table", { className: "w-full border-collapse", children: [
346
- /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsx13("tr", { className: "bg-gray-50 border-b border-gray-200", children: columns.map((column) => /* @__PURE__ */ jsx13(
347
- "th",
348
- {
349
- className: `
350
- px-4 py-3 text-sm font-semibold text-gray-700
387
+ const getColumnStyle = (column) => {
388
+ if (columnWidths[column.key]) {
389
+ return { width: columnWidths[column.key], minWidth: columnWidths[column.key] };
390
+ }
391
+ if (column.width) {
392
+ return { width: column.width, minWidth: column.minWidth ?? 50 };
393
+ }
394
+ return { minWidth: column.minWidth ?? 50 };
395
+ };
396
+ return /* @__PURE__ */ jsx13("div", { className: `overflow-x-auto ${className}`, children: /* @__PURE__ */ jsxs9(
397
+ "table",
398
+ {
399
+ ref: tableRef,
400
+ className: "w-full border-collapse",
401
+ style: { tableLayout: fixedLayout || resizable ? "fixed" : "auto" },
402
+ children: [
403
+ /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsx13("tr", { className: "bg-gray-50 border-b border-gray-200", children: columns.map((column, index) => /* @__PURE__ */ jsxs9(
404
+ "th",
405
+ {
406
+ className: `
407
+ px-4 py-3 text-sm font-semibold text-gray-700 relative
351
408
  ${alignmentClasses[column.align ?? "left"]}
352
409
  ${column.sortable ? "cursor-pointer select-none hover:bg-gray-100" : ""}
353
410
  `,
354
- style: { width: column.width },
355
- onClick: () => handleHeaderClick(column),
356
- children: /* @__PURE__ */ jsxs9("div", { className: `flex items-center gap-1 ${column.align === "right" ? "justify-end" : column.align === "center" ? "justify-center" : ""}`, children: [
357
- /* @__PURE__ */ jsx13("span", { children: column.header }),
358
- getSortIcon(column)
359
- ] })
360
- },
361
- column.key
362
- )) }) }),
363
- /* @__PURE__ */ jsx13("tbody", { children: loading ? /* @__PURE__ */ jsx13("tr", { children: /* @__PURE__ */ jsx13("td", { colSpan: columns.length, className: "px-4 py-8 text-center text-gray-500", children: /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-center gap-2", children: [
364
- /* @__PURE__ */ jsx13("div", { className: "w-5 h-5 border-2 border-primary-200 border-t-primary-600 rounded-full animate-spin" }),
365
- /* @__PURE__ */ jsx13("span", { children: "Loading..." })
366
- ] }) }) }) : sortedData.length === 0 ? /* @__PURE__ */ jsx13("tr", { children: /* @__PURE__ */ jsx13("td", { colSpan: columns.length, className: "px-4 py-8 text-center text-gray-500", children: emptyMessage }) }) : sortedData.map((item) => /* @__PURE__ */ jsx13(
367
- "tr",
368
- {
369
- className: `
411
+ style: getColumnStyle(column),
412
+ onClick: () => handleHeaderClick(column),
413
+ children: [
414
+ /* @__PURE__ */ jsxs9(
415
+ "div",
416
+ {
417
+ className: `flex items-center gap-1 overflow-hidden ${column.align === "right" ? "justify-end" : column.align === "center" ? "justify-center" : ""}`,
418
+ children: [
419
+ /* @__PURE__ */ jsx13("span", { className: "truncate", children: column.header }),
420
+ getSortIcon(column)
421
+ ]
422
+ }
423
+ ),
424
+ resizable && index < columns.length - 1 && /* @__PURE__ */ jsx13(
425
+ "div",
426
+ {
427
+ className: "absolute top-0 right-0 w-1 h-full cursor-col-resize bg-transparent hover:bg-primary-300 transition-colors",
428
+ onMouseDown: (e) => handleResizeStart(e, column.key),
429
+ onClick: (e) => e.stopPropagation()
430
+ }
431
+ )
432
+ ]
433
+ },
434
+ column.key
435
+ )) }) }),
436
+ /* @__PURE__ */ jsx13("tbody", { children: loading ? /* @__PURE__ */ jsx13("tr", { children: /* @__PURE__ */ jsx13("td", { colSpan: columns.length, className: "px-4 py-8 text-center text-gray-500", children: /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-center gap-2", children: [
437
+ /* @__PURE__ */ jsx13("div", { className: "w-5 h-5 border-2 border-primary-200 border-t-primary-600 rounded-full animate-spin" }),
438
+ /* @__PURE__ */ jsx13("span", { children: "Loading..." })
439
+ ] }) }) }) : sortedData.length === 0 ? /* @__PURE__ */ jsx13("tr", { children: /* @__PURE__ */ jsx13("td", { colSpan: columns.length, className: "px-4 py-8 text-center text-gray-500", children: emptyMessage }) }) : sortedData.map((item) => /* @__PURE__ */ jsx13(
440
+ "tr",
441
+ {
442
+ className: `
370
443
  border-b border-gray-100 hover:bg-gray-50 transition-colors
371
444
  ${onRowClick ? "cursor-pointer" : ""}
372
445
  `,
373
- onClick: () => onRowClick?.(item),
374
- children: columns.map((column) => /* @__PURE__ */ jsx13(
375
- "td",
376
- {
377
- className: `px-4 py-3 text-sm text-gray-800 ${alignmentClasses[column.align ?? "left"]}`,
378
- style: { width: column.width },
379
- children: column.render(item)
446
+ onClick: () => onRowClick?.(item),
447
+ children: columns.map((column) => /* @__PURE__ */ jsx13(
448
+ "td",
449
+ {
450
+ className: `px-4 py-3 text-sm text-gray-800 overflow-hidden ${alignmentClasses[column.align ?? "left"]}`,
451
+ style: getColumnStyle(column),
452
+ children: /* @__PURE__ */ jsx13("div", { className: "truncate", children: column.render(item) })
453
+ },
454
+ column.key
455
+ ))
380
456
  },
381
- column.key
382
- ))
383
- },
384
- getRowKey(item)
385
- )) })
386
- ] }) });
457
+ getRowKey(item)
458
+ )) })
459
+ ]
460
+ }
461
+ ) });
387
462
  }
388
463
 
389
464
  // src/components/DataTable.tsx
390
- import { useState as useState3, useMemo as useMemo3, useCallback } from "react";
465
+ import { useState as useState3, useMemo as useMemo3, useCallback as useCallback2 } from "react";
391
466
 
392
467
  // src/components/Select.tsx
393
- import { useState as useState2, useRef, useEffect as useEffect2, useMemo as useMemo2 } from "react";
468
+ import { useState as useState2, useRef as useRef2, useEffect as useEffect2, useMemo as useMemo2 } from "react";
394
469
  import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
395
470
  function Select({
396
471
  options,
@@ -407,8 +482,8 @@ function Select({
407
482
  const [isOpen, setIsOpen] = useState2(false);
408
483
  const [searchTerm, setSearchTerm] = useState2("");
409
484
  const [highlightedIndex, setHighlightedIndex] = useState2(0);
410
- const containerRef = useRef(null);
411
- const inputRef = useRef(null);
485
+ const containerRef = useRef2(null);
486
+ const inputRef = useRef2(null);
412
487
  const filteredOptions = useMemo2(() => {
413
488
  if (!searchTerm) return options;
414
489
  const term = searchTerm.toLowerCase();
@@ -641,12 +716,14 @@ function DataTable({
641
716
  showFilters = true,
642
717
  initialSortKey,
643
718
  initialSortDirection = "asc",
644
- onFilteredDataChange
719
+ onFilteredDataChange,
720
+ resizable = false,
721
+ fixedLayout = false
645
722
  }) {
646
723
  const [filters, setFilters] = useState3({});
647
724
  const [sortKey, setSortKey] = useState3(initialSortKey || null);
648
725
  const [sortDirection, setSortDirection] = useState3(initialSortDirection);
649
- const handleFilterChange = useCallback((columnKey, value) => {
726
+ const handleFilterChange = useCallback2((columnKey, value) => {
650
727
  setFilters((prev) => {
651
728
  const next = { ...prev };
652
729
  if (value === "" || Array.isArray(value) && value.length === 0) {
@@ -657,7 +734,7 @@ function DataTable({
657
734
  return next;
658
735
  });
659
736
  }, []);
660
- const clearFilters = useCallback(() => {
737
+ const clearFilters = useCallback2(() => {
661
738
  setFilters({});
662
739
  }, []);
663
740
  const filteredData = useMemo3(() => {
@@ -717,7 +794,7 @@ function DataTable({
717
794
  useMemo3(() => {
718
795
  onFilteredDataChange?.(sortedData);
719
796
  }, [sortedData, onFilteredDataChange]);
720
- const handleSort = useCallback((columnKey) => {
797
+ const handleSort = useCallback2((columnKey) => {
721
798
  if (sortKey === columnKey) {
722
799
  setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
723
800
  } else {
@@ -789,7 +866,9 @@ function DataTable({
789
866
  getRowKey,
790
867
  onRowClick,
791
868
  loading,
792
- emptyMessage
869
+ emptyMessage,
870
+ resizable,
871
+ fixedLayout
793
872
  }
794
873
  )
795
874
  ] });
@@ -918,7 +997,7 @@ function Tabs({
918
997
  }
919
998
 
920
999
  // src/components/DatePicker.tsx
921
- import { useState as useState5, useRef as useRef2, useEffect as useEffect3, useMemo as useMemo4 } from "react";
1000
+ import { useState as useState5, useRef as useRef3, useEffect as useEffect3, useMemo as useMemo4 } from "react";
922
1001
  import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
923
1002
  var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
924
1003
  var MONTHS = [
@@ -1025,7 +1104,7 @@ function DatePicker({
1025
1104
  const [isOpen, setIsOpen] = useState5(false);
1026
1105
  const [viewDate, setViewDate] = useState5(/* @__PURE__ */ new Date());
1027
1106
  const [hoverDate, setHoverDate] = useState5(null);
1028
- const containerRef = useRef2(null);
1107
+ const containerRef = useRef3(null);
1029
1108
  const [selectingEnd, setSelectingEnd] = useState5(false);
1030
1109
  useEffect3(() => {
1031
1110
  const handleClickOutside = (e) => {
@@ -1228,7 +1307,7 @@ function DatePicker({
1228
1307
  }
1229
1308
 
1230
1309
  // src/components/Timer.tsx
1231
- import { useState as useState6, useEffect as useEffect4, useCallback as useCallback2, useRef as useRef3 } from "react";
1310
+ import { useState as useState6, useEffect as useEffect4, useCallback as useCallback3, useRef as useRef4 } from "react";
1232
1311
  import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1233
1312
  function formatTime(totalSeconds) {
1234
1313
  const hours = Math.floor(totalSeconds / 3600);
@@ -1257,8 +1336,8 @@ function Timer({
1257
1336
  const [internalElapsedSeconds, setInternalElapsedSeconds] = useState6(initialSeconds);
1258
1337
  const isRunning = isControlled ? controlledIsRunning : internalIsRunning;
1259
1338
  const elapsedSeconds = isControlled ? controlledElapsedSeconds ?? 0 : internalElapsedSeconds;
1260
- const intervalRef = useRef3(null);
1261
- const startTimeRef = useRef3(null);
1339
+ const intervalRef = useRef4(null);
1340
+ const startTimeRef = useRef4(null);
1262
1341
  useEffect4(() => {
1263
1342
  return () => {
1264
1343
  if (intervalRef.current) {
@@ -1289,7 +1368,7 @@ function Timer({
1289
1368
  }
1290
1369
  };
1291
1370
  }, [isRunning, isControlled, onTick]);
1292
- const handleStart = useCallback2(() => {
1371
+ const handleStart = useCallback3(() => {
1293
1372
  const now = /* @__PURE__ */ new Date();
1294
1373
  startTimeRef.current = now;
1295
1374
  if (!isControlled) {
@@ -1297,13 +1376,13 @@ function Timer({
1297
1376
  }
1298
1377
  onStart?.(now);
1299
1378
  }, [isControlled, onStart]);
1300
- const handleStop = useCallback2(() => {
1379
+ const handleStop = useCallback3(() => {
1301
1380
  if (!isControlled) {
1302
1381
  setInternalIsRunning(false);
1303
1382
  }
1304
1383
  onStop?.(elapsedSeconds);
1305
1384
  }, [isControlled, elapsedSeconds, onStop]);
1306
- const handleReset = useCallback2(() => {
1385
+ const handleReset = useCallback3(() => {
1307
1386
  if (!isControlled) {
1308
1387
  setInternalIsRunning(false);
1309
1388
  setInternalElapsedSeconds(0);
@@ -1311,7 +1390,7 @@ function Timer({
1311
1390
  startTimeRef.current = null;
1312
1391
  onReset?.();
1313
1392
  }, [isControlled, onReset]);
1314
- const handleToggle = useCallback2(() => {
1393
+ const handleToggle = useCallback3(() => {
1315
1394
  if (isRunning) {
1316
1395
  handleStop();
1317
1396
  } else {