@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.js
CHANGED
|
@@ -22,6 +22,7 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Button: () => Button,
|
|
24
24
|
Card: () => Card,
|
|
25
|
+
DataTable: () => DataTable,
|
|
25
26
|
DatePicker: () => DatePicker,
|
|
26
27
|
EmptyState: () => EmptyState,
|
|
27
28
|
FormButtonGroup: () => FormButtonGroup,
|
|
@@ -37,6 +38,7 @@ __export(index_exports, {
|
|
|
37
38
|
Table: () => Table,
|
|
38
39
|
Tabs: () => Tabs,
|
|
39
40
|
Timer: () => Timer,
|
|
41
|
+
createFilterOptions: () => createFilterOptions,
|
|
40
42
|
formatTime: () => formatTime
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -429,6 +431,9 @@ function Table({
|
|
|
429
431
|
] }) });
|
|
430
432
|
}
|
|
431
433
|
|
|
434
|
+
// src/components/DataTable.tsx
|
|
435
|
+
var import_react7 = require("react");
|
|
436
|
+
|
|
432
437
|
// src/components/Select.tsx
|
|
433
438
|
var import_react6 = require("react");
|
|
434
439
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
@@ -612,9 +617,245 @@ function Select({
|
|
|
612
617
|
] });
|
|
613
618
|
}
|
|
614
619
|
|
|
615
|
-
// src/components/
|
|
616
|
-
var import_react7 = require("react");
|
|
620
|
+
// src/components/DataTable.tsx
|
|
617
621
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
622
|
+
function ColumnFilter({ column, value, onChange }) {
|
|
623
|
+
const filterType = column.filterType ?? "text";
|
|
624
|
+
if (filterType === "none") {
|
|
625
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-9" });
|
|
626
|
+
}
|
|
627
|
+
if (filterType === "select" && column.filterOptions) {
|
|
628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
629
|
+
Select,
|
|
630
|
+
{
|
|
631
|
+
options: [{ value: "", label: "Alla" }, ...column.filterOptions],
|
|
632
|
+
value: value || "",
|
|
633
|
+
onChange: (val) => onChange(val),
|
|
634
|
+
placeholder: column.filterPlaceholder || "Alla",
|
|
635
|
+
className: "w-full text-sm"
|
|
636
|
+
}
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
if (filterType === "multiselect" && column.filterOptions) {
|
|
640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
641
|
+
Select,
|
|
642
|
+
{
|
|
643
|
+
options: column.filterOptions,
|
|
644
|
+
value: value || [],
|
|
645
|
+
onChange: (val) => onChange(val),
|
|
646
|
+
placeholder: column.filterPlaceholder || "Alla",
|
|
647
|
+
multiple: true,
|
|
648
|
+
className: "w-full text-sm"
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
if (filterType === "boolean") {
|
|
653
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
654
|
+
Select,
|
|
655
|
+
{
|
|
656
|
+
options: [
|
|
657
|
+
{ value: "", label: "Alla" },
|
|
658
|
+
{ value: "true", label: "Ja" },
|
|
659
|
+
{ value: "false", label: "Nej" }
|
|
660
|
+
],
|
|
661
|
+
value: value || "",
|
|
662
|
+
onChange: (val) => onChange(val),
|
|
663
|
+
className: "w-full text-sm"
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
668
|
+
Input,
|
|
669
|
+
{
|
|
670
|
+
type: "text",
|
|
671
|
+
value: value || "",
|
|
672
|
+
onChange: (e) => onChange(e.target.value),
|
|
673
|
+
placeholder: column.filterPlaceholder || `Filtrera ${column.header.toLowerCase()}...`,
|
|
674
|
+
className: "w-full text-sm h-9"
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
function DataTable({
|
|
679
|
+
data,
|
|
680
|
+
columns,
|
|
681
|
+
getRowKey,
|
|
682
|
+
onRowClick,
|
|
683
|
+
loading,
|
|
684
|
+
emptyMessage = "Ingen data att visa.",
|
|
685
|
+
className,
|
|
686
|
+
showFilters = true,
|
|
687
|
+
initialSortKey,
|
|
688
|
+
initialSortDirection = "asc",
|
|
689
|
+
onFilteredDataChange
|
|
690
|
+
}) {
|
|
691
|
+
const [filters, setFilters] = (0, import_react7.useState)({});
|
|
692
|
+
const [sortKey, setSortKey] = (0, import_react7.useState)(initialSortKey || null);
|
|
693
|
+
const [sortDirection, setSortDirection] = (0, import_react7.useState)(initialSortDirection);
|
|
694
|
+
const handleFilterChange = (0, import_react7.useCallback)((columnKey, value) => {
|
|
695
|
+
setFilters((prev) => {
|
|
696
|
+
const next = { ...prev };
|
|
697
|
+
if (value === "" || Array.isArray(value) && value.length === 0) {
|
|
698
|
+
delete next[columnKey];
|
|
699
|
+
} else {
|
|
700
|
+
next[columnKey] = value;
|
|
701
|
+
}
|
|
702
|
+
return next;
|
|
703
|
+
});
|
|
704
|
+
}, []);
|
|
705
|
+
const clearFilters = (0, import_react7.useCallback)(() => {
|
|
706
|
+
setFilters({});
|
|
707
|
+
}, []);
|
|
708
|
+
const filteredData = (0, import_react7.useMemo)(() => {
|
|
709
|
+
let result = [...data];
|
|
710
|
+
for (const [columnKey, filterValue] of Object.entries(filters)) {
|
|
711
|
+
if (!filterValue || Array.isArray(filterValue) && filterValue.length === 0) continue;
|
|
712
|
+
const column = columns.find((c) => c.key === columnKey);
|
|
713
|
+
if (!column) continue;
|
|
714
|
+
const getFilterableValue = column.filterValue || ((item) => {
|
|
715
|
+
return item[columnKey];
|
|
716
|
+
});
|
|
717
|
+
result = result.filter((item) => {
|
|
718
|
+
const itemValue = getFilterableValue(item);
|
|
719
|
+
if (itemValue === null || itemValue === void 0) {
|
|
720
|
+
return filterValue === "";
|
|
721
|
+
}
|
|
722
|
+
const filterType = column.filterType ?? "text";
|
|
723
|
+
if (filterType === "boolean") {
|
|
724
|
+
const boolFilterValue = filterValue === "true";
|
|
725
|
+
return itemValue === boolFilterValue;
|
|
726
|
+
}
|
|
727
|
+
if (filterType === "multiselect" && Array.isArray(filterValue)) {
|
|
728
|
+
return filterValue.includes(String(itemValue));
|
|
729
|
+
}
|
|
730
|
+
if (filterType === "select") {
|
|
731
|
+
return String(itemValue).toLowerCase() === String(filterValue).toLowerCase();
|
|
732
|
+
}
|
|
733
|
+
return String(itemValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
return result;
|
|
737
|
+
}, [data, filters, columns]);
|
|
738
|
+
const sortedData = (0, import_react7.useMemo)(() => {
|
|
739
|
+
if (!sortKey) return filteredData;
|
|
740
|
+
const column = columns.find((c) => c.key === sortKey);
|
|
741
|
+
if (!column) return filteredData;
|
|
742
|
+
const sorted = [...filteredData].sort((a, b) => {
|
|
743
|
+
if (column.sortFn) {
|
|
744
|
+
return column.sortFn(a, b);
|
|
745
|
+
}
|
|
746
|
+
const getValueForSort = column.filterValue || ((item) => {
|
|
747
|
+
return item[sortKey];
|
|
748
|
+
});
|
|
749
|
+
const aVal = getValueForSort(a);
|
|
750
|
+
const bVal = getValueForSort(b);
|
|
751
|
+
if (aVal === null || aVal === void 0) return 1;
|
|
752
|
+
if (bVal === null || bVal === void 0) return -1;
|
|
753
|
+
if (typeof aVal === "string" && typeof bVal === "string") {
|
|
754
|
+
return aVal.localeCompare(bVal, "sv");
|
|
755
|
+
}
|
|
756
|
+
if (aVal < bVal) return -1;
|
|
757
|
+
if (aVal > bVal) return 1;
|
|
758
|
+
return 0;
|
|
759
|
+
});
|
|
760
|
+
return sortDirection === "desc" ? sorted.reverse() : sorted;
|
|
761
|
+
}, [filteredData, sortKey, sortDirection, columns]);
|
|
762
|
+
(0, import_react7.useMemo)(() => {
|
|
763
|
+
onFilteredDataChange?.(sortedData);
|
|
764
|
+
}, [sortedData, onFilteredDataChange]);
|
|
765
|
+
const handleSort = (0, import_react7.useCallback)((columnKey) => {
|
|
766
|
+
if (sortKey === columnKey) {
|
|
767
|
+
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
768
|
+
} else {
|
|
769
|
+
setSortKey(columnKey);
|
|
770
|
+
setSortDirection("asc");
|
|
771
|
+
}
|
|
772
|
+
}, [sortKey]);
|
|
773
|
+
const tableColumns = (0, import_react7.useMemo)(() => {
|
|
774
|
+
return columns.map((column) => {
|
|
775
|
+
const isSortable = column.sortable !== false && column.filterType !== "none";
|
|
776
|
+
const isCurrentSort = sortKey === column.key;
|
|
777
|
+
return {
|
|
778
|
+
key: column.key,
|
|
779
|
+
header: isSortable ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
780
|
+
"button",
|
|
781
|
+
{
|
|
782
|
+
onClick: () => handleSort(column.key),
|
|
783
|
+
className: "flex items-center gap-1 hover:text-gray-900 transition-colors w-full text-left",
|
|
784
|
+
children: [
|
|
785
|
+
column.header,
|
|
786
|
+
isCurrentSort ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-primary-600", children: sortDirection === "asc" ? "\u2191" : "\u2193" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-gray-400 opacity-50", children: "\u2195" })
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
) : column.header,
|
|
790
|
+
render: column.render,
|
|
791
|
+
width: column.width,
|
|
792
|
+
align: column.align,
|
|
793
|
+
sortable: false
|
|
794
|
+
// We handle sorting ourselves
|
|
795
|
+
};
|
|
796
|
+
});
|
|
797
|
+
}, [columns, sortKey, sortDirection, handleSort]);
|
|
798
|
+
const hasActiveFilters = Object.keys(filters).length > 0;
|
|
799
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className, children: [
|
|
800
|
+
showFilters && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "mb-4", children: [
|
|
801
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-2 mb-2", children: [
|
|
802
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "text-sm text-gray-500", children: [
|
|
803
|
+
"Visar ",
|
|
804
|
+
sortedData.length,
|
|
805
|
+
" av ",
|
|
806
|
+
data.length
|
|
807
|
+
] }),
|
|
808
|
+
hasActiveFilters && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
809
|
+
"button",
|
|
810
|
+
{
|
|
811
|
+
onClick: clearFilters,
|
|
812
|
+
className: "text-sm text-primary-600 hover:underline",
|
|
813
|
+
children: "Rensa filter"
|
|
814
|
+
}
|
|
815
|
+
)
|
|
816
|
+
] }),
|
|
817
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("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__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "min-w-0", children: [
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { className: "text-xs text-gray-500 mb-1 block truncate", children: column.header }),
|
|
819
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
820
|
+
ColumnFilter,
|
|
821
|
+
{
|
|
822
|
+
column,
|
|
823
|
+
value: filters[column.key] || "",
|
|
824
|
+
onChange: (value) => handleFilterChange(column.key, value)
|
|
825
|
+
}
|
|
826
|
+
)
|
|
827
|
+
] }, column.key)) })
|
|
828
|
+
] }),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
830
|
+
Table,
|
|
831
|
+
{
|
|
832
|
+
data: sortedData,
|
|
833
|
+
columns: tableColumns,
|
|
834
|
+
getRowKey,
|
|
835
|
+
onRowClick,
|
|
836
|
+
loading,
|
|
837
|
+
emptyMessage
|
|
838
|
+
}
|
|
839
|
+
)
|
|
840
|
+
] });
|
|
841
|
+
}
|
|
842
|
+
function createFilterOptions(data, getValue, labelMap) {
|
|
843
|
+
const uniqueValues = /* @__PURE__ */ new Set();
|
|
844
|
+
for (const item of data) {
|
|
845
|
+
const value = getValue(item);
|
|
846
|
+
if (value !== null && value !== void 0 && value !== "") {
|
|
847
|
+
uniqueValues.add(value);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return Array.from(uniqueValues).sort((a, b) => a.localeCompare(b, "sv")).map((value) => ({
|
|
851
|
+
value,
|
|
852
|
+
label: labelMap?.[value] || value
|
|
853
|
+
}));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/components/Tabs.tsx
|
|
857
|
+
var import_react8 = require("react");
|
|
858
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
618
859
|
function Tabs({
|
|
619
860
|
tabs,
|
|
620
861
|
activeTab: controlledActiveTab,
|
|
@@ -625,7 +866,7 @@ function Tabs({
|
|
|
625
866
|
className = ""
|
|
626
867
|
}) {
|
|
627
868
|
const isControlled = controlledActiveTab !== void 0;
|
|
628
|
-
const [internalActiveTab, setInternalActiveTab] = (0,
|
|
869
|
+
const [internalActiveTab, setInternalActiveTab] = (0, import_react8.useState)(
|
|
629
870
|
defaultTab || tabs.find((t) => !t.disabled)?.id || tabs[0]?.id
|
|
630
871
|
);
|
|
631
872
|
const activeTab = isControlled ? controlledActiveTab : internalActiveTab;
|
|
@@ -693,11 +934,11 @@ function Tabs({
|
|
|
693
934
|
}
|
|
694
935
|
};
|
|
695
936
|
const styles = variantStyles[variant];
|
|
696
|
-
return /* @__PURE__ */ (0,
|
|
697
|
-
/* @__PURE__ */ (0,
|
|
937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className, children: [
|
|
938
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: `flex ${styles.container}`, role: "tablist", children: tabs.map((tab, index) => {
|
|
698
939
|
const isActive = tab.id === activeTab;
|
|
699
940
|
const isDisabled = tab.disabled ?? false;
|
|
700
|
-
return /* @__PURE__ */ (0,
|
|
941
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
701
942
|
"button",
|
|
702
943
|
{
|
|
703
944
|
type: "button",
|
|
@@ -709,7 +950,7 @@ function Tabs({
|
|
|
709
950
|
className: styles.tab(isActive, isDisabled),
|
|
710
951
|
onClick: () => handleTabClick(tab),
|
|
711
952
|
onKeyDown: (e) => handleKeyDown(e, index),
|
|
712
|
-
children: /* @__PURE__ */ (0,
|
|
953
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
713
954
|
tab.icon,
|
|
714
955
|
tab.label
|
|
715
956
|
] })
|
|
@@ -717,13 +958,13 @@ function Tabs({
|
|
|
717
958
|
tab.id
|
|
718
959
|
);
|
|
719
960
|
}) }),
|
|
720
|
-
renderContent && activeContent && /* @__PURE__ */ (0,
|
|
961
|
+
renderContent && activeContent && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pt-4", role: "tabpanel", children: activeContent })
|
|
721
962
|
] });
|
|
722
963
|
}
|
|
723
964
|
|
|
724
965
|
// src/components/DatePicker.tsx
|
|
725
|
-
var
|
|
726
|
-
var
|
|
966
|
+
var import_react9 = require("react");
|
|
967
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
727
968
|
var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
728
969
|
var MONTHS = [
|
|
729
970
|
"January",
|
|
@@ -826,12 +1067,12 @@ function DatePicker({
|
|
|
826
1067
|
disabled = false,
|
|
827
1068
|
className = ""
|
|
828
1069
|
}) {
|
|
829
|
-
const [isOpen, setIsOpen] = (0,
|
|
830
|
-
const [viewDate, setViewDate] = (0,
|
|
831
|
-
const [hoverDate, setHoverDate] = (0,
|
|
832
|
-
const containerRef = (0,
|
|
833
|
-
const [selectingEnd, setSelectingEnd] = (0,
|
|
834
|
-
(0,
|
|
1070
|
+
const [isOpen, setIsOpen] = (0, import_react9.useState)(false);
|
|
1071
|
+
const [viewDate, setViewDate] = (0, import_react9.useState)(/* @__PURE__ */ new Date());
|
|
1072
|
+
const [hoverDate, setHoverDate] = (0, import_react9.useState)(null);
|
|
1073
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
1074
|
+
const [selectingEnd, setSelectingEnd] = (0, import_react9.useState)(false);
|
|
1075
|
+
(0, import_react9.useEffect)(() => {
|
|
835
1076
|
const handleClickOutside = (e) => {
|
|
836
1077
|
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
837
1078
|
setIsOpen(false);
|
|
@@ -841,7 +1082,7 @@ function DatePicker({
|
|
|
841
1082
|
document.addEventListener("mousedown", handleClickOutside);
|
|
842
1083
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
843
1084
|
}, []);
|
|
844
|
-
const displayText = (0,
|
|
1085
|
+
const displayText = (0, import_react9.useMemo)(() => {
|
|
845
1086
|
if (!value) return "";
|
|
846
1087
|
if (range) {
|
|
847
1088
|
const rangeValue = value;
|
|
@@ -858,7 +1099,7 @@ function DatePicker({
|
|
|
858
1099
|
}
|
|
859
1100
|
return formatDate(value);
|
|
860
1101
|
}, [value, range]);
|
|
861
|
-
const calendarDays = (0,
|
|
1102
|
+
const calendarDays = (0, import_react9.useMemo)(() => {
|
|
862
1103
|
const year = viewDate.getFullYear();
|
|
863
1104
|
const month = viewDate.getMonth();
|
|
864
1105
|
const firstDay = new Date(year, month, 1);
|
|
@@ -941,9 +1182,9 @@ function DatePicker({
|
|
|
941
1182
|
};
|
|
942
1183
|
const today = /* @__PURE__ */ new Date();
|
|
943
1184
|
today.setHours(0, 0, 0, 0);
|
|
944
|
-
return /* @__PURE__ */ (0,
|
|
945
|
-
label && /* @__PURE__ */ (0,
|
|
946
|
-
/* @__PURE__ */ (0,
|
|
1185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: containerRef, className: `relative ${className}`, children: [
|
|
1186
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: label }),
|
|
1187
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
947
1188
|
"div",
|
|
948
1189
|
{
|
|
949
1190
|
className: `
|
|
@@ -953,15 +1194,15 @@ function DatePicker({
|
|
|
953
1194
|
transition-all
|
|
954
1195
|
`,
|
|
955
1196
|
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
956
|
-
children: /* @__PURE__ */ (0,
|
|
957
|
-
/* @__PURE__ */ (0,
|
|
958
|
-
/* @__PURE__ */ (0,
|
|
1197
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1198
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: displayText ? "text-gray-800" : "text-gray-400", children: displayText || placeholder }),
|
|
1199
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { className: "w-5 h-5 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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" }) })
|
|
959
1200
|
] })
|
|
960
1201
|
}
|
|
961
1202
|
),
|
|
962
|
-
error && /* @__PURE__ */ (0,
|
|
963
|
-
isOpen && /* @__PURE__ */ (0,
|
|
964
|
-
range && /* @__PURE__ */ (0,
|
|
1203
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-1 text-xs text-red-600", children: error }),
|
|
1204
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "absolute z-50 mt-1 bg-white border border-gray-200 rounded-xl shadow-lg p-4", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: range ? "flex gap-4" : "", children: [
|
|
1205
|
+
range && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "border-r border-gray-100 pr-4 space-y-1", children: PRESET_RANGES.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
965
1206
|
"button",
|
|
966
1207
|
{
|
|
967
1208
|
type: "button",
|
|
@@ -971,42 +1212,42 @@ function DatePicker({
|
|
|
971
1212
|
},
|
|
972
1213
|
preset.label
|
|
973
1214
|
)) }),
|
|
974
|
-
/* @__PURE__ */ (0,
|
|
975
|
-
/* @__PURE__ */ (0,
|
|
976
|
-
/* @__PURE__ */ (0,
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
977
1218
|
"button",
|
|
978
1219
|
{
|
|
979
1220
|
type: "button",
|
|
980
1221
|
className: "p-1 hover:bg-gray-100 rounded",
|
|
981
1222
|
onClick: goToPrevMonth,
|
|
982
|
-
children: /* @__PURE__ */ (0,
|
|
1223
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
|
|
983
1224
|
}
|
|
984
1225
|
),
|
|
985
|
-
/* @__PURE__ */ (0,
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "text-sm font-semibold text-gray-800", children: [
|
|
986
1227
|
MONTHS[viewDate.getMonth()],
|
|
987
1228
|
" ",
|
|
988
1229
|
viewDate.getFullYear()
|
|
989
1230
|
] }),
|
|
990
|
-
/* @__PURE__ */ (0,
|
|
1231
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
991
1232
|
"button",
|
|
992
1233
|
{
|
|
993
1234
|
type: "button",
|
|
994
1235
|
className: "p-1 hover:bg-gray-100 rounded",
|
|
995
1236
|
onClick: goToNextMonth,
|
|
996
|
-
children: /* @__PURE__ */ (0,
|
|
1237
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
|
|
997
1238
|
}
|
|
998
1239
|
)
|
|
999
1240
|
] }),
|
|
1000
|
-
/* @__PURE__ */ (0,
|
|
1001
|
-
/* @__PURE__ */ (0,
|
|
1241
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "grid grid-cols-7 gap-1 mb-2", children: WEEKDAYS.map((day) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-center text-xs font-medium text-gray-500 py-1", children: day }, day)) }),
|
|
1242
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "grid grid-cols-7 gap-1", children: calendarDays.map((date, index) => {
|
|
1002
1243
|
if (!date) {
|
|
1003
|
-
return /* @__PURE__ */ (0,
|
|
1244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "w-9 h-9" }, `empty-${index}`);
|
|
1004
1245
|
}
|
|
1005
1246
|
const isDisabled = isDateDisabled(date);
|
|
1006
1247
|
const isSelected = isDateSelected(date);
|
|
1007
1248
|
const inRange = isDateInRange(date);
|
|
1008
1249
|
const isToday = isSameDay(date, today);
|
|
1009
|
-
return /* @__PURE__ */ (0,
|
|
1250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1010
1251
|
"button",
|
|
1011
1252
|
{
|
|
1012
1253
|
type: "button",
|
|
@@ -1032,8 +1273,8 @@ function DatePicker({
|
|
|
1032
1273
|
}
|
|
1033
1274
|
|
|
1034
1275
|
// src/components/Timer.tsx
|
|
1035
|
-
var
|
|
1036
|
-
var
|
|
1276
|
+
var import_react10 = require("react");
|
|
1277
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1037
1278
|
function formatTime(totalSeconds) {
|
|
1038
1279
|
const hours = Math.floor(totalSeconds / 3600);
|
|
1039
1280
|
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
@@ -1057,20 +1298,20 @@ function Timer({
|
|
|
1057
1298
|
elapsedSeconds: controlledElapsedSeconds
|
|
1058
1299
|
}) {
|
|
1059
1300
|
const isControlled = controlledIsRunning !== void 0;
|
|
1060
|
-
const [internalIsRunning, setInternalIsRunning] = (0,
|
|
1061
|
-
const [internalElapsedSeconds, setInternalElapsedSeconds] = (0,
|
|
1301
|
+
const [internalIsRunning, setInternalIsRunning] = (0, import_react10.useState)(false);
|
|
1302
|
+
const [internalElapsedSeconds, setInternalElapsedSeconds] = (0, import_react10.useState)(initialSeconds);
|
|
1062
1303
|
const isRunning = isControlled ? controlledIsRunning : internalIsRunning;
|
|
1063
1304
|
const elapsedSeconds = isControlled ? controlledElapsedSeconds ?? 0 : internalElapsedSeconds;
|
|
1064
|
-
const intervalRef = (0,
|
|
1065
|
-
const startTimeRef = (0,
|
|
1066
|
-
(0,
|
|
1305
|
+
const intervalRef = (0, import_react10.useRef)(null);
|
|
1306
|
+
const startTimeRef = (0, import_react10.useRef)(null);
|
|
1307
|
+
(0, import_react10.useEffect)(() => {
|
|
1067
1308
|
return () => {
|
|
1068
1309
|
if (intervalRef.current) {
|
|
1069
1310
|
clearInterval(intervalRef.current);
|
|
1070
1311
|
}
|
|
1071
1312
|
};
|
|
1072
1313
|
}, []);
|
|
1073
|
-
(0,
|
|
1314
|
+
(0, import_react10.useEffect)(() => {
|
|
1074
1315
|
if (isControlled) return;
|
|
1075
1316
|
if (isRunning) {
|
|
1076
1317
|
intervalRef.current = setInterval(() => {
|
|
@@ -1093,7 +1334,7 @@ function Timer({
|
|
|
1093
1334
|
}
|
|
1094
1335
|
};
|
|
1095
1336
|
}, [isRunning, isControlled, onTick]);
|
|
1096
|
-
const handleStart = (0,
|
|
1337
|
+
const handleStart = (0, import_react10.useCallback)(() => {
|
|
1097
1338
|
const now = /* @__PURE__ */ new Date();
|
|
1098
1339
|
startTimeRef.current = now;
|
|
1099
1340
|
if (!isControlled) {
|
|
@@ -1101,13 +1342,13 @@ function Timer({
|
|
|
1101
1342
|
}
|
|
1102
1343
|
onStart?.(now);
|
|
1103
1344
|
}, [isControlled, onStart]);
|
|
1104
|
-
const handleStop = (0,
|
|
1345
|
+
const handleStop = (0, import_react10.useCallback)(() => {
|
|
1105
1346
|
if (!isControlled) {
|
|
1106
1347
|
setInternalIsRunning(false);
|
|
1107
1348
|
}
|
|
1108
1349
|
onStop?.(elapsedSeconds);
|
|
1109
1350
|
}, [isControlled, elapsedSeconds, onStop]);
|
|
1110
|
-
const handleReset = (0,
|
|
1351
|
+
const handleReset = (0, import_react10.useCallback)(() => {
|
|
1111
1352
|
if (!isControlled) {
|
|
1112
1353
|
setInternalIsRunning(false);
|
|
1113
1354
|
setInternalElapsedSeconds(0);
|
|
@@ -1115,7 +1356,7 @@ function Timer({
|
|
|
1115
1356
|
startTimeRef.current = null;
|
|
1116
1357
|
onReset?.();
|
|
1117
1358
|
}, [isControlled, onReset]);
|
|
1118
|
-
const handleToggle = (0,
|
|
1359
|
+
const handleToggle = (0, import_react10.useCallback)(() => {
|
|
1119
1360
|
if (isRunning) {
|
|
1120
1361
|
handleStop();
|
|
1121
1362
|
} else {
|
|
@@ -1143,10 +1384,10 @@ function Timer({
|
|
|
1143
1384
|
}
|
|
1144
1385
|
};
|
|
1145
1386
|
const styles = sizeClasses3[size];
|
|
1146
|
-
return /* @__PURE__ */ (0,
|
|
1147
|
-
/* @__PURE__ */ (0,
|
|
1148
|
-
/* @__PURE__ */ (0,
|
|
1149
|
-
/* @__PURE__ */ (0,
|
|
1387
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `flex items-center ${styles.container} ${className}`, children: [
|
|
1388
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: `font-mono font-bold text-gray-800 ${styles.time} tabular-nums`, children: formatTime(elapsedSeconds) }),
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1150
1391
|
"button",
|
|
1151
1392
|
{
|
|
1152
1393
|
type: "button",
|
|
@@ -1160,14 +1401,14 @@ function Timer({
|
|
|
1160
1401
|
title: isRunning ? "Pause" : "Start",
|
|
1161
1402
|
children: isRunning ? (
|
|
1162
1403
|
// Pause icon
|
|
1163
|
-
/* @__PURE__ */ (0,
|
|
1404
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { className: styles.icon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" }) })
|
|
1164
1405
|
) : (
|
|
1165
1406
|
// Play icon
|
|
1166
|
-
/* @__PURE__ */ (0,
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { className: styles.icon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("path", { d: "M8 5v14l11-7z" }) })
|
|
1167
1408
|
)
|
|
1168
1409
|
}
|
|
1169
1410
|
),
|
|
1170
|
-
elapsedSeconds > 0 && /* @__PURE__ */ (0,
|
|
1411
|
+
elapsedSeconds > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1171
1412
|
"button",
|
|
1172
1413
|
{
|
|
1173
1414
|
type: "button",
|
|
@@ -1178,10 +1419,10 @@ function Timer({
|
|
|
1178
1419
|
transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500
|
|
1179
1420
|
`,
|
|
1180
1421
|
title: "Stop",
|
|
1181
|
-
children: /* @__PURE__ */ (0,
|
|
1422
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { className: styles.icon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("path", { d: "M6 6h12v12H6z" }) })
|
|
1182
1423
|
}
|
|
1183
1424
|
),
|
|
1184
|
-
showReset && elapsedSeconds > 0 && !isRunning && /* @__PURE__ */ (0,
|
|
1425
|
+
showReset && elapsedSeconds > 0 && !isRunning && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1185
1426
|
"button",
|
|
1186
1427
|
{
|
|
1187
1428
|
type: "button",
|
|
@@ -1192,7 +1433,7 @@ function Timer({
|
|
|
1192
1433
|
transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400
|
|
1193
1434
|
`,
|
|
1194
1435
|
title: "Reset",
|
|
1195
|
-
children: /* @__PURE__ */ (0,
|
|
1436
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { className: styles.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("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" }) })
|
|
1196
1437
|
}
|
|
1197
1438
|
)
|
|
1198
1439
|
] })
|
|
@@ -1202,6 +1443,7 @@ function Timer({
|
|
|
1202
1443
|
0 && (module.exports = {
|
|
1203
1444
|
Button,
|
|
1204
1445
|
Card,
|
|
1446
|
+
DataTable,
|
|
1205
1447
|
DatePicker,
|
|
1206
1448
|
EmptyState,
|
|
1207
1449
|
FormButtonGroup,
|
|
@@ -1217,6 +1459,7 @@ function Timer({
|
|
|
1217
1459
|
Table,
|
|
1218
1460
|
Tabs,
|
|
1219
1461
|
Timer,
|
|
1462
|
+
createFilterOptions,
|
|
1220
1463
|
formatTime
|
|
1221
1464
|
});
|
|
1222
1465
|
//# sourceMappingURL=index.js.map
|