@kreativa/ui 0.1.0 → 0.1.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/dist/index.d.mts +93 -1
- package/dist/index.d.ts +93 -1
- package/dist/index.js +303 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +295 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -386,6 +386,9 @@ function Table({
|
|
|
386
386
|
] }) });
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
+
// src/components/DataTable.tsx
|
|
390
|
+
import { useState as useState3, useMemo as useMemo3, useCallback } from "react";
|
|
391
|
+
|
|
389
392
|
// src/components/Select.tsx
|
|
390
393
|
import { useState as useState2, useRef, useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
391
394
|
import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
@@ -569,9 +572,245 @@ function Select({
|
|
|
569
572
|
] });
|
|
570
573
|
}
|
|
571
574
|
|
|
572
|
-
// src/components/
|
|
573
|
-
import { useState as useState3 } from "react";
|
|
575
|
+
// src/components/DataTable.tsx
|
|
574
576
|
import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
577
|
+
function ColumnFilter({ column, value, onChange }) {
|
|
578
|
+
const filterType = column.filterType ?? "text";
|
|
579
|
+
if (filterType === "none") {
|
|
580
|
+
return /* @__PURE__ */ jsx15("div", { className: "h-9" });
|
|
581
|
+
}
|
|
582
|
+
if (filterType === "select" && column.filterOptions) {
|
|
583
|
+
return /* @__PURE__ */ jsx15(
|
|
584
|
+
Select,
|
|
585
|
+
{
|
|
586
|
+
options: [{ value: "", label: "Alla" }, ...column.filterOptions],
|
|
587
|
+
value: value || "",
|
|
588
|
+
onChange: (val) => onChange(val),
|
|
589
|
+
placeholder: column.filterPlaceholder || "Alla",
|
|
590
|
+
className: "w-full text-sm"
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
if (filterType === "multiselect" && column.filterOptions) {
|
|
595
|
+
return /* @__PURE__ */ jsx15(
|
|
596
|
+
Select,
|
|
597
|
+
{
|
|
598
|
+
options: column.filterOptions,
|
|
599
|
+
value: value || [],
|
|
600
|
+
onChange: (val) => onChange(val),
|
|
601
|
+
placeholder: column.filterPlaceholder || "Alla",
|
|
602
|
+
multiple: true,
|
|
603
|
+
className: "w-full text-sm"
|
|
604
|
+
}
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
if (filterType === "boolean") {
|
|
608
|
+
return /* @__PURE__ */ jsx15(
|
|
609
|
+
Select,
|
|
610
|
+
{
|
|
611
|
+
options: [
|
|
612
|
+
{ value: "", label: "Alla" },
|
|
613
|
+
{ value: "true", label: "Ja" },
|
|
614
|
+
{ value: "false", label: "Nej" }
|
|
615
|
+
],
|
|
616
|
+
value: value || "",
|
|
617
|
+
onChange: (val) => onChange(val),
|
|
618
|
+
className: "w-full text-sm"
|
|
619
|
+
}
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
return /* @__PURE__ */ jsx15(
|
|
623
|
+
Input,
|
|
624
|
+
{
|
|
625
|
+
type: "text",
|
|
626
|
+
value: value || "",
|
|
627
|
+
onChange: (e) => onChange(e.target.value),
|
|
628
|
+
placeholder: column.filterPlaceholder || `Filtrera ${column.header.toLowerCase()}...`,
|
|
629
|
+
className: "w-full text-sm h-9"
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
function DataTable({
|
|
634
|
+
data,
|
|
635
|
+
columns,
|
|
636
|
+
getRowKey,
|
|
637
|
+
onRowClick,
|
|
638
|
+
loading,
|
|
639
|
+
emptyMessage = "Ingen data att visa.",
|
|
640
|
+
className,
|
|
641
|
+
showFilters = true,
|
|
642
|
+
initialSortKey,
|
|
643
|
+
initialSortDirection = "asc",
|
|
644
|
+
onFilteredDataChange
|
|
645
|
+
}) {
|
|
646
|
+
const [filters, setFilters] = useState3({});
|
|
647
|
+
const [sortKey, setSortKey] = useState3(initialSortKey || null);
|
|
648
|
+
const [sortDirection, setSortDirection] = useState3(initialSortDirection);
|
|
649
|
+
const handleFilterChange = useCallback((columnKey, value) => {
|
|
650
|
+
setFilters((prev) => {
|
|
651
|
+
const next = { ...prev };
|
|
652
|
+
if (value === "" || Array.isArray(value) && value.length === 0) {
|
|
653
|
+
delete next[columnKey];
|
|
654
|
+
} else {
|
|
655
|
+
next[columnKey] = value;
|
|
656
|
+
}
|
|
657
|
+
return next;
|
|
658
|
+
});
|
|
659
|
+
}, []);
|
|
660
|
+
const clearFilters = useCallback(() => {
|
|
661
|
+
setFilters({});
|
|
662
|
+
}, []);
|
|
663
|
+
const filteredData = useMemo3(() => {
|
|
664
|
+
let result = [...data];
|
|
665
|
+
for (const [columnKey, filterValue] of Object.entries(filters)) {
|
|
666
|
+
if (!filterValue || Array.isArray(filterValue) && filterValue.length === 0) continue;
|
|
667
|
+
const column = columns.find((c) => c.key === columnKey);
|
|
668
|
+
if (!column) continue;
|
|
669
|
+
const getFilterableValue = column.filterValue || ((item) => {
|
|
670
|
+
return item[columnKey];
|
|
671
|
+
});
|
|
672
|
+
result = result.filter((item) => {
|
|
673
|
+
const itemValue = getFilterableValue(item);
|
|
674
|
+
if (itemValue === null || itemValue === void 0) {
|
|
675
|
+
return filterValue === "";
|
|
676
|
+
}
|
|
677
|
+
const filterType = column.filterType ?? "text";
|
|
678
|
+
if (filterType === "boolean") {
|
|
679
|
+
const boolFilterValue = filterValue === "true";
|
|
680
|
+
return itemValue === boolFilterValue;
|
|
681
|
+
}
|
|
682
|
+
if (filterType === "multiselect" && Array.isArray(filterValue)) {
|
|
683
|
+
return filterValue.includes(String(itemValue));
|
|
684
|
+
}
|
|
685
|
+
if (filterType === "select") {
|
|
686
|
+
return String(itemValue).toLowerCase() === String(filterValue).toLowerCase();
|
|
687
|
+
}
|
|
688
|
+
return String(itemValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
return result;
|
|
692
|
+
}, [data, filters, columns]);
|
|
693
|
+
const sortedData = useMemo3(() => {
|
|
694
|
+
if (!sortKey) return filteredData;
|
|
695
|
+
const column = columns.find((c) => c.key === sortKey);
|
|
696
|
+
if (!column) return filteredData;
|
|
697
|
+
const sorted = [...filteredData].sort((a, b) => {
|
|
698
|
+
if (column.sortFn) {
|
|
699
|
+
return column.sortFn(a, b);
|
|
700
|
+
}
|
|
701
|
+
const getValueForSort = column.filterValue || ((item) => {
|
|
702
|
+
return item[sortKey];
|
|
703
|
+
});
|
|
704
|
+
const aVal = getValueForSort(a);
|
|
705
|
+
const bVal = getValueForSort(b);
|
|
706
|
+
if (aVal === null || aVal === void 0) return 1;
|
|
707
|
+
if (bVal === null || bVal === void 0) return -1;
|
|
708
|
+
if (typeof aVal === "string" && typeof bVal === "string") {
|
|
709
|
+
return aVal.localeCompare(bVal, "sv");
|
|
710
|
+
}
|
|
711
|
+
if (aVal < bVal) return -1;
|
|
712
|
+
if (aVal > bVal) return 1;
|
|
713
|
+
return 0;
|
|
714
|
+
});
|
|
715
|
+
return sortDirection === "desc" ? sorted.reverse() : sorted;
|
|
716
|
+
}, [filteredData, sortKey, sortDirection, columns]);
|
|
717
|
+
useMemo3(() => {
|
|
718
|
+
onFilteredDataChange?.(sortedData);
|
|
719
|
+
}, [sortedData, onFilteredDataChange]);
|
|
720
|
+
const handleSort = useCallback((columnKey) => {
|
|
721
|
+
if (sortKey === columnKey) {
|
|
722
|
+
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
723
|
+
} else {
|
|
724
|
+
setSortKey(columnKey);
|
|
725
|
+
setSortDirection("asc");
|
|
726
|
+
}
|
|
727
|
+
}, [sortKey]);
|
|
728
|
+
const tableColumns = useMemo3(() => {
|
|
729
|
+
return columns.map((column) => {
|
|
730
|
+
const isSortable = column.sortable !== false && column.filterType !== "none";
|
|
731
|
+
const isCurrentSort = sortKey === column.key;
|
|
732
|
+
return {
|
|
733
|
+
key: column.key,
|
|
734
|
+
header: isSortable ? /* @__PURE__ */ jsxs11(
|
|
735
|
+
"button",
|
|
736
|
+
{
|
|
737
|
+
onClick: () => handleSort(column.key),
|
|
738
|
+
className: "flex items-center gap-1 hover:text-gray-900 transition-colors w-full text-left",
|
|
739
|
+
children: [
|
|
740
|
+
column.header,
|
|
741
|
+
isCurrentSort ? /* @__PURE__ */ jsx15("span", { className: "text-primary-600", children: sortDirection === "asc" ? "\u2191" : "\u2193" }) : /* @__PURE__ */ jsx15("span", { className: "text-gray-400 opacity-50", children: "\u2195" })
|
|
742
|
+
]
|
|
743
|
+
}
|
|
744
|
+
) : column.header,
|
|
745
|
+
render: column.render,
|
|
746
|
+
width: column.width,
|
|
747
|
+
align: column.align,
|
|
748
|
+
sortable: false
|
|
749
|
+
// We handle sorting ourselves
|
|
750
|
+
};
|
|
751
|
+
});
|
|
752
|
+
}, [columns, sortKey, sortDirection, handleSort]);
|
|
753
|
+
const hasActiveFilters = Object.keys(filters).length > 0;
|
|
754
|
+
return /* @__PURE__ */ jsxs11("div", { className, children: [
|
|
755
|
+
showFilters && /* @__PURE__ */ jsxs11("div", { className: "mb-4", children: [
|
|
756
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2 mb-2", children: [
|
|
757
|
+
/* @__PURE__ */ jsxs11("span", { className: "text-sm text-gray-500", children: [
|
|
758
|
+
"Visar ",
|
|
759
|
+
sortedData.length,
|
|
760
|
+
" av ",
|
|
761
|
+
data.length
|
|
762
|
+
] }),
|
|
763
|
+
hasActiveFilters && /* @__PURE__ */ jsx15(
|
|
764
|
+
"button",
|
|
765
|
+
{
|
|
766
|
+
onClick: clearFilters,
|
|
767
|
+
className: "text-sm text-primary-600 hover:underline",
|
|
768
|
+
children: "Rensa filter"
|
|
769
|
+
}
|
|
770
|
+
)
|
|
771
|
+
] }),
|
|
772
|
+
/* @__PURE__ */ jsx15("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-3 p-3 bg-gray-50 border border-gray-200 rounded-lg", children: columns.filter((col) => col.filterType !== "none").map((column) => /* @__PURE__ */ jsxs11("div", { className: "min-w-0", children: [
|
|
773
|
+
/* @__PURE__ */ jsx15("label", { className: "text-xs text-gray-500 mb-1 block truncate", children: column.header }),
|
|
774
|
+
/* @__PURE__ */ jsx15(
|
|
775
|
+
ColumnFilter,
|
|
776
|
+
{
|
|
777
|
+
column,
|
|
778
|
+
value: filters[column.key] || "",
|
|
779
|
+
onChange: (value) => handleFilterChange(column.key, value)
|
|
780
|
+
}
|
|
781
|
+
)
|
|
782
|
+
] }, column.key)) })
|
|
783
|
+
] }),
|
|
784
|
+
/* @__PURE__ */ jsx15(
|
|
785
|
+
Table,
|
|
786
|
+
{
|
|
787
|
+
data: sortedData,
|
|
788
|
+
columns: tableColumns,
|
|
789
|
+
getRowKey,
|
|
790
|
+
onRowClick,
|
|
791
|
+
loading,
|
|
792
|
+
emptyMessage
|
|
793
|
+
}
|
|
794
|
+
)
|
|
795
|
+
] });
|
|
796
|
+
}
|
|
797
|
+
function createFilterOptions(data, getValue, labelMap) {
|
|
798
|
+
const uniqueValues = /* @__PURE__ */ new Set();
|
|
799
|
+
for (const item of data) {
|
|
800
|
+
const value = getValue(item);
|
|
801
|
+
if (value !== null && value !== void 0 && value !== "") {
|
|
802
|
+
uniqueValues.add(value);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return Array.from(uniqueValues).sort((a, b) => a.localeCompare(b, "sv")).map((value) => ({
|
|
806
|
+
value,
|
|
807
|
+
label: labelMap?.[value] || value
|
|
808
|
+
}));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/components/Tabs.tsx
|
|
812
|
+
import { useState as useState4 } from "react";
|
|
813
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
575
814
|
function Tabs({
|
|
576
815
|
tabs,
|
|
577
816
|
activeTab: controlledActiveTab,
|
|
@@ -582,7 +821,7 @@ function Tabs({
|
|
|
582
821
|
className = ""
|
|
583
822
|
}) {
|
|
584
823
|
const isControlled = controlledActiveTab !== void 0;
|
|
585
|
-
const [internalActiveTab, setInternalActiveTab] =
|
|
824
|
+
const [internalActiveTab, setInternalActiveTab] = useState4(
|
|
586
825
|
defaultTab || tabs.find((t) => !t.disabled)?.id || tabs[0]?.id
|
|
587
826
|
);
|
|
588
827
|
const activeTab = isControlled ? controlledActiveTab : internalActiveTab;
|
|
@@ -650,11 +889,11 @@ function Tabs({
|
|
|
650
889
|
}
|
|
651
890
|
};
|
|
652
891
|
const styles = variantStyles[variant];
|
|
653
|
-
return /* @__PURE__ */
|
|
654
|
-
/* @__PURE__ */
|
|
892
|
+
return /* @__PURE__ */ jsxs12("div", { className, children: [
|
|
893
|
+
/* @__PURE__ */ jsx16("div", { className: `flex ${styles.container}`, role: "tablist", children: tabs.map((tab, index) => {
|
|
655
894
|
const isActive = tab.id === activeTab;
|
|
656
895
|
const isDisabled = tab.disabled ?? false;
|
|
657
|
-
return /* @__PURE__ */
|
|
896
|
+
return /* @__PURE__ */ jsx16(
|
|
658
897
|
"button",
|
|
659
898
|
{
|
|
660
899
|
type: "button",
|
|
@@ -666,7 +905,7 @@ function Tabs({
|
|
|
666
905
|
className: styles.tab(isActive, isDisabled),
|
|
667
906
|
onClick: () => handleTabClick(tab),
|
|
668
907
|
onKeyDown: (e) => handleKeyDown(e, index),
|
|
669
|
-
children: /* @__PURE__ */
|
|
908
|
+
children: /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-2", children: [
|
|
670
909
|
tab.icon,
|
|
671
910
|
tab.label
|
|
672
911
|
] })
|
|
@@ -674,13 +913,13 @@ function Tabs({
|
|
|
674
913
|
tab.id
|
|
675
914
|
);
|
|
676
915
|
}) }),
|
|
677
|
-
renderContent && activeContent && /* @__PURE__ */
|
|
916
|
+
renderContent && activeContent && /* @__PURE__ */ jsx16("div", { className: "pt-4", role: "tabpanel", children: activeContent })
|
|
678
917
|
] });
|
|
679
918
|
}
|
|
680
919
|
|
|
681
920
|
// src/components/DatePicker.tsx
|
|
682
|
-
import { useState as
|
|
683
|
-
import { jsx as
|
|
921
|
+
import { useState as useState5, useRef as useRef2, useEffect as useEffect3, useMemo as useMemo4 } from "react";
|
|
922
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
684
923
|
var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
685
924
|
var MONTHS = [
|
|
686
925
|
"January",
|
|
@@ -783,11 +1022,11 @@ function DatePicker({
|
|
|
783
1022
|
disabled = false,
|
|
784
1023
|
className = ""
|
|
785
1024
|
}) {
|
|
786
|
-
const [isOpen, setIsOpen] =
|
|
787
|
-
const [viewDate, setViewDate] =
|
|
788
|
-
const [hoverDate, setHoverDate] =
|
|
1025
|
+
const [isOpen, setIsOpen] = useState5(false);
|
|
1026
|
+
const [viewDate, setViewDate] = useState5(/* @__PURE__ */ new Date());
|
|
1027
|
+
const [hoverDate, setHoverDate] = useState5(null);
|
|
789
1028
|
const containerRef = useRef2(null);
|
|
790
|
-
const [selectingEnd, setSelectingEnd] =
|
|
1029
|
+
const [selectingEnd, setSelectingEnd] = useState5(false);
|
|
791
1030
|
useEffect3(() => {
|
|
792
1031
|
const handleClickOutside = (e) => {
|
|
793
1032
|
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
@@ -798,7 +1037,7 @@ function DatePicker({
|
|
|
798
1037
|
document.addEventListener("mousedown", handleClickOutside);
|
|
799
1038
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
800
1039
|
}, []);
|
|
801
|
-
const displayText =
|
|
1040
|
+
const displayText = useMemo4(() => {
|
|
802
1041
|
if (!value) return "";
|
|
803
1042
|
if (range) {
|
|
804
1043
|
const rangeValue = value;
|
|
@@ -815,7 +1054,7 @@ function DatePicker({
|
|
|
815
1054
|
}
|
|
816
1055
|
return formatDate(value);
|
|
817
1056
|
}, [value, range]);
|
|
818
|
-
const calendarDays =
|
|
1057
|
+
const calendarDays = useMemo4(() => {
|
|
819
1058
|
const year = viewDate.getFullYear();
|
|
820
1059
|
const month = viewDate.getMonth();
|
|
821
1060
|
const firstDay = new Date(year, month, 1);
|
|
@@ -898,9 +1137,9 @@ function DatePicker({
|
|
|
898
1137
|
};
|
|
899
1138
|
const today = /* @__PURE__ */ new Date();
|
|
900
1139
|
today.setHours(0, 0, 0, 0);
|
|
901
|
-
return /* @__PURE__ */
|
|
902
|
-
label && /* @__PURE__ */
|
|
903
|
-
/* @__PURE__ */
|
|
1140
|
+
return /* @__PURE__ */ jsxs13("div", { ref: containerRef, className: `relative ${className}`, children: [
|
|
1141
|
+
label && /* @__PURE__ */ jsx17("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: label }),
|
|
1142
|
+
/* @__PURE__ */ jsx17(
|
|
904
1143
|
"div",
|
|
905
1144
|
{
|
|
906
1145
|
className: `
|
|
@@ -910,15 +1149,15 @@ function DatePicker({
|
|
|
910
1149
|
transition-all
|
|
911
1150
|
`,
|
|
912
1151
|
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
913
|
-
children: /* @__PURE__ */
|
|
914
|
-
/* @__PURE__ */
|
|
915
|
-
/* @__PURE__ */
|
|
1152
|
+
children: /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between", children: [
|
|
1153
|
+
/* @__PURE__ */ jsx17("span", { className: displayText ? "text-gray-800" : "text-gray-400", children: displayText || placeholder }),
|
|
1154
|
+
/* @__PURE__ */ jsx17("svg", { className: "w-5 h-5 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx17("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) })
|
|
916
1155
|
] })
|
|
917
1156
|
}
|
|
918
1157
|
),
|
|
919
|
-
error && /* @__PURE__ */
|
|
920
|
-
isOpen && /* @__PURE__ */
|
|
921
|
-
range && /* @__PURE__ */
|
|
1158
|
+
error && /* @__PURE__ */ jsx17("p", { className: "mt-1 text-xs text-red-600", children: error }),
|
|
1159
|
+
isOpen && /* @__PURE__ */ jsx17("div", { className: "absolute z-50 mt-1 bg-white border border-gray-200 rounded-xl shadow-lg p-4", children: /* @__PURE__ */ jsxs13("div", { className: range ? "flex gap-4" : "", children: [
|
|
1160
|
+
range && /* @__PURE__ */ jsx17("div", { className: "border-r border-gray-100 pr-4 space-y-1", children: PRESET_RANGES.map((preset) => /* @__PURE__ */ jsx17(
|
|
922
1161
|
"button",
|
|
923
1162
|
{
|
|
924
1163
|
type: "button",
|
|
@@ -928,42 +1167,42 @@ function DatePicker({
|
|
|
928
1167
|
},
|
|
929
1168
|
preset.label
|
|
930
1169
|
)) }),
|
|
931
|
-
/* @__PURE__ */
|
|
932
|
-
/* @__PURE__ */
|
|
933
|
-
/* @__PURE__ */
|
|
1170
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
1171
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between mb-4", children: [
|
|
1172
|
+
/* @__PURE__ */ jsx17(
|
|
934
1173
|
"button",
|
|
935
1174
|
{
|
|
936
1175
|
type: "button",
|
|
937
1176
|
className: "p-1 hover:bg-gray-100 rounded",
|
|
938
1177
|
onClick: goToPrevMonth,
|
|
939
|
-
children: /* @__PURE__ */
|
|
1178
|
+
children: /* @__PURE__ */ jsx17("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx17("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
|
|
940
1179
|
}
|
|
941
1180
|
),
|
|
942
|
-
/* @__PURE__ */
|
|
1181
|
+
/* @__PURE__ */ jsxs13("span", { className: "text-sm font-semibold text-gray-800", children: [
|
|
943
1182
|
MONTHS[viewDate.getMonth()],
|
|
944
1183
|
" ",
|
|
945
1184
|
viewDate.getFullYear()
|
|
946
1185
|
] }),
|
|
947
|
-
/* @__PURE__ */
|
|
1186
|
+
/* @__PURE__ */ jsx17(
|
|
948
1187
|
"button",
|
|
949
1188
|
{
|
|
950
1189
|
type: "button",
|
|
951
1190
|
className: "p-1 hover:bg-gray-100 rounded",
|
|
952
1191
|
onClick: goToNextMonth,
|
|
953
|
-
children: /* @__PURE__ */
|
|
1192
|
+
children: /* @__PURE__ */ jsx17("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx17("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
|
|
954
1193
|
}
|
|
955
1194
|
)
|
|
956
1195
|
] }),
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsx17("div", { className: "grid grid-cols-7 gap-1 mb-2", children: WEEKDAYS.map((day) => /* @__PURE__ */ jsx17("div", { className: "text-center text-xs font-medium text-gray-500 py-1", children: day }, day)) }),
|
|
1197
|
+
/* @__PURE__ */ jsx17("div", { className: "grid grid-cols-7 gap-1", children: calendarDays.map((date, index) => {
|
|
959
1198
|
if (!date) {
|
|
960
|
-
return /* @__PURE__ */
|
|
1199
|
+
return /* @__PURE__ */ jsx17("div", { className: "w-9 h-9" }, `empty-${index}`);
|
|
961
1200
|
}
|
|
962
1201
|
const isDisabled = isDateDisabled(date);
|
|
963
1202
|
const isSelected = isDateSelected(date);
|
|
964
1203
|
const inRange = isDateInRange(date);
|
|
965
1204
|
const isToday = isSameDay(date, today);
|
|
966
|
-
return /* @__PURE__ */
|
|
1205
|
+
return /* @__PURE__ */ jsx17(
|
|
967
1206
|
"button",
|
|
968
1207
|
{
|
|
969
1208
|
type: "button",
|
|
@@ -989,8 +1228,8 @@ function DatePicker({
|
|
|
989
1228
|
}
|
|
990
1229
|
|
|
991
1230
|
// src/components/Timer.tsx
|
|
992
|
-
import { useState as
|
|
993
|
-
import { jsx as
|
|
1231
|
+
import { useState as useState6, useEffect as useEffect4, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
1232
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
994
1233
|
function formatTime(totalSeconds) {
|
|
995
1234
|
const hours = Math.floor(totalSeconds / 3600);
|
|
996
1235
|
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
@@ -1014,8 +1253,8 @@ function Timer({
|
|
|
1014
1253
|
elapsedSeconds: controlledElapsedSeconds
|
|
1015
1254
|
}) {
|
|
1016
1255
|
const isControlled = controlledIsRunning !== void 0;
|
|
1017
|
-
const [internalIsRunning, setInternalIsRunning] =
|
|
1018
|
-
const [internalElapsedSeconds, setInternalElapsedSeconds] =
|
|
1256
|
+
const [internalIsRunning, setInternalIsRunning] = useState6(false);
|
|
1257
|
+
const [internalElapsedSeconds, setInternalElapsedSeconds] = useState6(initialSeconds);
|
|
1019
1258
|
const isRunning = isControlled ? controlledIsRunning : internalIsRunning;
|
|
1020
1259
|
const elapsedSeconds = isControlled ? controlledElapsedSeconds ?? 0 : internalElapsedSeconds;
|
|
1021
1260
|
const intervalRef = useRef3(null);
|
|
@@ -1050,7 +1289,7 @@ function Timer({
|
|
|
1050
1289
|
}
|
|
1051
1290
|
};
|
|
1052
1291
|
}, [isRunning, isControlled, onTick]);
|
|
1053
|
-
const handleStart =
|
|
1292
|
+
const handleStart = useCallback2(() => {
|
|
1054
1293
|
const now = /* @__PURE__ */ new Date();
|
|
1055
1294
|
startTimeRef.current = now;
|
|
1056
1295
|
if (!isControlled) {
|
|
@@ -1058,13 +1297,13 @@ function Timer({
|
|
|
1058
1297
|
}
|
|
1059
1298
|
onStart?.(now);
|
|
1060
1299
|
}, [isControlled, onStart]);
|
|
1061
|
-
const handleStop =
|
|
1300
|
+
const handleStop = useCallback2(() => {
|
|
1062
1301
|
if (!isControlled) {
|
|
1063
1302
|
setInternalIsRunning(false);
|
|
1064
1303
|
}
|
|
1065
1304
|
onStop?.(elapsedSeconds);
|
|
1066
1305
|
}, [isControlled, elapsedSeconds, onStop]);
|
|
1067
|
-
const handleReset =
|
|
1306
|
+
const handleReset = useCallback2(() => {
|
|
1068
1307
|
if (!isControlled) {
|
|
1069
1308
|
setInternalIsRunning(false);
|
|
1070
1309
|
setInternalElapsedSeconds(0);
|
|
@@ -1072,7 +1311,7 @@ function Timer({
|
|
|
1072
1311
|
startTimeRef.current = null;
|
|
1073
1312
|
onReset?.();
|
|
1074
1313
|
}, [isControlled, onReset]);
|
|
1075
|
-
const handleToggle =
|
|
1314
|
+
const handleToggle = useCallback2(() => {
|
|
1076
1315
|
if (isRunning) {
|
|
1077
1316
|
handleStop();
|
|
1078
1317
|
} else {
|
|
@@ -1100,10 +1339,10 @@ function Timer({
|
|
|
1100
1339
|
}
|
|
1101
1340
|
};
|
|
1102
1341
|
const styles = sizeClasses3[size];
|
|
1103
|
-
return /* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1105
|
-
/* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */
|
|
1342
|
+
return /* @__PURE__ */ jsxs14("div", { className: `flex items-center ${styles.container} ${className}`, children: [
|
|
1343
|
+
/* @__PURE__ */ jsx18("div", { className: `font-mono font-bold text-gray-800 ${styles.time} tabular-nums`, children: formatTime(elapsedSeconds) }),
|
|
1344
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
1345
|
+
/* @__PURE__ */ jsx18(
|
|
1107
1346
|
"button",
|
|
1108
1347
|
{
|
|
1109
1348
|
type: "button",
|
|
@@ -1117,14 +1356,14 @@ function Timer({
|
|
|
1117
1356
|
title: isRunning ? "Pause" : "Start",
|
|
1118
1357
|
children: isRunning ? (
|
|
1119
1358
|
// Pause icon
|
|
1120
|
-
/* @__PURE__ */
|
|
1359
|
+
/* @__PURE__ */ jsx18("svg", { className: styles.icon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx18("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" }) })
|
|
1121
1360
|
) : (
|
|
1122
1361
|
// Play icon
|
|
1123
|
-
/* @__PURE__ */
|
|
1362
|
+
/* @__PURE__ */ jsx18("svg", { className: styles.icon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx18("path", { d: "M8 5v14l11-7z" }) })
|
|
1124
1363
|
)
|
|
1125
1364
|
}
|
|
1126
1365
|
),
|
|
1127
|
-
elapsedSeconds > 0 && /* @__PURE__ */
|
|
1366
|
+
elapsedSeconds > 0 && /* @__PURE__ */ jsx18(
|
|
1128
1367
|
"button",
|
|
1129
1368
|
{
|
|
1130
1369
|
type: "button",
|
|
@@ -1135,10 +1374,10 @@ function Timer({
|
|
|
1135
1374
|
transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500
|
|
1136
1375
|
`,
|
|
1137
1376
|
title: "Stop",
|
|
1138
|
-
children: /* @__PURE__ */
|
|
1377
|
+
children: /* @__PURE__ */ jsx18("svg", { className: styles.icon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx18("path", { d: "M6 6h12v12H6z" }) })
|
|
1139
1378
|
}
|
|
1140
1379
|
),
|
|
1141
|
-
showReset && elapsedSeconds > 0 && !isRunning && /* @__PURE__ */
|
|
1380
|
+
showReset && elapsedSeconds > 0 && !isRunning && /* @__PURE__ */ jsx18(
|
|
1142
1381
|
"button",
|
|
1143
1382
|
{
|
|
1144
1383
|
type: "button",
|
|
@@ -1149,7 +1388,7 @@ function Timer({
|
|
|
1149
1388
|
transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400
|
|
1150
1389
|
`,
|
|
1151
1390
|
title: "Reset",
|
|
1152
|
-
children: /* @__PURE__ */
|
|
1391
|
+
children: /* @__PURE__ */ jsx18("svg", { className: styles.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx18("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) })
|
|
1153
1392
|
}
|
|
1154
1393
|
)
|
|
1155
1394
|
] })
|
|
@@ -1158,6 +1397,7 @@ function Timer({
|
|
|
1158
1397
|
export {
|
|
1159
1398
|
Button,
|
|
1160
1399
|
Card,
|
|
1400
|
+
DataTable,
|
|
1161
1401
|
DatePicker,
|
|
1162
1402
|
EmptyState,
|
|
1163
1403
|
FormButtonGroup,
|
|
@@ -1173,6 +1413,7 @@ export {
|
|
|
1173
1413
|
Table,
|
|
1174
1414
|
Tabs,
|
|
1175
1415
|
Timer,
|
|
1416
|
+
createFilterOptions,
|
|
1176
1417
|
formatTime
|
|
1177
1418
|
};
|
|
1178
1419
|
//# sourceMappingURL=index.mjs.map
|