@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.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();
@@ -646,7 +721,7 @@ function DataTable({
646
721
  const [filters, setFilters] = useState3({});
647
722
  const [sortKey, setSortKey] = useState3(initialSortKey || null);
648
723
  const [sortDirection, setSortDirection] = useState3(initialSortDirection);
649
- const handleFilterChange = useCallback((columnKey, value) => {
724
+ const handleFilterChange = useCallback2((columnKey, value) => {
650
725
  setFilters((prev) => {
651
726
  const next = { ...prev };
652
727
  if (value === "" || Array.isArray(value) && value.length === 0) {
@@ -657,7 +732,7 @@ function DataTable({
657
732
  return next;
658
733
  });
659
734
  }, []);
660
- const clearFilters = useCallback(() => {
735
+ const clearFilters = useCallback2(() => {
661
736
  setFilters({});
662
737
  }, []);
663
738
  const filteredData = useMemo3(() => {
@@ -717,7 +792,7 @@ function DataTable({
717
792
  useMemo3(() => {
718
793
  onFilteredDataChange?.(sortedData);
719
794
  }, [sortedData, onFilteredDataChange]);
720
- const handleSort = useCallback((columnKey) => {
795
+ const handleSort = useCallback2((columnKey) => {
721
796
  if (sortKey === columnKey) {
722
797
  setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
723
798
  } else {
@@ -918,7 +993,7 @@ function Tabs({
918
993
  }
919
994
 
920
995
  // src/components/DatePicker.tsx
921
- import { useState as useState5, useRef as useRef2, useEffect as useEffect3, useMemo as useMemo4 } from "react";
996
+ import { useState as useState5, useRef as useRef3, useEffect as useEffect3, useMemo as useMemo4 } from "react";
922
997
  import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
923
998
  var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
924
999
  var MONTHS = [
@@ -1025,7 +1100,7 @@ function DatePicker({
1025
1100
  const [isOpen, setIsOpen] = useState5(false);
1026
1101
  const [viewDate, setViewDate] = useState5(/* @__PURE__ */ new Date());
1027
1102
  const [hoverDate, setHoverDate] = useState5(null);
1028
- const containerRef = useRef2(null);
1103
+ const containerRef = useRef3(null);
1029
1104
  const [selectingEnd, setSelectingEnd] = useState5(false);
1030
1105
  useEffect3(() => {
1031
1106
  const handleClickOutside = (e) => {
@@ -1228,7 +1303,7 @@ function DatePicker({
1228
1303
  }
1229
1304
 
1230
1305
  // src/components/Timer.tsx
1231
- import { useState as useState6, useEffect as useEffect4, useCallback as useCallback2, useRef as useRef3 } from "react";
1306
+ import { useState as useState6, useEffect as useEffect4, useCallback as useCallback3, useRef as useRef4 } from "react";
1232
1307
  import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1233
1308
  function formatTime(totalSeconds) {
1234
1309
  const hours = Math.floor(totalSeconds / 3600);
@@ -1257,8 +1332,8 @@ function Timer({
1257
1332
  const [internalElapsedSeconds, setInternalElapsedSeconds] = useState6(initialSeconds);
1258
1333
  const isRunning = isControlled ? controlledIsRunning : internalIsRunning;
1259
1334
  const elapsedSeconds = isControlled ? controlledElapsedSeconds ?? 0 : internalElapsedSeconds;
1260
- const intervalRef = useRef3(null);
1261
- const startTimeRef = useRef3(null);
1335
+ const intervalRef = useRef4(null);
1336
+ const startTimeRef = useRef4(null);
1262
1337
  useEffect4(() => {
1263
1338
  return () => {
1264
1339
  if (intervalRef.current) {
@@ -1289,7 +1364,7 @@ function Timer({
1289
1364
  }
1290
1365
  };
1291
1366
  }, [isRunning, isControlled, onTick]);
1292
- const handleStart = useCallback2(() => {
1367
+ const handleStart = useCallback3(() => {
1293
1368
  const now = /* @__PURE__ */ new Date();
1294
1369
  startTimeRef.current = now;
1295
1370
  if (!isControlled) {
@@ -1297,13 +1372,13 @@ function Timer({
1297
1372
  }
1298
1373
  onStart?.(now);
1299
1374
  }, [isControlled, onStart]);
1300
- const handleStop = useCallback2(() => {
1375
+ const handleStop = useCallback3(() => {
1301
1376
  if (!isControlled) {
1302
1377
  setInternalIsRunning(false);
1303
1378
  }
1304
1379
  onStop?.(elapsedSeconds);
1305
1380
  }, [isControlled, elapsedSeconds, onStop]);
1306
- const handleReset = useCallback2(() => {
1381
+ const handleReset = useCallback3(() => {
1307
1382
  if (!isControlled) {
1308
1383
  setInternalIsRunning(false);
1309
1384
  setInternalElapsedSeconds(0);
@@ -1311,7 +1386,7 @@ function Timer({
1311
1386
  startTimeRef.current = null;
1312
1387
  onReset?.();
1313
1388
  }, [isControlled, onReset]);
1314
- const handleToggle = useCallback2(() => {
1389
+ const handleToggle = useCallback3(() => {
1315
1390
  if (isRunning) {
1316
1391
  handleStop();
1317
1392
  } else {