@pk-design/react-tailwind 1.0.5 → 1.1.0
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/react-tailwind.cjs.js +1593 -362
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +1579 -360
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/Alert/Alert.d.ts +3 -0
- package/dist/types/components/Alert/Alert.test.d.ts +1 -0
- package/dist/types/components/Alert/Alert.types.d.ts +34 -0
- package/dist/types/components/Alert/index.d.ts +2 -35
- package/dist/types/components/Badge/Badge.d.ts +3 -0
- package/dist/types/components/Badge/Badge.test.d.ts +1 -0
- package/dist/types/components/Badge/Badge.types.d.ts +22 -0
- package/dist/types/components/Badge/index.d.ts +2 -24
- package/dist/types/components/Banner/Banner.d.ts +3 -0
- package/dist/types/components/Banner/Banner.test.d.ts +1 -0
- package/dist/types/components/Banner/Banner.types.d.ts +14 -0
- package/dist/types/components/Banner/index.d.ts +2 -16
- package/dist/types/components/BodyText/BodyText.d.ts +3 -0
- package/dist/types/components/BodyText/BodyText.test.d.ts +1 -0
- package/dist/types/components/BodyText/BodyText.types.d.ts +27 -0
- package/dist/types/components/BodyText/index.d.ts +2 -28
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +3 -0
- package/dist/types/components/Breadcrumb/Breadcrumb.test.d.ts +1 -0
- package/dist/types/components/Breadcrumb/Breadcrumb.types.d.ts +29 -0
- package/dist/types/components/Breadcrumb/index.d.ts +2 -30
- package/dist/types/components/Button/Button.d.ts +2 -3
- package/dist/types/components/Button/Button.test.d.ts +1 -0
- package/dist/types/components/Button/Button.types.d.ts +171 -0
- package/dist/types/components/Button/index.d.ts +1 -1
- package/dist/types/components/Card/Card.d.ts +3 -0
- package/dist/types/components/Card/Card.test.d.ts +1 -0
- package/dist/types/components/Card/Card.types.d.ts +23 -0
- package/dist/types/components/Card/index.d.ts +2 -9
- package/dist/types/components/Checkbox/Checkbox.d.ts +2 -0
- package/dist/types/components/Checkbox/Checkbox.test.d.ts +1 -0
- package/dist/types/components/Checkbox/Checkbox.types.d.ts +14 -0
- package/dist/types/components/Checkbox/index.d.ts +2 -13
- package/dist/types/components/CheckboxGroup/CheckboxGroup.d.ts +5 -0
- package/dist/types/components/CheckboxGroup/CheckboxGroup.test.d.ts +1 -0
- package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +55 -0
- package/dist/types/components/CheckboxGroup/index.d.ts +2 -56
- package/dist/types/components/DataTable/DataTable.d.ts +2 -0
- package/dist/types/components/DataTable/DataTable.icons.d.ts +4 -0
- package/dist/types/components/DataTable/DataTable.test.d.ts +1 -0
- package/dist/types/components/DataTable/DataTable.types.d.ts +63 -0
- package/dist/types/components/DataTable/DataTable.utils.d.ts +12 -0
- package/dist/types/components/DataTable/DataTable.utils.test.d.ts +1 -0
- package/dist/types/components/DataTable/index.d.ts +4 -0
- package/dist/types/components/DetailedInformation/DetailedInformation.d.ts +2 -0
- package/dist/types/components/DetailedInformation/DetailedInformation.test.d.ts +1 -0
- package/dist/types/components/DetailedInformation/DetailedInformation.types.d.ts +54 -0
- package/dist/types/components/DetailedInformation/index.d.ts +2 -63
- package/dist/types/components/Drawer/Drawer.d.ts +1 -1
- package/dist/types/components/Drawer/Drawer.test.d.ts +1 -0
- package/dist/types/components/Drawer/{type.d.ts → Drawer.types.d.ts} +1 -1
- package/dist/types/components/Drawer/index.d.ts +1 -1
- package/dist/types/components/Dropdown/Dropdown.d.ts +3 -0
- package/dist/types/components/Dropdown/Dropdown.test.d.ts +1 -0
- package/dist/types/components/Dropdown/Dropdown.types.d.ts +107 -0
- package/dist/types/components/Dropdown/index.d.ts +2 -62
- package/dist/types/components/HeadingText/HeadingText.d.ts +9 -0
- package/dist/types/components/HeadingText/HeadingText.types.d.ts +5 -0
- package/dist/types/components/HeadingText/index.d.ts +2 -12
- package/dist/types/components/Input/Input.d.ts +6 -0
- package/dist/types/components/Input/Input.test.d.ts +1 -0
- package/dist/types/components/Input/Input.types.d.ts +12 -0
- package/dist/types/components/Input/index.d.ts +2 -13
- package/dist/types/components/Label/Label.d.ts +6 -0
- package/dist/types/components/Label/index.d.ts +1 -5
- package/dist/types/components/Pagination/Pagination.d.ts +1 -31
- package/dist/types/components/Pagination/Pagination.test.d.ts +1 -0
- package/dist/types/components/Pagination/Pagination.types.d.ts +36 -0
- package/dist/types/components/Pagination/index.d.ts +1 -0
- package/dist/types/components/Pagination/usePagination.d.ts +1 -5
- package/dist/types/components/PasswordInput/PasswordInput.d.ts +6 -0
- package/dist/types/components/PasswordInput/PasswordInput.test.d.ts +1 -0
- package/dist/types/components/PasswordInput/index.d.ts +1 -6
- package/dist/types/components/Radio/Radio.d.ts +2 -0
- package/dist/types/components/Radio/Radio.test.d.ts +1 -0
- package/dist/types/components/Radio/Radio.types.d.ts +14 -0
- package/dist/types/components/Radio/index.d.ts +2 -0
- package/dist/types/components/RadioGroup/RadioGroup.d.ts +3 -0
- package/dist/types/components/RadioGroup/RadioGroup.test.d.ts +1 -0
- package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +65 -0
- package/dist/types/components/RadioGroup/index.d.ts +2 -0
- package/dist/types/components/RadioSwitch/RadioSwitch.d.ts +7 -0
- package/dist/types/components/RadioSwitch/RadioSwitch.test.d.ts +1 -0
- package/dist/types/components/RadioSwitch/RadioSwitch.types.d.ts +47 -0
- package/dist/types/components/RadioSwitch/index.d.ts +2 -0
- package/dist/types/components/RangeInput/-backup.d.ts +10 -0
- package/dist/types/components/RangeInput/RangeInput.d.ts +2 -0
- package/dist/types/components/RangeInput/RangeInput.test.d.ts +1 -0
- package/dist/types/components/RangeInput/RangeInput.types.d.ts +14 -0
- package/dist/types/components/RangeInput/index.d.ts +1 -10
- package/dist/types/components/RangeSlider/RangeSlider.d.ts +3 -0
- package/dist/types/components/RangeSlider/RangeSlider.test.d.ts +1 -0
- package/dist/types/components/RangeSlider/RangeSlider.types.d.ts +68 -0
- package/dist/types/components/RangeSlider/index.d.ts +2 -0
- package/dist/types/components/SelectBox/SelectBox.d.ts +2 -0
- package/dist/types/components/SelectBox/SelectBox.test.d.ts +1 -0
- package/dist/types/components/SelectBox/SelectBox.types.d.ts +94 -0
- package/dist/types/components/SelectBox/index.d.ts +2 -40
- package/dist/types/components/Table/Table.d.ts +10 -0
- package/dist/types/components/Table/Table.test.d.ts +1 -0
- package/dist/types/components/Table/Table.types.d.ts +91 -0
- package/dist/types/components/Table/index.d.ts +2 -48
- package/dist/types/components/Tabs/Tabs.d.ts +3 -0
- package/dist/types/components/Tabs/Tabs.test.d.ts +1 -0
- package/dist/types/components/Tabs/Tabs.types.d.ts +48 -0
- package/dist/types/components/Tabs/index.d.ts +1 -2
- package/dist/types/components/TextContent/TextContent.d.ts +3 -0
- package/dist/types/components/TextContent/TextContent.test.d.ts +1 -0
- package/dist/types/components/TextContent/TextContent.types.d.ts +23 -0
- package/dist/types/components/TextContent/index.d.ts +2 -10
- package/dist/types/components/Textarea/Textarea.d.ts +2 -0
- package/dist/types/components/Textarea/Textarea.test.d.ts +1 -0
- package/dist/types/components/Textarea/Textarea.types.d.ts +6 -0
- package/dist/types/components/Textarea/index.d.ts +2 -7
- package/dist/types/components/Toast/Toast.d.ts +4 -8
- package/dist/types/components/Toast/Toast.test.d.ts +1 -0
- package/dist/types/components/Toast/Toast.types.d.ts +117 -0
- package/dist/types/components/Toast/ToastProvider.d.ts +2 -11
- package/dist/types/components/Toast/ToastProvider.test.d.ts +1 -0
- package/dist/types/components/Toast/index.d.ts +1 -0
- package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.d.ts +3 -0
- package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.test.d.ts +1 -0
- package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.types.d.ts +18 -0
- package/dist/types/components/VirtualizedDataTable/VirtualizedRow.d.ts +5 -0
- package/dist/types/components/VirtualizedDataTable/index.d.ts +2 -0
- package/dist/types/components/_Dialog/Dialog.d.ts +4 -0
- package/dist/types/components/_Dialog/Dialog.types.d.ts +53 -0
- package/dist/types/components/_Dialog/index.d.ts +2 -0
- package/dist/types/components/common-type.d.ts +2 -1
- package/dist/types/hooks/use-debounce.d.ts +2 -0
- package/dist/types/hooks/use-is-mobile.d.ts +1 -0
- package/dist/types/hooks/use-is-tablet.d.ts +1 -0
- package/dist/types/index.d.ts +11 -6
- package/dist/types/utils/get.d.ts +1 -0
- package/package.json +28 -5
- package/dist/types/components/Button/type.d.ts +0 -58
- package/dist/types/components/RadioButton/index.d.ts +0 -7
- package/dist/types/components/RadioButtonGroup/index.d.ts +0 -19
- package/dist/types/components/RadioSwtich/index.d.ts +0 -14
- package/dist/types/components/Tag/index.d.ts +0 -12
- package/dist/types/components/hooks/use-debounce.d.ts +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { jsx, jsxs, Fragment
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import React__default, { forwardRef, useState, useEffect, Fragment,
|
|
5
|
-
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Menu, MenuButton, MenuItems, MenuItem, RadioGroup, Radio, Combobox, ComboboxInput, ComboboxButton, ComboboxOptions, ComboboxOption, TabGroup, TabList, Tab, TabPanels, TabPanel, Transition } from '@headlessui/react';
|
|
4
|
+
import React__default, { forwardRef, useMemo, useState, useId, useEffect, useRef, Fragment as Fragment$1, useCallback, useImperativeHandle, createContext, useContext, memo, useLayoutEffect, createElement } from 'react';
|
|
5
|
+
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Menu, MenuButton, MenuItems, MenuItem, RadioGroup as RadioGroup$1, Radio as Radio$1, Combobox, ComboboxInput, ComboboxButton, ComboboxOptions, ComboboxOption, TabGroup, TabList, Tab, TabPanels, TabPanel, Transition } from '@headlessui/react';
|
|
6
6
|
|
|
7
7
|
const buildClassName = (...classes) => twMerge(classes.filter(Boolean));
|
|
8
8
|
|
|
9
|
-
var _path$
|
|
10
|
-
function _extends$
|
|
9
|
+
var _path$i, _path2;
|
|
10
|
+
function _extends$i() { return _extends$i = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$i.apply(null, arguments); }
|
|
11
11
|
var SvgBadgeCheck = function SvgBadgeCheck(props) {
|
|
12
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
12
|
+
return /*#__PURE__*/React.createElement("svg", _extends$i({
|
|
13
13
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14
14
|
width: 24,
|
|
15
15
|
height: 24,
|
|
@@ -20,17 +20,17 @@ var SvgBadgeCheck = function SvgBadgeCheck(props) {
|
|
|
20
20
|
strokeWidth: 2,
|
|
21
21
|
className: "lucide lucide-badge-check-icon lucide-badge-check",
|
|
22
22
|
viewBox: "0 0 24 24"
|
|
23
|
-
}, props), _path$
|
|
23
|
+
}, props), _path$i || (_path$i = /*#__PURE__*/React.createElement("path", {
|
|
24
24
|
d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76"
|
|
25
25
|
})), _path2 || (_path2 = /*#__PURE__*/React.createElement("path", {
|
|
26
26
|
d: "m9 12 2 2 4-4"
|
|
27
27
|
})));
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
var _circle$4, _path$
|
|
31
|
-
function _extends$
|
|
30
|
+
var _circle$4, _path$h;
|
|
31
|
+
function _extends$h() { return _extends$h = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$h.apply(null, arguments); }
|
|
32
32
|
var SvgCircleX = function SvgCircleX(props) {
|
|
33
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
33
|
+
return /*#__PURE__*/React.createElement("svg", _extends$h({
|
|
34
34
|
xmlns: "http://www.w3.org/2000/svg",
|
|
35
35
|
width: 24,
|
|
36
36
|
height: 24,
|
|
@@ -45,15 +45,15 @@ var SvgCircleX = function SvgCircleX(props) {
|
|
|
45
45
|
cx: 12,
|
|
46
46
|
cy: 12,
|
|
47
47
|
r: 10
|
|
48
|
-
})), _path$
|
|
48
|
+
})), _path$h || (_path$h = /*#__PURE__*/React.createElement("path", {
|
|
49
49
|
d: "m15 9-6 6M9 9l6 6"
|
|
50
50
|
})));
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
var _circle$3, _path$
|
|
54
|
-
function _extends$
|
|
53
|
+
var _circle$3, _path$g;
|
|
54
|
+
function _extends$g() { return _extends$g = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$g.apply(null, arguments); }
|
|
55
55
|
var SvgCircleAlert = function SvgCircleAlert(props) {
|
|
56
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
56
|
+
return /*#__PURE__*/React.createElement("svg", _extends$g({
|
|
57
57
|
xmlns: "http://www.w3.org/2000/svg",
|
|
58
58
|
width: 24,
|
|
59
59
|
height: 24,
|
|
@@ -68,15 +68,15 @@ var SvgCircleAlert = function SvgCircleAlert(props) {
|
|
|
68
68
|
cx: 12,
|
|
69
69
|
cy: 12,
|
|
70
70
|
r: 10
|
|
71
|
-
})), _path$
|
|
71
|
+
})), _path$g || (_path$g = /*#__PURE__*/React.createElement("path", {
|
|
72
72
|
d: "M12 8v4M12 16h.01"
|
|
73
73
|
})));
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
var _circle$2, _path$
|
|
77
|
-
function _extends$
|
|
76
|
+
var _circle$2, _path$f;
|
|
77
|
+
function _extends$f() { return _extends$f = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$f.apply(null, arguments); }
|
|
78
78
|
var SvgInfo = function SvgInfo(props) {
|
|
79
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
79
|
+
return /*#__PURE__*/React.createElement("svg", _extends$f({
|
|
80
80
|
xmlns: "http://www.w3.org/2000/svg",
|
|
81
81
|
width: 24,
|
|
82
82
|
height: 24,
|
|
@@ -91,15 +91,15 @@ var SvgInfo = function SvgInfo(props) {
|
|
|
91
91
|
cx: 12,
|
|
92
92
|
cy: 12,
|
|
93
93
|
r: 10
|
|
94
|
-
})), _path$
|
|
94
|
+
})), _path$f || (_path$f = /*#__PURE__*/React.createElement("path", {
|
|
95
95
|
d: "M12 16v-4M12 8h.01"
|
|
96
96
|
})));
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
-
var _path$
|
|
100
|
-
function _extends$
|
|
99
|
+
var _path$e;
|
|
100
|
+
function _extends$e() { return _extends$e = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$e.apply(null, arguments); }
|
|
101
101
|
var SvgX = function SvgX(props) {
|
|
102
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
102
|
+
return /*#__PURE__*/React.createElement("svg", _extends$e({
|
|
103
103
|
xmlns: "http://www.w3.org/2000/svg",
|
|
104
104
|
width: 24,
|
|
105
105
|
height: 24,
|
|
@@ -110,7 +110,7 @@ var SvgX = function SvgX(props) {
|
|
|
110
110
|
strokeWidth: 2,
|
|
111
111
|
className: "lucide lucide-x-icon lucide-x",
|
|
112
112
|
viewBox: "0 0 24 24"
|
|
113
|
-
}, props), _path$
|
|
113
|
+
}, props), _path$e || (_path$e = /*#__PURE__*/React.createElement("path", {
|
|
114
114
|
d: "M18 6 6 18M6 6l12 12"
|
|
115
115
|
})));
|
|
116
116
|
};
|
|
@@ -121,19 +121,23 @@ const loaderSizeMap = {
|
|
|
121
121
|
md: 'size-8',
|
|
122
122
|
lg: 'size-10',
|
|
123
123
|
};
|
|
124
|
-
const Loader = ({ className = 'text-blue-600', size = 'md' }) => {
|
|
125
|
-
return (jsx("svg", { className: buildClassName(loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", children: jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
|
|
124
|
+
const Loader = ({ className = 'text-blue-600 dark:text-white', size = 'md', ...props }) => {
|
|
125
|
+
return (jsx("svg", { className: buildClassName(loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
const TextContent = forwardRef((props, ref) => {
|
|
129
|
-
const {
|
|
130
|
-
return React__default.createElement(
|
|
131
|
-
ref,
|
|
132
|
-
className: buildClassName(xsmall && 'text-xs/5', small && 'text-sm/6', strong && 'font-semibold', muted && 'text-gray-500', error && 'text-red-500', className),
|
|
129
|
+
const { xsmall = false, small = false, strong = false, muted = false, error = false, className, ...rest } = props;
|
|
130
|
+
return React__default.createElement('span', {
|
|
133
131
|
...rest,
|
|
132
|
+
ref,
|
|
133
|
+
className: buildClassName(xsmall && 'text-xs/5', small && 'text-sm/6', strong && 'font-semibold', muted && 'text-gray-500', error && 'text-red-500 dark:text-red-500', className),
|
|
134
134
|
});
|
|
135
135
|
});
|
|
136
|
+
TextContent.displayName = 'TextContent';
|
|
136
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Tailwind CSS classes for theme + variant combinations.
|
|
140
|
+
*/
|
|
137
141
|
const THEMED_BTN_CLASS = {
|
|
138
142
|
primary: {
|
|
139
143
|
default: 'text-white bg-blue-600 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-blue-600 disabled:bg-blue-400 dark:disabled:bg-blue-500',
|
|
@@ -151,22 +155,58 @@ const THEMED_BTN_CLASS = {
|
|
|
151
155
|
plain: 'text-gray-700 hover:bg-black/5 dark:text-gray-200 dark:hover:bg-gray-700 focus:outline-blue-600 disabled:text-gray-400',
|
|
152
156
|
},
|
|
153
157
|
};
|
|
158
|
+
/**
|
|
159
|
+
* Generate size classes.
|
|
160
|
+
*/
|
|
154
161
|
const getBtnSizeClasses = (size, iconOnly = false) => {
|
|
155
162
|
switch (size) {
|
|
156
163
|
case 'xs':
|
|
157
|
-
return
|
|
164
|
+
return iconOnly ? 'p-1 rounded-sm text-xs' : 'px-2 py-1 rounded-sm gap-1 text-xs';
|
|
158
165
|
case 'sm':
|
|
159
|
-
return
|
|
166
|
+
return iconOnly ? 'p-1.5 rounded text-sm' : 'px-3 py-1.5 rounded gap-1.5 text-sm';
|
|
160
167
|
case 'md':
|
|
161
|
-
return
|
|
168
|
+
return iconOnly ? 'p-2 rounded-md text-sm' : 'px-4 py-2 rounded-md gap-2 text-sm';
|
|
162
169
|
case 'lg':
|
|
163
|
-
return
|
|
170
|
+
return iconOnly ? 'p-2.5 rounded-lg text-base' : 'px-5 py-2.5 rounded-lg gap-2 text-base';
|
|
164
171
|
}
|
|
165
172
|
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Base shared tailwind classes.
|
|
175
|
+
*/
|
|
176
|
+
const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
|
|
177
|
+
'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
|
|
178
|
+
'transition duration-200 ease-in-out';
|
|
179
|
+
const Button = forwardRef(({ as, type, ...props }, ref) => {
|
|
180
|
+
const { className = '', children, theme = 'primary', variant = 'default', size = 'md', loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, ...restProps } = props;
|
|
181
|
+
const Component = as || 'button';
|
|
182
|
+
// Accessibility guard for icon-only buttons
|
|
183
|
+
if (iconOnly && !restProps['aria-label']) {
|
|
184
|
+
console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
|
|
185
|
+
}
|
|
186
|
+
// Disable interaction when loading
|
|
187
|
+
const resolvedDisabled = disabled || loading;
|
|
188
|
+
// Clean invalid props when not rendering a real button
|
|
189
|
+
const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
|
|
190
|
+
const buttonSpecificProps = Component === 'button'
|
|
191
|
+
? {
|
|
192
|
+
type: type !== null && type !== void 0 ? type : 'button',
|
|
193
|
+
disabled: resolvedDisabled,
|
|
194
|
+
}
|
|
195
|
+
: {};
|
|
196
|
+
/**
|
|
197
|
+
* Memoized className for perf & stability.
|
|
198
|
+
*/
|
|
199
|
+
const classes = useMemo(() => buildClassName(BASE_BTN_CLASS, THEMED_BTN_CLASS[theme][variant], getBtnSizeClasses(size, iconOnly), !noOutlineOnFocus && 'focus:outline focus:outline-2 focus:outline-offset-2', rounded && 'rounded-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', className), [theme, variant, size, iconOnly, rounded, noOutlineOnFocus, className]);
|
|
200
|
+
/**
|
|
201
|
+
* SPECIAL CASE:
|
|
202
|
+
* iconOnly + loading → show spinner only
|
|
203
|
+
*/
|
|
204
|
+
if (loading && iconOnly) {
|
|
205
|
+
return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }) }));
|
|
206
|
+
}
|
|
207
|
+
return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }), loadingText && jsx(TextContent, { children: loadingText })] })) : (children) }));
|
|
169
208
|
});
|
|
209
|
+
Button.displayName = 'Button';
|
|
170
210
|
|
|
171
211
|
const AlertTypeIconMap = {
|
|
172
212
|
success: SvgBadgeCheck,
|
|
@@ -183,8 +223,9 @@ const TypeClassMap = {
|
|
|
183
223
|
const Alert = (props) => {
|
|
184
224
|
const { type = 'info', className = '', message, children, removable = false, onRemove = () => { }, ...restProps } = props;
|
|
185
225
|
const Icon = AlertTypeIconMap[type] || SvgInfo;
|
|
186
|
-
return (jsxs("div", { className: buildClassName(className, 'flex items-center justify-between rounded-md p-3 text-sm ring-1 ring-inset gap-2', TypeClassMap[type]), ...restProps, children: [jsxs("span", { className: "inline-flex items-center gap-2", children: [jsx(Icon, { className: "size-5" }), jsx("span", { className: "pr-2 inline-flex items-center", children: message || children })] }), removable && (jsx(Button, { iconOnly: true, size: "xs", variant: "plain", theme: "secondary", className: "hover:bg-black/5 text-gray-600 dark:hover:bg-black/5 dark:text-gray-600", onClick: () => onRemove(), children: jsx(SvgX, { className: "size-4.5" }) }))] }));
|
|
226
|
+
return (jsxs("div", { className: buildClassName(className, 'flex items-center justify-between rounded-md p-3 text-sm ring-1 ring-inset gap-2', TypeClassMap[type]), ...restProps, children: [jsxs("span", { className: "inline-flex items-center gap-2", children: [jsx(Icon, { className: "size-5", "data-testid": `alert-${type}-icon` }), jsx("span", { className: "pr-2 inline-flex items-center", children: message || children })] }), removable && (jsx(Button, { iconOnly: true, size: "xs", variant: "plain", theme: "secondary", className: "hover:bg-black/5 text-gray-600 dark:hover:bg-black/5 dark:text-gray-600", onClick: () => onRemove(), "data-testid": "alert-remove-btn", children: jsx(SvgX, { className: "size-4.5", "data-testid": "alert-remove-icon" }) }))] }));
|
|
187
227
|
};
|
|
228
|
+
Alert.displayName = 'Alert';
|
|
188
229
|
|
|
189
230
|
const ThemeClassMap = {
|
|
190
231
|
success: 'bg-green-50 ring-green-600/20 text-green-700 dark:bg-green-600 dark:text-green-50 dark:ring-green-400/20',
|
|
@@ -195,13 +236,14 @@ const ThemeClassMap = {
|
|
|
195
236
|
};
|
|
196
237
|
const Badge = forwardRef((props, ref) => {
|
|
197
238
|
const { theme = 'info', className = '', children, removable = false, rounded = false, onRemove = () => { }, ...restProps } = props;
|
|
198
|
-
return (jsxs(
|
|
239
|
+
return (jsxs(TextContent, { ref: ref, className: buildClassName(className, 'inline-flex items-center pl-3 py-1 text-xs font-medium ring-1 ring-inset', ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md'), "data-testid": `badge-${theme}`, ...restProps, children: [jsx(TextContent, { className: "pr-3 inline-flex items-center", children: children }), removable ? (jsx(SvgX, { className: "size-5 px-0.5 mr-1 hover:bg-slate-200 dark:hover:bg-white/10 rounded-sm cursor-pointer text-gray-600 dark:text-gray-300", role: "button", onClick: () => onRemove() })) : null] }));
|
|
199
240
|
});
|
|
241
|
+
Badge.displayName = 'Badge';
|
|
200
242
|
|
|
201
|
-
var _path$
|
|
202
|
-
function _extends$
|
|
243
|
+
var _path$d;
|
|
244
|
+
function _extends$d() { return _extends$d = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$d.apply(null, arguments); }
|
|
203
245
|
var SvgTriangleAlert = function SvgTriangleAlert(props) {
|
|
204
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
246
|
+
return /*#__PURE__*/React.createElement("svg", _extends$d({
|
|
205
247
|
xmlns: "http://www.w3.org/2000/svg",
|
|
206
248
|
width: 24,
|
|
207
249
|
height: 24,
|
|
@@ -212,15 +254,15 @@ var SvgTriangleAlert = function SvgTriangleAlert(props) {
|
|
|
212
254
|
strokeWidth: 2,
|
|
213
255
|
className: "lucide lucide-triangle-alert-icon lucide-triangle-alert",
|
|
214
256
|
viewBox: "0 0 24 24"
|
|
215
|
-
}, props), _path$
|
|
257
|
+
}, props), _path$d || (_path$d = /*#__PURE__*/React.createElement("path", {
|
|
216
258
|
d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4M12 17h.01"
|
|
217
259
|
})));
|
|
218
260
|
};
|
|
219
261
|
|
|
220
|
-
var _circle$1, _path$
|
|
221
|
-
function _extends$
|
|
262
|
+
var _circle$1, _path$c;
|
|
263
|
+
function _extends$c() { return _extends$c = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$c.apply(null, arguments); }
|
|
222
264
|
var SvgCircleCheck = function SvgCircleCheck(props) {
|
|
223
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
265
|
+
return /*#__PURE__*/React.createElement("svg", _extends$c({
|
|
224
266
|
xmlns: "http://www.w3.org/2000/svg",
|
|
225
267
|
width: 24,
|
|
226
268
|
height: 24,
|
|
@@ -235,12 +277,13 @@ var SvgCircleCheck = function SvgCircleCheck(props) {
|
|
|
235
277
|
cx: 12,
|
|
236
278
|
cy: 12,
|
|
237
279
|
r: 10
|
|
238
|
-
})), _path$
|
|
280
|
+
})), _path$c || (_path$c = /*#__PURE__*/React.createElement("path", {
|
|
239
281
|
d: "m9 12 2 2 4-4"
|
|
240
282
|
})));
|
|
241
283
|
};
|
|
242
284
|
|
|
243
285
|
const sizeMap = {
|
|
286
|
+
4: `size-4`,
|
|
244
287
|
5: `size-5`,
|
|
245
288
|
6: `size-6`,
|
|
246
289
|
7: `size-7`,
|
|
@@ -255,14 +298,15 @@ const IconMap = {
|
|
|
255
298
|
warning: SvgTriangleAlert,
|
|
256
299
|
};
|
|
257
300
|
const Banner = forwardRef((props, ref) => {
|
|
258
|
-
const { type = 'info', iconSize = 5, children } = props;
|
|
259
|
-
const className = buildClassName('text-sm banner rounded-md flex w-full p-3
|
|
301
|
+
const { type = 'info', iconSize = 5, children, ...restProps } = props;
|
|
302
|
+
const className = buildClassName('text-sm banner rounded-md flex w-full p-3 items-center', type === 'info' ? 'bg-blue-50 text-blue-700' : '', type === 'error' ? 'bg-red-50 text-red-700' : '', type === 'success' ? 'bg-green-50 text-green-700' : '', type === 'warning' ? 'bg-orange-50 text-orange-700' : '');
|
|
260
303
|
const renderIcon = () => {
|
|
261
304
|
const Icon = IconMap[type];
|
|
262
|
-
return jsx(Icon, { className: buildClassName('ml-1 mr-3', sizeMap[iconSize]) });
|
|
305
|
+
return (jsx(Icon, { "data-testid": `banner-${type}-icon`, className: buildClassName('ml-1 mr-3', sizeMap[iconSize]) }));
|
|
263
306
|
};
|
|
264
|
-
return (jsxs("div", { ref: ref, className: className, children: [renderIcon(), jsx("div", { className: "flex items-center w-full justify-between", children: children })] }));
|
|
307
|
+
return (jsxs("div", { ref: ref, className: className, "data-testid": `banner-${type}`, ...restProps, children: [renderIcon(), jsx("div", { className: "flex items-center w-full justify-between", children: children })] }));
|
|
265
308
|
});
|
|
309
|
+
Banner.displayName = 'Banner';
|
|
266
310
|
|
|
267
311
|
const BodyText = forwardRef((props, ref) => {
|
|
268
312
|
const { as = 'p', small = false, strong = false, muted = false, error = false, inline = false, className, ...rest } = props;
|
|
@@ -272,11 +316,12 @@ const BodyText = forwardRef((props, ref) => {
|
|
|
272
316
|
...rest,
|
|
273
317
|
});
|
|
274
318
|
});
|
|
319
|
+
BodyText.displayName = 'BodyText';
|
|
275
320
|
|
|
276
|
-
var _path$
|
|
277
|
-
function _extends$
|
|
321
|
+
var _path$b;
|
|
322
|
+
function _extends$b() { return _extends$b = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$b.apply(null, arguments); }
|
|
278
323
|
var SvgSlash = function SvgSlash(props) {
|
|
279
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
324
|
+
return /*#__PURE__*/React.createElement("svg", _extends$b({
|
|
280
325
|
xmlns: "http://www.w3.org/2000/svg",
|
|
281
326
|
width: 24,
|
|
282
327
|
height: 24,
|
|
@@ -287,7 +332,7 @@ var SvgSlash = function SvgSlash(props) {
|
|
|
287
332
|
strokeWidth: 2,
|
|
288
333
|
className: "lucide lucide-slash-icon lucide-slash",
|
|
289
334
|
viewBox: "0 0 24 24"
|
|
290
|
-
}, props), _path$
|
|
335
|
+
}, props), _path$b || (_path$b = /*#__PURE__*/React.createElement("path", {
|
|
291
336
|
d: "M22 2 2 22"
|
|
292
337
|
})));
|
|
293
338
|
};
|
|
@@ -295,67 +340,156 @@ var SvgSlash = function SvgSlash(props) {
|
|
|
295
340
|
const Breadcrumb = ({ items, render }) => {
|
|
296
341
|
const count = items.length;
|
|
297
342
|
const isLast = (index) => index === count - 1;
|
|
298
|
-
return (jsx("div", { className: "flex flex-wrap items-center gap-2", children: items.map((item, index) => (jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", children: [
|
|
343
|
+
return (jsx("div", { className: "flex flex-wrap items-center gap-2", "data-testid": "breadcrumb-container", children: items.map((item, index) => (jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", "data-testid": `breadcrumb-item-${item.key}`, children: [jsx("span", { className: "font-medium", children: isLast(index) ? item.text : render(item) }), !isLast(index) && (jsx(SvgSlash, { "data-testid": `breadcrumb-divider-${index}`, className: "size-4" }))] }, item.key))) }));
|
|
299
344
|
};
|
|
345
|
+
Breadcrumb.displayName = 'Breadcrumb';
|
|
300
346
|
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
...rest
|
|
305
|
-
|
|
347
|
+
const BASE_CARD_CLASS = 'bg-white rounded-md dark:bg-gray-800';
|
|
348
|
+
const Card = forwardRef((props, ref) => {
|
|
349
|
+
// Remove Card-only props from rest (avoid leaking to DOM)
|
|
350
|
+
const { as: Element = 'div', hoverable = false, bordered = false, noPadding = false, ...rest } = props;
|
|
351
|
+
// Extract className safely from rest
|
|
352
|
+
const { className, ...cleanRest } = rest;
|
|
353
|
+
const classes = buildClassName(BASE_CARD_CLASS, !noPadding && 'p-2 md:p-4', hoverable && 'shadow hover:shadow-md transition-shadow duration-200', bordered && 'border border-gray-200 dark:border-gray-700', className);
|
|
354
|
+
return jsx(Element, { ref: ref, className: classes, ...cleanRest });
|
|
355
|
+
});
|
|
356
|
+
Card.displayName = 'Card';
|
|
306
357
|
|
|
307
358
|
const Label = forwardRef((props, ref) => {
|
|
308
359
|
const { className, children, ...labelProps } = props;
|
|
309
|
-
return (jsx("label", { ref: ref, ...labelProps, className: buildClassName('block text-sm font-medium text-gray-
|
|
360
|
+
return (jsx("label", { ref: ref, ...labelProps, className: buildClassName('block text-sm font-medium text-gray-600 dark:text-gray-300', className), children: children }));
|
|
310
361
|
});
|
|
362
|
+
Label.displayName = 'Label';
|
|
311
363
|
|
|
312
364
|
const Checkbox = forwardRef((props, ref) => {
|
|
313
|
-
|
|
314
|
-
|
|
365
|
+
var _a;
|
|
366
|
+
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
|
|
367
|
+
// Checkbox-only props removed from rest
|
|
368
|
+
...rest } = props;
|
|
369
|
+
const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : 'checkbox-' + Math.random();
|
|
370
|
+
return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center', containerClass), children: [jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-center relative select-none', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsx("input", { ref: ref, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer size-4.5 cursor-pointer transition-all appearance-none rounded', 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600', 'focus:ring-blue-500 dark:focus:ring-offset-gray-900', disabled
|
|
371
|
+
? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
372
|
+
: 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md', className) }), jsx("span", { className: "absolute text-white opacity-0 peer-checked:opacity-100 \n top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3.25 w-3.25", viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })] }), label && (jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
|
|
373
|
+
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
374
|
+
: 'cursor-pointer text-gray-600 dark:text-gray-300', 'ml-2 font-normal', labelClass), children: label }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
315
375
|
});
|
|
376
|
+
Checkbox.displayName = 'Checkbox';
|
|
316
377
|
|
|
317
378
|
const CheckboxGroup = (props) => {
|
|
318
|
-
const { name, options, label
|
|
379
|
+
const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value = [], onChange, inline = false, } = props;
|
|
319
380
|
const [items, setItems] = useState([]);
|
|
381
|
+
const groupId = useId(); // unique per group
|
|
382
|
+
/**
|
|
383
|
+
* Normalize options (strings → {label, value})
|
|
384
|
+
*/
|
|
320
385
|
useEffect(() => {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return { label: option, value: option };
|
|
324
|
-
}
|
|
325
|
-
return option;
|
|
326
|
-
}));
|
|
386
|
+
const normalized = options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt);
|
|
387
|
+
setItems(normalized);
|
|
327
388
|
}, [options]);
|
|
328
|
-
|
|
389
|
+
/**
|
|
390
|
+
* Group-level change handler. Converts raw checkbox change events
|
|
391
|
+
* into a typed updated values array.
|
|
392
|
+
*/
|
|
393
|
+
const handleGroupChange = (e) => {
|
|
394
|
+
const raw = e.target.value;
|
|
395
|
+
const checked = e.target.checked;
|
|
396
|
+
// Convert HTML string value → typed T value
|
|
397
|
+
// Locate in normalized items
|
|
398
|
+
const matched = items.find((i) => String(i.value) === raw);
|
|
399
|
+
const itemValue = matched === null || matched === void 0 ? void 0 : matched.value;
|
|
400
|
+
let updated;
|
|
401
|
+
if (checked) {
|
|
402
|
+
updated = [...value, itemValue];
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
updated = value.filter((v) => v !== itemValue);
|
|
406
|
+
}
|
|
407
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
408
|
+
};
|
|
409
|
+
return (jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), jsx(BodyText, { muted: true, small: true, children: labelHint })] })), jsx("div", { className: buildClassName('flex gap-3', inline ? 'flex-wrap' : 'flex-col'), children: items.map((item, index) => {
|
|
410
|
+
const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
|
|
411
|
+
return (jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, value: String(item.value), checked: value.includes(item.value), onChange: handleGroupChange }, key));
|
|
412
|
+
}) })] }));
|
|
329
413
|
};
|
|
414
|
+
CheckboxGroup.displayName = 'CheckboxGroup';
|
|
330
415
|
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const SubTitle4 = ({ children, className }) => (jsx("h5", { className: buildClassName('text-base font-medium', className), children: children }));
|
|
336
|
-
const HeadingText = {
|
|
337
|
-
Title,
|
|
338
|
-
SubTitle,
|
|
339
|
-
SubTitle2,
|
|
340
|
-
SubTitle3,
|
|
341
|
-
SubTitle4,
|
|
416
|
+
const TEXT_ALIGN = {
|
|
417
|
+
left: 'text-left',
|
|
418
|
+
center: 'text-center',
|
|
419
|
+
right: 'text-right',
|
|
342
420
|
};
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
421
|
+
const TABLE_LAYOUT = {
|
|
422
|
+
fixed: 'table-fixed',
|
|
423
|
+
auto: 'table-auto',
|
|
424
|
+
};
|
|
425
|
+
/* ----------------------------------------------
|
|
426
|
+
* Loader Rows
|
|
427
|
+
* ---------------------------------------------- */
|
|
428
|
+
const TableRowLoader = ({ colSize, rowSize }) => (jsx(Fragment, { children: Array.from({ length: rowSize }).map((_, rowIdx) => (jsx(TableRow, { className: "animate-pulse", children: Array.from({ length: colSize }).map((_, colIdx) => (jsx(TableCell, { children: jsx("div", { className: "flex items-center h-6", children: jsx("div", { className: "h-4 bg-gray-300 dark:bg-gray-700 shimmer rounded w-full" }) }) }, colIdx))) }, rowIdx))) }));
|
|
429
|
+
/* ----------------------------------------------
|
|
430
|
+
* Table
|
|
431
|
+
* ---------------------------------------------- */
|
|
432
|
+
const Table = ({ children, layout = 'auto', className = '' }) => {
|
|
433
|
+
return (jsx("table", { className: buildClassName('border-collapse w-full divide-y divide-gray-200 dark:divide-gray-700', TABLE_LAYOUT[layout], className), children: children }));
|
|
348
434
|
};
|
|
435
|
+
/* ----------------------------------------------
|
|
436
|
+
* Head
|
|
437
|
+
* ---------------------------------------------- */
|
|
438
|
+
const TableHead = ({ children, className = '' }) => (jsx("thead", { className: buildClassName('text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800 dark:text-gray-300', className), children: jsx("tr", { children: children }) }));
|
|
439
|
+
/* ----------------------------------------------
|
|
440
|
+
* Body
|
|
441
|
+
* ---------------------------------------------- */
|
|
442
|
+
const TableBody = ({ children, loading, colSize, rowSize = 5, className, }) => (jsx("tbody", { className: buildClassName('text-sm text-gray-700 dark:text-white divide-y divide-gray-200 dark:divide-gray-700', className), children: loading ? jsx(TableRowLoader, { colSize: colSize, rowSize: rowSize }) : children }));
|
|
443
|
+
/* ----------------------------------------------
|
|
444
|
+
* Foot
|
|
445
|
+
* ---------------------------------------------- */
|
|
446
|
+
const TableFoot = ({ children }) => (jsx("tfoot", { children: jsx("tr", { children: children }) }));
|
|
447
|
+
/* ----------------------------------------------
|
|
448
|
+
* Rows
|
|
449
|
+
* ---------------------------------------------- */
|
|
450
|
+
const TableRow = ({ children, hoverable = false, className, ...props }) => (jsx("tr", { ...props, className: buildClassName('last:border-b border-gray-200 dark:border-gray-700', hoverable && 'group', className), children: children }));
|
|
451
|
+
/* ----------------------------------------------
|
|
452
|
+
* Cells
|
|
453
|
+
* ---------------------------------------------- */
|
|
454
|
+
const TableCell = ({ children, align = 'left', className, ...props }) => (jsx("td", { ...props, className: buildClassName('px-3 py-3 first:pl-4 last:pr-4 bg-white dark:bg-gray-900 group-hover:bg-gray-50 group-hover:dark:bg-gray-800', TEXT_ALIGN[align], className), children: jsx("div", { className: "leading-normal", children: children }) }));
|
|
455
|
+
const TableHeaderCell = ({ children, align = 'left', className, ...props }) => (jsx("th", { ...props, className: buildClassName('px-3 py-3 first:pl-4 last:pr-4 tracking-wider whitespace-nowrap text-gray-800 dark:text-gray-300', TEXT_ALIGN[align], className), children: jsx(BodyText, { as: "span", className: "leading-none", children: children }) }));
|
|
456
|
+
/* ----------------------------------------------
|
|
457
|
+
* Empty Row
|
|
458
|
+
* ---------------------------------------------- */
|
|
459
|
+
const EmptyTableRow = ({ children, colSpan = 1, contentClass, }) => (jsx("tr", { children: jsx("td", { colSpan: colSpan, className: "px-4 py-8", children: jsx("div", { className: buildClassName('p-6 text-center space-y-3 flex flex-col items-center justify-center', contentClass), children: children }) }) }));
|
|
349
460
|
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
461
|
+
const DOTS = '...';
|
|
462
|
+
const usePagination = ({ totalPages, currentPage, siblingCount = 1, // number of pages to show around currentPage
|
|
463
|
+
}) => {
|
|
464
|
+
const totalPageNumbers = siblingCount * 2 + 5;
|
|
465
|
+
if (totalPageNumbers >= totalPages) {
|
|
466
|
+
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
467
|
+
}
|
|
468
|
+
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
|
|
469
|
+
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
|
|
470
|
+
const shouldShowLeftDots = leftSiblingIndex > 2;
|
|
471
|
+
const shouldShowRightDots = rightSiblingIndex < totalPages - 1;
|
|
472
|
+
const firstPageIndex = 1;
|
|
473
|
+
const lastPageIndex = totalPages;
|
|
474
|
+
if (!shouldShowLeftDots && shouldShowRightDots) {
|
|
475
|
+
const leftRange = Array.from({ length: 3 + 2 * siblingCount }, (_, i) => i + 1);
|
|
476
|
+
return [...leftRange, DOTS, totalPages];
|
|
477
|
+
}
|
|
478
|
+
if (shouldShowLeftDots && !shouldShowRightDots) {
|
|
479
|
+
const rightRange = Array.from({ length: 3 + 2 * siblingCount }, (_, i) => totalPages - (3 + 2 * siblingCount) + 1 + i);
|
|
480
|
+
return [firstPageIndex, DOTS, ...rightRange];
|
|
481
|
+
}
|
|
482
|
+
if (shouldShowLeftDots && shouldShowRightDots) {
|
|
483
|
+
const middleRange = Array.from({ length: 2 * siblingCount + 1 }, (_, i) => leftSiblingIndex + i);
|
|
484
|
+
return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex];
|
|
485
|
+
}
|
|
486
|
+
return [];
|
|
487
|
+
};
|
|
354
488
|
|
|
355
|
-
var _path$
|
|
356
|
-
function _extends$
|
|
489
|
+
var _path$a;
|
|
490
|
+
function _extends$a() { return _extends$a = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$a.apply(null, arguments); }
|
|
357
491
|
var SvgChevronLeft = function SvgChevronLeft(props) {
|
|
358
|
-
return /*#__PURE__*/React.createElement("svg", _extends$
|
|
492
|
+
return /*#__PURE__*/React.createElement("svg", _extends$a({
|
|
359
493
|
xmlns: "http://www.w3.org/2000/svg",
|
|
360
494
|
width: 24,
|
|
361
495
|
height: 24,
|
|
@@ -366,48 +500,184 @@ var SvgChevronLeft = function SvgChevronLeft(props) {
|
|
|
366
500
|
strokeWidth: 2,
|
|
367
501
|
className: "lucide lucide-chevron-left-icon lucide-chevron-left",
|
|
368
502
|
viewBox: "0 0 24 24"
|
|
369
|
-
}, props), _path$
|
|
503
|
+
}, props), _path$a || (_path$a = /*#__PURE__*/React.createElement("path", {
|
|
370
504
|
d: "m15 18-6-6 6-6"
|
|
371
505
|
})));
|
|
372
506
|
};
|
|
373
507
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
508
|
+
var _path$9;
|
|
509
|
+
function _extends$9() { return _extends$9 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$9.apply(null, arguments); }
|
|
510
|
+
var SvgChevronRight = function SvgChevronRight(props) {
|
|
511
|
+
return /*#__PURE__*/React.createElement("svg", _extends$9({
|
|
512
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
513
|
+
width: 24,
|
|
514
|
+
height: 24,
|
|
515
|
+
fill: "none",
|
|
516
|
+
stroke: "currentColor",
|
|
517
|
+
strokeLinecap: "round",
|
|
518
|
+
strokeLinejoin: "round",
|
|
519
|
+
strokeWidth: 2,
|
|
520
|
+
className: "lucide lucide-chevron-right-icon lucide-chevron-right",
|
|
521
|
+
viewBox: "0 0 24 24"
|
|
522
|
+
}, props), _path$9 || (_path$9 = /*#__PURE__*/React.createElement("path", {
|
|
523
|
+
d: "m9 18 6-6-6-6"
|
|
524
|
+
})));
|
|
381
525
|
};
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
526
|
+
|
|
527
|
+
const Pagination = ({ page: currentPage, limit, total, onChange, className, ellipsis = true, // Default to showing ellipsis
|
|
528
|
+
}) => {
|
|
529
|
+
const pages = new Array(Math.ceil(total / limit)).fill(null).map((_, i) => i + 1);
|
|
530
|
+
const isFirstPage = currentPage === 1;
|
|
531
|
+
const isLastPage = total === 0 || pages.length === currentPage;
|
|
532
|
+
const range = usePagination({ totalPages: pages.length, currentPage });
|
|
533
|
+
const handlePageChange = (nextVal) => {
|
|
534
|
+
if (nextVal === currentPage)
|
|
535
|
+
return;
|
|
536
|
+
onChange({ page: nextVal });
|
|
537
|
+
};
|
|
538
|
+
if (ellipsis) {
|
|
539
|
+
return (jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6', className), children: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-mobile-prev-btn", children: [jsx("span", { children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-mobile-next-btn", children: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-next-btn", children: "Prev" }) }), range.map((page, idx) => {
|
|
540
|
+
if (page === DOTS) {
|
|
541
|
+
return (jsx("li", { "data-testid": `page-${page}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${page}-${idx}`));
|
|
542
|
+
}
|
|
543
|
+
return (jsx("li", { children: jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: page }) }, `${page}-${idx}`));
|
|
544
|
+
}), jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-prev-btn", children: "Next" }) })] })] })] }));
|
|
545
|
+
}
|
|
546
|
+
return (jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6', className), children: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "mobile-prev-btn", children: [jsx("span", { children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "mobile-next-btn", children: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("nav", { className: "isolate inline-flex items-center rounded-md", "aria-label": "Pagination", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "next-btn", children: [jsx("span", { className: "sr-only", children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), pages.map((page) => (jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: jsx("span", { className: "size-4 block", children: page }) }, page))), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "prev-btn", children: [jsx("span", { className: "sr-only", children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] })] })] }));
|
|
389
547
|
};
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
548
|
+
|
|
549
|
+
function get(obj, path, defaultValue) {
|
|
550
|
+
var _a;
|
|
551
|
+
if (!path)
|
|
552
|
+
return defaultValue;
|
|
553
|
+
const keys = path
|
|
554
|
+
.replace(/\[(\d+)]/g, '.$1') // convert [0] to .0
|
|
555
|
+
.split('.')
|
|
556
|
+
.filter(Boolean); // remove empty parts
|
|
557
|
+
return ((_a = keys.reduce((acc, key) => {
|
|
558
|
+
if (acc !== null && acc !== undefined && Object.prototype.hasOwnProperty.call(acc, key)) {
|
|
559
|
+
return acc[key];
|
|
560
|
+
}
|
|
561
|
+
return undefined;
|
|
562
|
+
}, obj)) !== null && _a !== void 0 ? _a : defaultValue);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Compute the next sorting direction for a column.
|
|
567
|
+
* - string: undefined → asc → desc → undefined
|
|
568
|
+
* - number/date: undefined → 1 → -1 → undefined
|
|
569
|
+
*/
|
|
570
|
+
const nextSortDirection = (prev, type = 'string') => {
|
|
571
|
+
if (type === 'string') {
|
|
572
|
+
if (!prev)
|
|
573
|
+
return 'asc';
|
|
574
|
+
if (prev === 'asc')
|
|
575
|
+
return 'desc';
|
|
576
|
+
return undefined;
|
|
577
|
+
}
|
|
578
|
+
// number / date
|
|
579
|
+
if (!prev)
|
|
580
|
+
return '1';
|
|
581
|
+
if (prev === '1')
|
|
582
|
+
return '-1';
|
|
583
|
+
return undefined;
|
|
398
584
|
};
|
|
399
|
-
|
|
400
|
-
|
|
585
|
+
/**
|
|
586
|
+
* Update the sort query object for a table column.
|
|
587
|
+
* Toggles sort direction, removing key when reset.
|
|
588
|
+
*/
|
|
589
|
+
const updateSortQuery = (sortQuery, name, type = 'string') => {
|
|
590
|
+
const next = nextSortDirection(sortQuery[name], type);
|
|
591
|
+
// Shallow clone once
|
|
592
|
+
const updated = { ...sortQuery };
|
|
593
|
+
if (next == null) {
|
|
594
|
+
delete updated[name];
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
updated[name] = next;
|
|
598
|
+
}
|
|
599
|
+
return updated;
|
|
401
600
|
};
|
|
402
601
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
602
|
+
var _path$8;
|
|
603
|
+
function _extends$8() { return _extends$8 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$8.apply(null, arguments); }
|
|
604
|
+
var SvgArrowDownWideNarrow = function SvgArrowDownWideNarrow(props) {
|
|
605
|
+
return /*#__PURE__*/React.createElement("svg", _extends$8({
|
|
606
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
607
|
+
width: 24,
|
|
608
|
+
height: 24,
|
|
609
|
+
fill: "none",
|
|
610
|
+
stroke: "currentColor",
|
|
611
|
+
strokeLinecap: "round",
|
|
612
|
+
strokeLinejoin: "round",
|
|
613
|
+
strokeWidth: 2,
|
|
614
|
+
className: "lucide lucide-arrow-down-wide-narrow-icon lucide-arrow-down-wide-narrow",
|
|
615
|
+
viewBox: "0 0 24 24"
|
|
616
|
+
}, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
|
|
617
|
+
d: "m3 16 4 4 4-4M7 20V4M11 4h10M11 8h7M11 12h4"
|
|
618
|
+
})));
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
var _path$7;
|
|
622
|
+
function _extends$7() { return _extends$7 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$7.apply(null, arguments); }
|
|
623
|
+
var SvgArrowDownNarrowWide = function SvgArrowDownNarrowWide(props) {
|
|
624
|
+
return /*#__PURE__*/React.createElement("svg", _extends$7({
|
|
625
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
626
|
+
width: 24,
|
|
627
|
+
height: 24,
|
|
628
|
+
fill: "none",
|
|
629
|
+
stroke: "currentColor",
|
|
630
|
+
strokeLinecap: "round",
|
|
631
|
+
strokeLinejoin: "round",
|
|
632
|
+
strokeWidth: 2,
|
|
633
|
+
className: "lucide lucide-arrow-down-narrow-wide-icon lucide-arrow-down-narrow-wide",
|
|
634
|
+
viewBox: "0 0 24 24"
|
|
635
|
+
}, props), _path$7 || (_path$7 = /*#__PURE__*/React.createElement("path", {
|
|
636
|
+
d: "m3 16 4 4 4-4M7 20V4M11 4h4M11 8h7M11 12h10"
|
|
637
|
+
})));
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
var _path$6;
|
|
641
|
+
function _extends$6() { return _extends$6 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$6.apply(null, arguments); }
|
|
642
|
+
var SvgArrowDownAz = function SvgArrowDownAz(props) {
|
|
643
|
+
return /*#__PURE__*/React.createElement("svg", _extends$6({
|
|
644
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
645
|
+
width: 24,
|
|
646
|
+
height: 24,
|
|
647
|
+
fill: "none",
|
|
648
|
+
stroke: "currentColor",
|
|
649
|
+
strokeLinecap: "round",
|
|
650
|
+
strokeLinejoin: "round",
|
|
651
|
+
strokeWidth: 2,
|
|
652
|
+
className: "lucide lucide-arrow-down-az-icon lucide-arrow-down-a-z",
|
|
653
|
+
viewBox: "0 0 24 24"
|
|
654
|
+
}, props), _path$6 || (_path$6 = /*#__PURE__*/React.createElement("path", {
|
|
655
|
+
d: "m3 16 4 4 4-4M7 20V4M20 8h-5M15 10V6.5a2.5 2.5 0 0 1 5 0V10M15 14h5l-5 6h5"
|
|
656
|
+
})));
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
var _path$5;
|
|
660
|
+
function _extends$5() { return _extends$5 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$5.apply(null, arguments); }
|
|
661
|
+
var SvgArrowDownZa = function SvgArrowDownZa(props) {
|
|
662
|
+
return /*#__PURE__*/React.createElement("svg", _extends$5({
|
|
663
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
664
|
+
width: 24,
|
|
665
|
+
height: 24,
|
|
666
|
+
fill: "none",
|
|
667
|
+
stroke: "currentColor",
|
|
668
|
+
strokeLinecap: "round",
|
|
669
|
+
strokeLinejoin: "round",
|
|
670
|
+
strokeWidth: 2,
|
|
671
|
+
className: "lucide lucide-arrow-down-za-icon lucide-arrow-down-z-a",
|
|
672
|
+
viewBox: "0 0 24 24"
|
|
673
|
+
}, props), _path$5 || (_path$5 = /*#__PURE__*/React.createElement("path", {
|
|
674
|
+
d: "m3 16 4 4 4-4M7 4v16M15 4h5l-5 6h5M15 20v-3.5a2.5 2.5 0 0 1 5 0V20M20 18h-5"
|
|
675
|
+
})));
|
|
406
676
|
};
|
|
407
677
|
|
|
408
678
|
var _path$4;
|
|
409
679
|
function _extends$4() { return _extends$4 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$4.apply(null, arguments); }
|
|
410
|
-
var
|
|
680
|
+
var SvgArrowUpDown = function SvgArrowUpDown(props) {
|
|
411
681
|
return /*#__PURE__*/React.createElement("svg", _extends$4({
|
|
412
682
|
xmlns: "http://www.w3.org/2000/svg",
|
|
413
683
|
width: 24,
|
|
@@ -417,56 +687,137 @@ var SvgFolderPlus = function SvgFolderPlus(props) {
|
|
|
417
687
|
strokeLinecap: "round",
|
|
418
688
|
strokeLinejoin: "round",
|
|
419
689
|
strokeWidth: 2,
|
|
420
|
-
className: "lucide lucide-
|
|
690
|
+
className: "lucide lucide-arrow-up-down-icon lucide-arrow-up-down",
|
|
421
691
|
viewBox: "0 0 24 24"
|
|
422
692
|
}, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
|
|
423
|
-
d: "
|
|
693
|
+
d: "m21 16-4 4-4-4M17 20V4M3 8l4-4 4 4M7 4v16"
|
|
424
694
|
})));
|
|
425
695
|
};
|
|
426
696
|
|
|
427
|
-
const
|
|
428
|
-
|
|
697
|
+
const SortIconMap = {
|
|
698
|
+
'1': (jsx(SvgArrowDownNarrowWide, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" })),
|
|
699
|
+
asc: (jsx(SvgArrowDownAz, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" })),
|
|
700
|
+
'-1': (jsx(SvgArrowDownWideNarrow, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" })),
|
|
701
|
+
desc: (jsx(SvgArrowDownZa, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" })),
|
|
429
702
|
};
|
|
703
|
+
const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
|
|
430
704
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
return
|
|
705
|
+
/**
|
|
706
|
+
* Smart auto-sort helper (optional usage)
|
|
707
|
+
*/
|
|
708
|
+
const autoSortItems = (items, column, sort) => {
|
|
709
|
+
const path = column.name;
|
|
710
|
+
return [...items].sort((a, b) => {
|
|
711
|
+
const valueA = get(a, path);
|
|
712
|
+
const valueB = get(b, path);
|
|
713
|
+
if (column.type === 'number') {
|
|
714
|
+
const nA = Number(valueA);
|
|
715
|
+
const nB = Number(valueB);
|
|
716
|
+
return sort === 'asc' || sort === '1' ? nA - nB : nB - nA;
|
|
717
|
+
}
|
|
718
|
+
if (column.type === 'date') {
|
|
719
|
+
const dA = new Date(valueA).getTime();
|
|
720
|
+
const dB = new Date(valueB).getTime();
|
|
721
|
+
return sort === 'asc' || sort === '1' ? dA - dB : dB - dA;
|
|
722
|
+
}
|
|
723
|
+
// String fallback
|
|
724
|
+
const sA = String(valueA).toLowerCase();
|
|
725
|
+
const sB = String(valueB).toLowerCase();
|
|
726
|
+
if (sA < sB)
|
|
727
|
+
return sort === 'asc' || sort === '1' ? -1 : 1;
|
|
728
|
+
if (sA > sB)
|
|
729
|
+
return sort === 'asc' || sort === '1' ? 1 : -1;
|
|
730
|
+
return 0;
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination = null, setPagination = () => { }, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', layout = 'auto', }) => {
|
|
734
|
+
const internalSorting = useRef({});
|
|
735
|
+
const doAutoSort = (column) => {
|
|
736
|
+
internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
|
|
737
|
+
autoSortItems(items, column, internalSorting.current[column.name]);
|
|
738
|
+
};
|
|
739
|
+
const handleSort = (column) => {
|
|
740
|
+
if (!column.sortable)
|
|
741
|
+
return;
|
|
742
|
+
onSort ? onSort(column) : doAutoSort(column);
|
|
743
|
+
};
|
|
744
|
+
return (jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [jsx("div", { className: buildClassName('overflow-x-auto overflow-y-hidden rounded-lg border border-gray-200 dark:border-gray-800', wrapperClass, pagination && 'rounded-b-none border-b-0'), children: jsxs(Table, { className: "relative w-full max-w-full h-fit", layout: layout, children: [jsx(TableHead, { children: columns.map((column) => (jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, className: buildClassName('bg-gray-50 dark:bg-gray-800', column.sticky === 'left' &&
|
|
745
|
+
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
746
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), children: jsxs(TextContent, { role: column.sortable ? 'button' : undefined, className: buildClassName('group', column.sortable && 'inline-flex items-center gap-2 cursor-pointer select-none'), onClick: () => handleSort(column), children: [jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(sorting[column.name]) : null] }) }, column.name))) }), jsx(TableBody, { loading: loading, colSize: columns.length, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && items.length === 0 ? (jsx(EmptyTableRow, { colSpan: columns.length, children: emptyMessage })) : (items.map((item) => (jsx(TableRow, { className: "group", children: columns.map((column) => {
|
|
747
|
+
var _a;
|
|
748
|
+
return (jsx(TableCell, { align: column.align, className: buildClassName(column.sticky === 'left' &&
|
|
749
|
+
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
750
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), children: column.render
|
|
751
|
+
? column.render(item)
|
|
752
|
+
: ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
|
|
753
|
+
}) }, item.id)))) })] }) }), pagination && (jsx(Pagination, { ...pagination, className: "rounded-b-lg border border-t-0 border-gray-200 dark:border-gray-800", onChange: setPagination }))] }));
|
|
754
|
+
};
|
|
755
|
+
DataTable.displayName = 'DataTable';
|
|
756
|
+
|
|
757
|
+
const Title = ({ children, className }) => (jsx("h1", { className: buildClassName('text-3xl font-bold', className), children: children }));
|
|
758
|
+
const SubTitle = ({ children, className }) => (jsx("h2", { className: buildClassName('text-2xl font-semibold', className), children: children }));
|
|
759
|
+
const SubTitle2 = ({ children, className }) => (jsx("h3", { className: buildClassName('text-xl font-semibold', className), children: children }));
|
|
760
|
+
const SubTitle3 = ({ children, className }) => (jsx("h4", { className: buildClassName('text-lg font-medium', className), children: children }));
|
|
761
|
+
const SubTitle4 = ({ children, className }) => (jsx("h5", { className: buildClassName('text-base font-medium', className), children: children }));
|
|
762
|
+
const HeadingText = {
|
|
763
|
+
Title,
|
|
764
|
+
SubTitle,
|
|
765
|
+
SubTitle2,
|
|
766
|
+
SubTitle3,
|
|
767
|
+
SubTitle4,
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
const DetailedInformation = forwardRef((props, ref) => {
|
|
771
|
+
const { className, title, subTitle, details, compact = false, divider = true, titleClass, detailsClass, ...divProps } = props;
|
|
772
|
+
const hasTitles = title || subTitle;
|
|
773
|
+
const filteredDetails = details.filter((detail) => !detail.hidden);
|
|
774
|
+
return (jsxs("div", { className: buildClassName('p-2 md:p-4', compact ? 'md:p-2' : '', className), ref: ref, ...divProps, children: [hasTitles && (jsxs("div", { className: "px-4 sm:px-0 pt-2 sm:pt-0 mb-2", children: [title && (jsx(HeadingText.SubTitle2, { className: buildClassName(titleClass), children: title })), subTitle && (jsx("p", { className: "mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-300", children: subTitle }))] })), filteredDetails.length ? (jsx("div", { className: buildClassName('border-gray-100 dark:border-gray-700', hasTitles && 'border-t', detailsClass), children: jsx("dl", { className: buildClassName(divider && 'divide-y divide-gray-100 dark:divide-gray-700'), children: filteredDetails.map((detail) => (jsxs("div", { className: buildClassName('px-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 items-center', compact ? 'py-1.5' : 'py-2.5'), children: [jsx("dt", { className: "text-sm/6 font-medium text-gray-900 dark:text-gray-100", children: detail.label }), jsx("dd", { className: buildClassName('mt-1 text-sm/6 text-gray-700 dark:text-gray-300 sm:col-span-2 sm:mt-0', detail.contentClass), children: detail.value || '- -' })] }, detail.label))) }) })) : null] }));
|
|
437
775
|
});
|
|
776
|
+
DetailedInformation.displayName = 'DetailedInformation';
|
|
438
777
|
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const firstPageIndex = 1;
|
|
451
|
-
const lastPageIndex = totalPages;
|
|
452
|
-
if (!shouldShowLeftDots && shouldShowRightDots) {
|
|
453
|
-
const leftRange = Array.from({ length: 3 + 2 * siblingCount }, (_, i) => i + 1);
|
|
454
|
-
return [...leftRange, DOTS, totalPages];
|
|
455
|
-
}
|
|
456
|
-
if (shouldShowLeftDots && !shouldShowRightDots) {
|
|
457
|
-
const rightRange = Array.from({ length: 3 + 2 * siblingCount }, (_, i) => totalPages - (3 + 2 * siblingCount) + 1 + i);
|
|
458
|
-
return [firstPageIndex, DOTS, ...rightRange];
|
|
459
|
-
}
|
|
460
|
-
if (shouldShowLeftDots && shouldShowRightDots) {
|
|
461
|
-
const middleRange = Array.from({ length: 2 * siblingCount + 1 }, (_, i) => leftSiblingIndex + i);
|
|
462
|
-
return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex];
|
|
463
|
-
}
|
|
464
|
-
return [];
|
|
778
|
+
const Dot = ({ as = 'span', className, ...props }) => React__default.createElement(as, {
|
|
779
|
+
className: buildClassName('inline-block rounded-full bg-gray-500 dark:bg-gray-300', 'w-1 h-1', className),
|
|
780
|
+
...props,
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
const alignmentMap = {
|
|
784
|
+
center: 'items-center justify-center h-full',
|
|
785
|
+
start: 'justify-start h-full',
|
|
786
|
+
end: 'justify-end h-full',
|
|
787
|
+
top: 'items-center justify-center',
|
|
788
|
+
bottom: 'items-end justify-center h-full',
|
|
465
789
|
};
|
|
790
|
+
const panelAlignmentMap = {
|
|
791
|
+
center: 'data-[closed]:scale-95 rounded-lg',
|
|
792
|
+
start: 'data-[closed]:-translate-x-1/2 rounded-r-lg',
|
|
793
|
+
end: 'data-[closed]:translate-x-1/2 rounded-l-lg',
|
|
794
|
+
top: 'data-[closed]:-translate-y-1/2 rounded-b-lg',
|
|
795
|
+
bottom: 'data-[closed]:translate-y-1/2 rounded-t-lg',
|
|
796
|
+
};
|
|
797
|
+
const panelSizeMap = {
|
|
798
|
+
xs: 'max-w-xs',
|
|
799
|
+
sm: 'max-w-sm',
|
|
800
|
+
md: 'max-w-md',
|
|
801
|
+
lg: 'max-w-lg',
|
|
802
|
+
xl: 'max-w-xl',
|
|
803
|
+
'2xl': 'max-w-2xl',
|
|
804
|
+
full: 'w-full',
|
|
805
|
+
};
|
|
806
|
+
const Drawer = ({ isOpen, onClose = () => { }, align = 'end', size = 'md', backdrop = true, title, description, children, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, }) => {
|
|
807
|
+
const showTitleSection = Boolean(title || description);
|
|
808
|
+
return (jsxs(Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: onClose, transition: true, children: [backdrop && jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/20 transition-opacity" }), jsx("div", { className: "fixed inset-0 w-screen h-screen", children: jsx("div", { className: buildClassName('flex', alignmentMap[align]), children: jsxs(DialogPanel, { transition: true, className: buildClassName('w-full bg-white dark:bg-gray-800 backdrop-blur-2xl shadow-sm', 'overflow-y-auto space-y-4 duration-200 ease-out data-[closed]:opacity-0', panelAlignmentMap[align], panelSizeMap[size], panelClass), children: [showTitleSection && (jsxs("div", { className: buildClassName('space-y-2 pt-2 md:pt-4 px-4 md:px-6', titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10'), children: [jsxs(DialogTitle, { as: "h3", className: buildClassName('text-lg font-medium dark:text-white flex items-center gap-2', titleClass), children: [showBackButton && (jsx(Button, { iconOnly: true, variant: "plain", theme: "secondary", size: "sm", onClick: onClose, children: jsx(SvgChevronLeft, { className: "size-5" }) })), title] }), description && (jsx("p", { className: buildClassName('text-sm dark:text-white/60', descriptionClass), children: description }))] })), jsx("div", { className: contentClass, children: children })] }) }) })] }));
|
|
809
|
+
};
|
|
810
|
+
Drawer.displayName = 'Drawer';
|
|
811
|
+
|
|
812
|
+
const BASE_ITEM_CLASS = 'flex w-full px-3 py-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg';
|
|
813
|
+
const Dropdown = ({ triggerButton, renderTriggerButton, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', menuItemClass, onMenuClick, }) => {
|
|
814
|
+
return (jsxs(Menu, { as: "div", className: "relative inline-block", children: [renderTriggerButton ? (jsx(MenuButton, { as: Fragment$1, children: (state) => renderTriggerButton(state) })) : (jsx(MenuButton, { as: Fragment$1, children: triggerButton })), jsx(MenuItems, { transition: transition, anchor: anchor, className: buildClassName('w-52 origin-top-right rounded-xl shadow-sm p-1 text-sm transition duration-100 ease-out', 'border border-gray-100 bg-white text-gray-800', 'dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200', 'focus:outline-none data-[closed]:scale-95 data-[closed]:opacity-0', '[--anchor-gap:--spacing(1)]', itemsContainerClass), "data-testid": "dropdown-items", as: "ul", children: items.map((item, idx) => (jsxs(Fragment$1, { children: [jsx(MenuItem, { as: "li", disabled: item.disabled, children: ({ disabled }) => (jsx("div", { onClick: () => !disabled && (onMenuClick === null || onMenuClick === void 0 ? void 0 : onMenuClick(item, idx)), className: buildClassName(BASE_ITEM_CLASS, menuItemClass, disabled && 'cursor-not-allowed opacity-70'), "data-testid": `dropdown-item-${item.id}`, children: renderItem ? renderItem(item) : item.label })) }), item.divider && (jsx("li", { className: "my-1 h-px bg-gray-200 dark:bg-white/5", "data-testid": "dropdown-divider" }, item.id))] }, item.id))) })] }));
|
|
815
|
+
};
|
|
816
|
+
Dropdown.displayName = 'Dropdown';
|
|
466
817
|
|
|
467
818
|
var _path$3;
|
|
468
819
|
function _extends$3() { return _extends$3 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$3.apply(null, arguments); }
|
|
469
|
-
var
|
|
820
|
+
var SvgFolderPlus = function SvgFolderPlus(props) {
|
|
470
821
|
return /*#__PURE__*/React.createElement("svg", _extends$3({
|
|
471
822
|
xmlns: "http://www.w3.org/2000/svg",
|
|
472
823
|
width: 24,
|
|
@@ -476,35 +827,33 @@ var SvgChevronRight = function SvgChevronRight(props) {
|
|
|
476
827
|
strokeLinecap: "round",
|
|
477
828
|
strokeLinejoin: "round",
|
|
478
829
|
strokeWidth: 2,
|
|
479
|
-
className: "lucide lucide-
|
|
830
|
+
className: "lucide lucide-folder-plus-icon lucide-folder-plus",
|
|
480
831
|
viewBox: "0 0 24 24"
|
|
481
832
|
}, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
|
|
482
|
-
d: "
|
|
833
|
+
d: "M12 10v6M9 13h6M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
|
|
483
834
|
})));
|
|
484
835
|
};
|
|
485
836
|
|
|
486
|
-
const
|
|
487
|
-
})
|
|
488
|
-
const pages = new Array(Math.ceil(total / limit)).fill(null).map((_, i) => i + 1);
|
|
489
|
-
const isFirstPage = currentPage === 1;
|
|
490
|
-
const isLastPage = total === 0 || pages.length === currentPage;
|
|
491
|
-
const range = usePagination({ totalPages: pages.length, currentPage });
|
|
492
|
-
const handlePageChange = (nextVal) => {
|
|
493
|
-
if (nextVal === currentPage)
|
|
494
|
-
return;
|
|
495
|
-
onChange({ page: nextVal });
|
|
496
|
-
};
|
|
497
|
-
if (ellipsis) {
|
|
498
|
-
return (jsx("div", { className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6', className), children: jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), children: "Prev" }) }), range.map((page, idx) => {
|
|
499
|
-
if (page === DOTS) {
|
|
500
|
-
return (jsx("li", { className: "px-2 py-1 text-gray-400", children: "\u2026" }, page + idx));
|
|
501
|
-
}
|
|
502
|
-
return (jsx("li", { children: jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: page }, page) }, page));
|
|
503
|
-
}), jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), children: "Next" }) })] })] }) }));
|
|
504
|
-
}
|
|
505
|
-
return (jsxs("div", { className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6', className), children: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsx("a", { href: "#", className: "relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50", children: "Previous" }), jsx("a", { href: "#", className: "relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50", children: "Next" })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("nav", { className: "isolate inline-flex items-center rounded-md", "aria-label": "Pagination", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), children: [jsx("span", { className: "sr-only", children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), pages.map((page) => (jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: jsx("span", { className: "size-4 block", children: page }) }, page))), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), children: [jsx("span", { className: "sr-only", children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] })] })] }));
|
|
837
|
+
const EmptyState = ({ title, description, className, action = null, }) => {
|
|
838
|
+
return (jsx("div", { className: buildClassName('w-full flex justify-center px-4 pt-16', className), children: jsxs("div", { className: "max-w-md flex flex-col items-center gap-4", children: [jsx(SvgFolderPlus, { className: "size-8" }), jsx(HeadingText.SubTitle, { children: title }), description && (jsx(BodyText, { as: "p", className: "text-center", muted: true, small: true, children: description })), action] }) }));
|
|
506
839
|
};
|
|
507
840
|
|
|
841
|
+
function buildInputClass(className, { isFocusWithin = false, disabled = false } = {}) {
|
|
842
|
+
return buildClassName('transition-all', 'block w-full rounded-lg bg-white px-3.5 py-2 text-base sm:text-sm/6', 'text-gray-900 outline outline-1 -outline-offset-1', 'outline-gray-300 dark:outline-gray-600', 'invalid:group-[.has-error]:outline-red-500', disabled
|
|
843
|
+
? 'pointer-events-none bg-gray-100 dark:bg-gray-800 dark:text-gray-600 dark:placeholder-gray-600 placeholder-gray-400 outline-gray-200 dark:outline-gray-700'
|
|
844
|
+
: 'bg-white dark:bg-gray-700 dark:text-white outline-gray-300 dark:outline-gray-600 dark:placeholder-gray-400 placeholder-gray-400', isFocusWithin
|
|
845
|
+
? 'focus-within:outline-blue-600 dark:focus-within:outline-blue-600 focus-within:outline-2 focus-within:-outline-offset-2'
|
|
846
|
+
: 'focus:outline-blue-600 dark:focus:outline-blue-600 focus:outline-2 focus:-outline-offset-2', className);
|
|
847
|
+
}
|
|
848
|
+
const Input = forwardRef((props, ref) => {
|
|
849
|
+
const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, ...inputProps } = props;
|
|
850
|
+
const hasError = Boolean(error);
|
|
851
|
+
const hasRightGroup = rightGroup !== null && rightGroup !== undefined;
|
|
852
|
+
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
853
|
+
return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsx("input", { ref: ref, autoComplete: "off", ...inputProps, name: name, id: id, type: type, className: buildClassName(buildInputClass(className), hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '') }), hasRightGroup && (jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center w-10", children: rightGroup }))] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
854
|
+
});
|
|
855
|
+
Input.displayName = 'Input';
|
|
856
|
+
|
|
508
857
|
const PASSWORD_TYPE = 'password';
|
|
509
858
|
const PASSWORD_PLACEHOLDER = '••••••••••';
|
|
510
859
|
const PasswordInput = (props) => {
|
|
@@ -513,13 +862,21 @@ const PasswordInput = (props) => {
|
|
|
513
862
|
return (jsx(Input, { ...inputProps, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: PASSWORD_PLACEHOLDER, rightGroup: jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1 mr-1", type: "button", onClick: () => setShowPassword(() => !showPassword), tabIndex: -1, children: showPassword ? (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: [jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" }), jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }));
|
|
514
863
|
};
|
|
515
864
|
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
865
|
+
const Radio = forwardRef((props, ref) => {
|
|
866
|
+
var _a;
|
|
867
|
+
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
|
|
868
|
+
// RadioButton-only props removed from rest
|
|
869
|
+
...rest } = props;
|
|
870
|
+
const radioId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : 'checkbox-' + Math.random();
|
|
871
|
+
return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center gap-2', containerClass), children: [jsx("div", { className: "grid place-items-center", children: jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-selected": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 size-4.5 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'focus:ring-blue-500 checked:focus:ring-blue-600 dark:focus:ring-offset-gray-800', disabled
|
|
872
|
+
? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
873
|
+
: buildClassName('bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500', 'checked:border-blue-600 checked:hover:border-blue-600 dark:checked:border-blue-600 dark:checked:hover:border-blue-600'), className) }) }), label && (jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
|
|
874
|
+
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
875
|
+
: 'cursor-pointer text-gray-600 dark:text-gray-300', labelClass), children: label }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
519
876
|
});
|
|
520
877
|
|
|
521
|
-
const
|
|
522
|
-
const { name, label
|
|
878
|
+
const RadioGroup = (props) => {
|
|
879
|
+
const { name, label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, row, error, showErrorMessage, onChange, } = props;
|
|
523
880
|
const [items, setItems] = useState([]);
|
|
524
881
|
useEffect(() => {
|
|
525
882
|
setItems(() => {
|
|
@@ -531,46 +888,45 @@ const RadioButtonGroup = (props) => {
|
|
|
531
888
|
});
|
|
532
889
|
});
|
|
533
890
|
}, [options]);
|
|
534
|
-
return (jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsx("div", { className: buildClassName('flex gap-
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
const RadioSwitch = ({ label, items, selected, onChange }) => {
|
|
538
|
-
const getLabel = (item) => {
|
|
539
|
-
return typeof item === 'string' ? item : item.label;
|
|
540
|
-
};
|
|
541
|
-
const getValue = (item) => {
|
|
542
|
-
return typeof item === 'string' ? item : item.value;
|
|
543
|
-
};
|
|
544
|
-
const getDescription = (item) => {
|
|
545
|
-
return typeof item === 'string' ? '' : item.description || '';
|
|
546
|
-
};
|
|
547
|
-
return (jsx(RadioGroup, { value: selected, onChange: onChange, "aria-label": label, className: "flex items-center bg-white rounded-full shadow-xs p-1 dark:bg-gray-800 gap-1 border border-gray-200 dark:border-gray-700", children: items.map((item) => (jsx(Radio, { value: getValue(item), className: buildClassName('group relative flex cursor-pointer transition', 'text-gray-500 dark:text-gray-300', 'focus:not-data-focus:outline-none data-focus:outline data-focus:outline-white'), children: jsx("div", { className: buildClassName('dark:text-white justify-between px-3 py-1 transition', 'group-data-checked:bg-blue-600 group-data-checked:text-white rounded-full'), children: jsxs("div", { className: "text-sm/6", children: [jsx("p", { className: "font-semibold dark:text-white", children: getLabel(item) }), item.description && (jsx("div", { className: "flex gap-2 text-white/50", children: getDescription(item) }))] }) }) }, getValue(item)))) }));
|
|
891
|
+
return (jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsx("div", { className: buildClassName('flex gap-4', row ? 'flex-row items-center' : 'flex-col items-start'), children: items.map((item) => (jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, value: item.value, checked: value === item.value, onChange: onChange }, item.value))) }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
548
892
|
};
|
|
549
893
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
894
|
+
/**
|
|
895
|
+
* GPU-accelerated Radio Switch with perfect highlight alignment.
|
|
896
|
+
* Supports wrapping, icons, descriptions, and dark mode.
|
|
897
|
+
*/
|
|
898
|
+
const RadioSwitch = ({ label, items, selected, onChange, wrapperClass, switchClass, contentClass, descriptionClass, }) => {
|
|
899
|
+
const containerRef = useRef(null);
|
|
900
|
+
const [style, setStyle] = useState({ left: 0, top: 0, width: 0, height: 0 });
|
|
901
|
+
const getLabel = (i) => (typeof i === 'string' ? i : i.label);
|
|
902
|
+
const getValue = (i) => (typeof i === 'string' ? i : i.value);
|
|
903
|
+
const getDescription = (i) => { var _a; return typeof i === 'string' ? '' : ((_a = i.description) !== null && _a !== void 0 ? _a : ''); };
|
|
904
|
+
/** ---------------------------------------------
|
|
905
|
+
* Update highlight when selected changes
|
|
906
|
+
* ----------------------------------------------*/
|
|
907
|
+
useEffect(() => {
|
|
908
|
+
if (!containerRef.current)
|
|
909
|
+
return;
|
|
910
|
+
const buttons = Array.from(containerRef.current.querySelectorAll('[data-radio-item]'));
|
|
911
|
+
const el = buttons.find((b) => b.dataset.value === selected);
|
|
912
|
+
if (!el)
|
|
913
|
+
return;
|
|
914
|
+
const rect = el.getBoundingClientRect();
|
|
915
|
+
const parentRect = containerRef.current.getBoundingClientRect();
|
|
916
|
+
const left = rect.left - parentRect.left;
|
|
917
|
+
const top = rect.top - parentRect.top;
|
|
918
|
+
setStyle({
|
|
919
|
+
left,
|
|
920
|
+
top,
|
|
921
|
+
width: rect.width,
|
|
922
|
+
height: rect.height,
|
|
923
|
+
});
|
|
924
|
+
}, [selected, items]);
|
|
925
|
+
return (jsxs(RadioGroup$1, { value: selected, onChange: onChange, "aria-label": label, ref: containerRef, className: buildClassName('relative flex flex-wrap gap-1', 'transition-colors duration-200 overflow-hidden', wrapperClass), children: [jsx("div", { className: buildClassName('absolute rounded-md bg-blue-600 dark:bg-blue-500', 'transition-transform transition-size duration-300 ease-[cubic-bezier(.25,.8,.25,1)]', switchClass), style: {
|
|
926
|
+
transform: `translate(${style.left}px, ${style.top}px)`,
|
|
927
|
+
width: style.width,
|
|
928
|
+
height: style.height,
|
|
929
|
+
} }), items.map((item) => (jsx(Radio$1, { value: getValue(item), "data-radio-item": true, "data-value": getValue(item), className: buildClassName('relative z-10 cursor-pointer select-none group', 'px-4 py-2 rounded-md transition-colors', 'text-gray-700 dark:text-gray-300 data-checked:text-white'), children: jsxs("div", { className: buildClassName('flex flex-col items-center justify-center gap-1', contentClass), children: [jsx(BodyText, { className: "font-semibold", children: getLabel(item) }), getDescription(item) && (jsx(BodyText, { className: buildClassName('text-xs opacity-80', descriptionClass), children: getDescription(item) }))] }) }, getValue(item))))] }));
|
|
574
930
|
};
|
|
575
931
|
|
|
576
932
|
/**
|
|
@@ -601,6 +957,104 @@ function isEmpty(value) {
|
|
|
601
957
|
return false; // for numbers, booleans, functions, etc.
|
|
602
958
|
}
|
|
603
959
|
|
|
960
|
+
// export interface RangeInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
961
|
+
// label?: string
|
|
962
|
+
// labelClass?: string
|
|
963
|
+
// labelHint?: React.ReactNode
|
|
964
|
+
// containerClass?: string
|
|
965
|
+
// showValue?: boolean
|
|
966
|
+
// valueSuffix?: string
|
|
967
|
+
// error?: string | null
|
|
968
|
+
// showErrorMessage?: boolean
|
|
969
|
+
// }
|
|
970
|
+
const RangeInput = forwardRef((props, ref) => {
|
|
971
|
+
const { label, labelClass = '', labelHint = null, containerClass = '', className = '', showValue = true, valueSuffix = '', error = null, showErrorMessage = true, id, min = 0, max = 100, value, ...inputProps } = props;
|
|
972
|
+
const hasError = !isEmpty(error);
|
|
973
|
+
return (jsxs("div", { className: buildClassName('group space-y-2', containerClass), children: [label && (jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { htmlFor: id, className: labelClass, children: label }), labelHint] })), jsxs("div", { className: "flex items-center gap-3", children: [jsx("input", { ref: ref, type: "range", id: id, min: min, max: max, value: value, ...inputProps, className: buildClassName('w-full h-2 rounded-lg appearance-none cursor-pointer', 'bg-gray-200 dark:bg-gray-700', 'accent-blue-600', // for WebKit
|
|
974
|
+
'[&::-webkit-slider-thumb]:appearance-none', '[&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4', '[&::-webkit-slider-thumb]:rounded-full', '[&::-webkit-slider-thumb]:bg-blue-600', '[&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4', '[&::-moz-range-thumb]:rounded-full', '[&::-moz-range-thumb]:bg-blue-600', hasError && 'accent-red-600', className) }), showValue && (jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-300 min-w-[3rem] text-right", children: [value, valueSuffix] }))] }), showErrorMessage && hasError && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', className = '', min, max, valueMin, valueMax, onChange, valueSuffix = '', error = null, showErrorMessage = true, }) => {
|
|
978
|
+
const trackRef = useRef(null);
|
|
979
|
+
// Refs for drag logic
|
|
980
|
+
const activeThumbRef = useRef(null);
|
|
981
|
+
// State for UI styling
|
|
982
|
+
const [activeThumbState, setActiveThumbState] = useState(null);
|
|
983
|
+
const hasError = !isEmpty(error);
|
|
984
|
+
const percent = (v) => ((v - min) / (max - min)) * 100;
|
|
985
|
+
// ---------------------------------------------
|
|
986
|
+
// Pointer Move (drag)
|
|
987
|
+
// ---------------------------------------------
|
|
988
|
+
const pointerMoveHandler = (e) => {
|
|
989
|
+
if (!trackRef.current || !activeThumbRef.current)
|
|
990
|
+
return;
|
|
991
|
+
const rect = trackRef.current.getBoundingClientRect();
|
|
992
|
+
const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
|
|
993
|
+
const rawVal = min + (x / rect.width) * (max - min);
|
|
994
|
+
if (activeThumbRef.current === 'min') {
|
|
995
|
+
onChange(Math.min(rawVal, valueMax - 1), valueMax);
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
onChange(valueMin, Math.max(rawVal, valueMin + 1));
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
const pointerUpHandler = () => {
|
|
1002
|
+
activeThumbRef.current = null;
|
|
1003
|
+
setActiveThumbState(null);
|
|
1004
|
+
window.removeEventListener('pointermove', pointerMoveHandler);
|
|
1005
|
+
window.removeEventListener('pointerup', pointerUpHandler);
|
|
1006
|
+
};
|
|
1007
|
+
const startDrag = (thumb) => (e) => {
|
|
1008
|
+
activeThumbRef.current = thumb;
|
|
1009
|
+
setActiveThumbState(thumb);
|
|
1010
|
+
window.addEventListener('pointermove', pointerMoveHandler);
|
|
1011
|
+
window.addEventListener('pointerup', pointerUpHandler);
|
|
1012
|
+
};
|
|
1013
|
+
// ---------------------------------------------
|
|
1014
|
+
// Click Track to move closest thumb
|
|
1015
|
+
// ---------------------------------------------
|
|
1016
|
+
const handleTrackClick = (e) => {
|
|
1017
|
+
if (!trackRef.current)
|
|
1018
|
+
return;
|
|
1019
|
+
const rect = trackRef.current.getBoundingClientRect();
|
|
1020
|
+
const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
|
|
1021
|
+
const rawVal = min + (x / rect.width) * (max - min);
|
|
1022
|
+
const pxMin = (percent(valueMin) / 100) * rect.width;
|
|
1023
|
+
const pxMax = (percent(valueMax) / 100) * rect.width;
|
|
1024
|
+
// If click is left of min thumb → move min thumb
|
|
1025
|
+
if (x <= pxMin) {
|
|
1026
|
+
onChange(rawVal, valueMax);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
// If click is right of max thumb → move max thumb
|
|
1030
|
+
if (x >= pxMax) {
|
|
1031
|
+
onChange(valueMin, rawVal);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
// Else click is between → move closest thumb
|
|
1035
|
+
const distMin = Math.abs(rawVal - valueMin);
|
|
1036
|
+
const distMax = Math.abs(rawVal - valueMax);
|
|
1037
|
+
if (distMin < distMax) {
|
|
1038
|
+
onChange(rawVal, valueMax);
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
onChange(valueMin, rawVal);
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxs("div", { ref: trackRef, onMouseDown: handleTrackClick, className: "relative h-8 select-none w-full", "data-testid": "range-track", children: [jsx("div", { className: "absolute top-1/2 left-0 right-0 h-2 rounded-full bg-gray-200 dark:bg-gray-700 -translate-y-1/2" }), jsx("div", { className: "absolute top-1/2 h-2 bg-blue-600 dark:bg-blue-500 -translate-y-1/2 rounded-full \n transition-all duration-150 ease-out", style: {
|
|
1045
|
+
left: `${percent(valueMin)}%`,
|
|
1046
|
+
width: `${percent(valueMax) - percent(valueMin)}%`,
|
|
1047
|
+
} }), jsx("div", { onPointerDown: startDrag('min'), className: buildClassName('absolute top-1/2 -translate-y-1/2 h-5 w-5 rounded-full', 'bg-white border-2 border-blue-600 shadow-md cursor-pointer touch-none', 'transition-all duration-150 ease-out', // animation
|
|
1048
|
+
activeThumbState === 'min' && 'ring-4 ring-blue-200', className), style: {
|
|
1049
|
+
left: `calc(${percent(valueMin)}% - 10px)`,
|
|
1050
|
+
zIndex: activeThumbState === 'min' ? 50 : 30,
|
|
1051
|
+
}, "data-testid": "thumb-min" }), jsx("div", { onPointerDown: startDrag('max'), className: buildClassName('absolute top-1/2 -translate-y-1/2 h-5 w-5 rounded-full', 'bg-white border-2 border-blue-600 shadow-md cursor-pointer touch-none', 'transition-all duration-150 ease-out', // animation
|
|
1052
|
+
activeThumbState === 'max' && 'ring-4 ring-blue-200', className), style: {
|
|
1053
|
+
left: `calc(${percent(valueMax)}% - 10px)`,
|
|
1054
|
+
zIndex: activeThumbState === 'max' ? 50 : 40,
|
|
1055
|
+
}, "data-testid": "thumb-max" })] }), jsxs("div", { className: "flex justify-between text-sm text-gray-700 dark:text-gray-300", children: [jsxs("span", { children: [Math.round(valueMin), valueSuffix] }), jsxs("span", { children: [Math.round(valueMax), valueSuffix] })] }), showErrorMessage && hasError && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
1056
|
+
};
|
|
1057
|
+
|
|
604
1058
|
const useDebounce = (value, delay) => {
|
|
605
1059
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
606
1060
|
useEffect(() => {
|
|
@@ -614,6 +1068,13 @@ const useDebounce = (value, delay) => {
|
|
|
614
1068
|
}, [value, delay]);
|
|
615
1069
|
return debouncedValue;
|
|
616
1070
|
};
|
|
1071
|
+
const useDebounceFn = (fn, ms) => {
|
|
1072
|
+
const timeout = useRef(null);
|
|
1073
|
+
return useCallback((...args) => {
|
|
1074
|
+
clearTimeout(timeout.current);
|
|
1075
|
+
timeout.current = setTimeout(() => fn(...args), ms);
|
|
1076
|
+
}, [fn, ms]);
|
|
1077
|
+
};
|
|
617
1078
|
|
|
618
1079
|
var _path$2;
|
|
619
1080
|
function _extends$2() { return _extends$2 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$2.apply(null, arguments); }
|
|
@@ -676,197 +1137,955 @@ var SvgCirclePlus = function SvgCirclePlus(props) {
|
|
|
676
1137
|
})));
|
|
677
1138
|
};
|
|
678
1139
|
|
|
679
|
-
const constructInputClass = (disabled) =>
|
|
680
|
-
const SelectBox = (props) => {
|
|
1140
|
+
const constructInputClass = (disabled) => buildInputClass('', { disabled });
|
|
1141
|
+
const SelectBox = forwardRef((props, forwardedRef) => {
|
|
681
1142
|
const { labelKey = 'label', valueKey = 'value', name, id = name, options, selected, label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, immediate = true, showErrorMessage = true, error = null, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, } = props;
|
|
682
1143
|
const hasError = Boolean(error);
|
|
683
1144
|
const [query, setQuery] = useState('');
|
|
684
1145
|
const debouncedQuery = useDebounce(query, 500);
|
|
685
1146
|
const [optionsLoading, setOptionsLoading] = useState(false);
|
|
686
|
-
const shouldShowTypeToSearch = async && !
|
|
687
|
-
const inputClass = constructInputClass(disabled);
|
|
1147
|
+
const shouldShowTypeToSearch = async && !query && !optionsLoading && options.length === 0;
|
|
1148
|
+
const inputClass = useMemo(() => constructInputClass(disabled), [disabled]);
|
|
1149
|
+
const chevronIconClass = buildClassName('size-4', 'stroke-gray-500 group-data-[hover]:stroke-gray-800', 'dark:stroke-gray-300 dark:group-data-[hover]:stroke-gray-100', disabled && 'dark:stroke-gray-600 stroke-gray-400');
|
|
688
1150
|
const inputRef = useRef(null);
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
return
|
|
1151
|
+
const containerRef = useRef(null);
|
|
1152
|
+
// Expose the input element to parent
|
|
1153
|
+
useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
|
|
1154
|
+
const filteredOptions = useMemo(() => {
|
|
1155
|
+
if (query === '' || async)
|
|
1156
|
+
return options;
|
|
1157
|
+
return options.filter((opt) => {
|
|
1158
|
+
const label = typeof opt === 'string' ? opt : opt[labelKey];
|
|
1159
|
+
return label === null || label === void 0 ? void 0 : label.toLowerCase().includes(query.toLowerCase());
|
|
695
1160
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
1161
|
+
}, [options, query, async, labelKey]);
|
|
1162
|
+
const getDisplayValue = useCallback((opt) => {
|
|
1163
|
+
return typeof opt === 'string' ? opt : opt[labelKey];
|
|
1164
|
+
}, [labelKey]);
|
|
1165
|
+
const getValue = useCallback((opt) => {
|
|
1166
|
+
return typeof opt === 'string' ? opt : opt[valueKey];
|
|
1167
|
+
}, [valueKey]);
|
|
702
1168
|
const removeSelection = (idx) => {
|
|
703
|
-
|
|
1169
|
+
var _a;
|
|
1170
|
+
if (!isArray(selected))
|
|
704
1171
|
return;
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1172
|
+
const next = [...selected];
|
|
1173
|
+
next.splice(idx, 1);
|
|
1174
|
+
onChange(next);
|
|
1175
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
709
1176
|
};
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
1177
|
+
// A strongly-typed internal handler (what you expect callers to receive)
|
|
1178
|
+
const handleOnChangeInternal = useCallback((value) => {
|
|
1179
|
+
// normal empty value
|
|
1180
|
+
if (value === null) {
|
|
1181
|
+
onChange(multiple ? [] : null);
|
|
1182
|
+
setQuery('');
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
// enforce maxSelection
|
|
1186
|
+
if (multiple && Array.isArray(value) && maxSelection && value.length > maxSelection) {
|
|
1187
|
+
// ignore the change
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
onChange(value);
|
|
1191
|
+
setQuery('');
|
|
1192
|
+
}, [onChange, multiple, maxSelection]);
|
|
1193
|
+
/**
|
|
1194
|
+
* Headless UI typings are overly broad (they include null[] and EnsureArray<T> etc).
|
|
1195
|
+
* We therefore pass a wrapper to the Combobox with a cast (only at the call site),
|
|
1196
|
+
* while keeping our internal logic strongly typed.
|
|
1197
|
+
*/
|
|
1198
|
+
const comboboxOnChangeWrapper = useCallback((raw) => {
|
|
1199
|
+
// normalize actual runtime shapes Headless UI uses: T | T[] | null
|
|
1200
|
+
if (raw === null || raw === undefined) {
|
|
1201
|
+
handleOnChangeInternal(null);
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
if (Array.isArray(raw)) {
|
|
1205
|
+
// sometimes union typing suggests arrays of nulls — guard it
|
|
1206
|
+
const allNulls = raw.length > 0 && raw.every((v) => v === null);
|
|
1207
|
+
if (allNulls) {
|
|
1208
|
+
handleOnChangeInternal([]);
|
|
714
1209
|
return;
|
|
715
1210
|
}
|
|
1211
|
+
// cast array to T[]
|
|
1212
|
+
handleOnChangeInternal(raw);
|
|
1213
|
+
return;
|
|
716
1214
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
};
|
|
1215
|
+
// single value
|
|
1216
|
+
handleOnChangeInternal(raw);
|
|
1217
|
+
}, [handleOnChangeInternal]);
|
|
1218
|
+
// support create (allowAdd) as a real ComboboxOption
|
|
1219
|
+
const handleAddAndSelect = useCallback(async (valueToAdd) => {
|
|
1220
|
+
try {
|
|
1221
|
+
const result = await onAdd(valueToAdd);
|
|
1222
|
+
// if the parent returned a new option (string or object), select it
|
|
1223
|
+
if (result) {
|
|
1224
|
+
// If parent returned raw string or object, call onChange with that
|
|
1225
|
+
handleOnChangeInternal(result);
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
// if parent didn't return anything, select the raw string value
|
|
1229
|
+
handleOnChangeInternal(valueToAdd);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
catch (err) {
|
|
1233
|
+
// eslint-disable-next-line no-console
|
|
1234
|
+
console.error('onAdd handler error', err);
|
|
1235
|
+
}
|
|
1236
|
+
}, [onAdd, handleOnChangeInternal]);
|
|
720
1237
|
const handleSearch = async (searchString) => {
|
|
721
1238
|
var _a;
|
|
722
1239
|
try {
|
|
723
1240
|
setOptionsLoading(true);
|
|
724
1241
|
await ((_a = props.onSearch) === null || _a === void 0 ? void 0 : _a.call(props, searchString));
|
|
725
1242
|
}
|
|
726
|
-
catch (
|
|
727
|
-
console.error('Error loading options:',
|
|
1243
|
+
catch (err) {
|
|
1244
|
+
console.error('Error loading options:', err);
|
|
728
1245
|
}
|
|
729
1246
|
finally {
|
|
730
1247
|
setOptionsLoading(false);
|
|
731
1248
|
}
|
|
732
1249
|
};
|
|
733
1250
|
useEffect(() => {
|
|
734
|
-
if (async && debouncedQuery)
|
|
1251
|
+
if (async && debouncedQuery)
|
|
735
1252
|
handleSearch(debouncedQuery);
|
|
736
|
-
}
|
|
737
1253
|
}, [debouncedQuery, async]);
|
|
738
|
-
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange:
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
};
|
|
749
|
-
const TABLE_LAYOUT_CLASSES = {
|
|
750
|
-
fixed: 'table-fixed',
|
|
751
|
-
auto: 'table-auto',
|
|
752
|
-
};
|
|
753
|
-
const TableRowLoader = ({ colSize, rowSize }) => (jsx(Fragment$1, { children: [...Array(rowSize)].map((_, rowIndex) => (jsx(TableRow, { className: "animate-pulse", children: [...Array(colSize)].map((_, colIndex) => (jsx(TableCell, { children: jsx("div", { className: "flex items-center h-6", children: jsx("div", { className: "h-4 bg-gray-300 shimmer dark:bg-gray-700 rounded w-full" }) }) }, colIndex))) }, rowIndex))) }));
|
|
754
|
-
const Table = ({ children, layout = 'auto', className = '' }) => (jsx("table", { className: buildClassName('border-collapse table-fixed w-full divide-y divide-gray-200', TABLE_LAYOUT_CLASSES[layout], className), children: children }));
|
|
755
|
-
const TabelHead = ({ children, className = '', onSort }) => (jsx("thead", { className: buildClassName('text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400', className), children: jsx(TableRow, { className: "dark:hover:bg-gray-700", hoverable: false, children: React__default.Children.map(children, (child) => {
|
|
756
|
-
// Check if child is a valid React element
|
|
757
|
-
if (React__default.isValidElement(child)) {
|
|
758
|
-
// Type the child explicitly to ReactElement so TypeScript knows it supports onClick
|
|
759
|
-
return React__default.cloneElement(child, { onClick: onSort });
|
|
760
|
-
}
|
|
761
|
-
// Return child as-is if it's not a React element
|
|
762
|
-
return child;
|
|
763
|
-
}) }) }));
|
|
764
|
-
const TabelBody = ({ children, loading, colSize, rowSize = 5, className = '', }) => (jsx("tbody", { className: buildClassName('text-sm text-gray-700 dark:text-white divide-y divide-gray-200 dark:divide-gray-700', className), children: loading ? (jsx(TableRowLoader, { colSize: colSize, rowSize: rowSize })) : (jsx(Fragment$1, { children: React__default.Children.map(children, (child) => {
|
|
765
|
-
// Check if child is a valid React element
|
|
766
|
-
if (React__default.isValidElement(child)) {
|
|
767
|
-
// Type the child explicitly to ReactElement so TypeScript knows it supports onClick
|
|
768
|
-
return React__default.cloneElement(child);
|
|
769
|
-
}
|
|
770
|
-
// Return child as-is if it's not a React element
|
|
771
|
-
return child;
|
|
772
|
-
}) })) }));
|
|
773
|
-
const TabelFoot = ({ children }) => (jsx("tfoot", { children: React__default.Children.map(children, (child) => {
|
|
774
|
-
// Check if child is a valid React element
|
|
775
|
-
if (React__default.isValidElement(child)) {
|
|
776
|
-
// Type the child explicitly to ReactElement so TypeScript knows it supports onClick
|
|
777
|
-
return React__default.cloneElement(child);
|
|
778
|
-
}
|
|
779
|
-
// Return child as-is if it's not a React element
|
|
780
|
-
return child;
|
|
781
|
-
}) }));
|
|
782
|
-
const TableRow = ({ children, hoverable = true, className, ...props }) => (jsx("tr", { className: buildClassName('last:border-b border-gray-200 dark:border-gray-700 group h-full', className), ...props, children: children }));
|
|
783
|
-
const TableCell = ({ children, align = 'left', className = '', ...props }) => (jsx("td", { className: buildClassName('first:pl-4 last:pr-4 px-3 py-3 bg-white dark:bg-gray-800 group-hover:bg-gray-50 group-hover:dark:bg-gray-600 whitespace-normal', className, TEXT_ALIGN_CLASSES[align]), ...props, children: jsx("div", { className: "antialiased leading-normal", children: children }) }));
|
|
784
|
-
const TableHeaderCell = ({ children, align = 'left', className = '', ...props }) => (jsx("th", { className: buildClassName('first:pl-4 last:pr-4 px-3 py-3 tracking-wider whitespace-nowrap', 'text-gray-800 dark:text-gray-400', TEXT_ALIGN_CLASSES[align], className), ...props, children: jsx(BodyText, { as: "div", className: "block antialiased leading-none", children: children }) }));
|
|
785
|
-
const EmptyTableRow = ({ children, colSpan = 1 }) => (jsx("tr", { children: jsx(TableCell, { colSpan: colSpan, align: "center", children: jsx("div", { className: "p-8 space-y-3", children: children }) }) }));
|
|
786
|
-
|
|
787
|
-
const Recent = () => jsx("div", { children: "Recent" });
|
|
788
|
-
const Popular = () => jsx("div", { children: "Popular" });
|
|
789
|
-
const Trending = () => jsx("div", { children: "Trending" });
|
|
790
|
-
const categories = [
|
|
791
|
-
{
|
|
792
|
-
name: 'Recent',
|
|
793
|
-
component: Recent,
|
|
794
|
-
props: {},
|
|
795
|
-
},
|
|
796
|
-
{
|
|
797
|
-
name: 'Popular',
|
|
798
|
-
component: Popular,
|
|
799
|
-
props: {},
|
|
800
|
-
},
|
|
801
|
-
{
|
|
802
|
-
name: 'Trending',
|
|
803
|
-
component: Trending,
|
|
804
|
-
props: {},
|
|
805
|
-
},
|
|
806
|
-
];
|
|
807
|
-
const Tabs = () => {
|
|
808
|
-
return (jsx("div", { className: "flex w-full pt-4", children: jsx("div", { className: "w-full max-w-md", children: jsxs(TabGroup, { children: [jsx(TabList, { className: "flex gap-4", children: categories.map(({ name }) => (jsx(Tab, { className: buildClassName('rounded-full py-1 px-3 text-sm/6 font-semibold dark:text-white border border-transparent', 'data-[selected]:bg-blue-100 dark:data-[selected]:bg-white/10 data-[selected]:text-blue-500', 'data-[hover]:bg-blue-50 dark:data-[hover]:bg-white/5', 'data-[selected]:data-[hover]:bg-blue-100 dark:data-[selected]:data-[hover]:bg-white/10', 'focus:outline-none data-[focus]:outline-1 data-[focus]:outline-blue-800 dark:data-[focus]:outline-white', 'data-[selected]:border-blue-500'), children: name }, name))) }), jsx(TabPanels, { className: "mt-3", children: categories.map(({ name, component: Component, props }) => (jsx(TabPanel, { className: "rounded-xl bg-white/5 p-3", children: jsx(Component, { ...props }) }, name))) })] }) }) }));
|
|
809
|
-
};
|
|
1254
|
+
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxs("div", { className: buildClassName(inputClass, 'flex flex-wrap gap-2 peer relative'), ref: forwardedRef, children: [isArray(selected) &&
|
|
1255
|
+
(selected === null || selected === void 0 ? void 0 : selected.length) > 0 &&
|
|
1256
|
+
selected.map((option, idx) => (jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsx(ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
|
|
1257
|
+
if (multiple && e.key === 'Backspace' && !query) {
|
|
1258
|
+
if (Array.isArray(selected) && selected.length > 0) {
|
|
1259
|
+
removeSelection(selected.length - 1);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}, "data-testid": "combobox-input" }), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxs("div", { className: "relative", ref: forwardedRef, children: [jsx(ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: inputClass, disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsx(Button, { size: "xs", iconOnly: true, theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4 stroke-gray-500" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsx(ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: optionsLoading ? (jsx("div", { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx("div", { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [filteredOptions.map((option) => (jsx(ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: ["No results found for ", jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsx(ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
1263
|
+
});
|
|
810
1264
|
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
secondary: 'bg-gray-100 text-gray-600 border border-gray-200',
|
|
814
|
-
success: 'bg-green-100 text-green-600 border border-green-200',
|
|
815
|
-
warning: 'bg-orange-100 text-orange-600 border border-orange-200',
|
|
816
|
-
error: 'bg-red-100 text-red-600 border border-red-200',
|
|
817
|
-
};
|
|
818
|
-
const Tag = (props) => {
|
|
819
|
-
const { as: Element = 'span', text, className = '', onClose, variant = 'secondary', ...elemProps } = props;
|
|
820
|
-
return (jsxs(Element, { ...elemProps, className: buildClassName(className, 'px-1.5 py-0.5 text-sm/6 font-normal', variantMap[variant], 'rounded'), children: [jsx("span", { children: text }), onClose && jsx(SvgCircleX, { className: "size-3", onClick: () => onClose(), role: "button" })] }));
|
|
1265
|
+
const Tabs = ({ tabs, defaultIndex = 0, variant = 'underline', className = '', listClass = '', panelClass = '', }) => {
|
|
1266
|
+
return (jsxs(TabGroup, { defaultIndex: defaultIndex, className: className, children: [jsx(TabList, { className: buildClassName('flex gap-2', listClass), children: tabs.map((tab, idx) => (jsx(Tab, { as: Fragment$1, children: ({ selected }) => (jsx("button", { className: buildTabClass({ selected, variant }), children: tab.label })) }, idx))) }), jsx(TabPanels, { className: buildClassName('mt-3', panelClass), children: tabs.map((tab, idx) => (jsx(TabPanel, { className: "rounded-lg bg-white dark:bg-gray-800 p-4 shadow-sm", children: tab.content }, idx))) })] }));
|
|
821
1267
|
};
|
|
1268
|
+
/**
|
|
1269
|
+
* Build tab styles based on variant + active state
|
|
1270
|
+
*/
|
|
1271
|
+
function buildTabClass({ selected, variant, }) {
|
|
1272
|
+
switch (variant) {
|
|
1273
|
+
/**
|
|
1274
|
+
* UNDERLINE (Default)
|
|
1275
|
+
*/
|
|
1276
|
+
case 'underline':
|
|
1277
|
+
return buildClassName('px-3 py-1.5 text-sm font-medium transition relative', 'text-gray-600 dark:text-gray-300', 'hover:text-gray-900 dark:hover:text-white', selected &&
|
|
1278
|
+
'text-gray-900 dark:text-white after:absolute after:left-0 after:right-0 after:-bottom-1 after:h-0.5 after:bg-blue-600');
|
|
1279
|
+
/**
|
|
1280
|
+
* SEGMENTED
|
|
1281
|
+
*/
|
|
1282
|
+
case 'segmented':
|
|
1283
|
+
return buildClassName('px-3 py-1.5 text-sm font-medium rounded-lg transition', selected
|
|
1284
|
+
? 'bg-blue-600 text-white'
|
|
1285
|
+
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700');
|
|
1286
|
+
/**
|
|
1287
|
+
* SOLID
|
|
1288
|
+
*/
|
|
1289
|
+
case 'solid':
|
|
1290
|
+
return buildClassName('px-3 py-1.5 text-sm font-medium rounded-md transition', selected
|
|
1291
|
+
? 'bg-gray-900 dark:bg-gray-700 text-white'
|
|
1292
|
+
: 'bg-gray-200 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-700');
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
822
1295
|
|
|
823
1296
|
const Textarea = forwardRef((props, ref) => {
|
|
824
1297
|
const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, ...inputProps } = props;
|
|
825
1298
|
const hasError = !isEmpty(error);
|
|
826
|
-
const hasRightGroup = rightGroup != null &&
|
|
827
|
-
const hasLeftGroup = leftGroup != null &&
|
|
828
|
-
return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsx("textarea", { ref: ref, ...inputProps, name: name, id: id, className: buildClassName('transition-all', 'block w-full rounded-lg bg-white px-3.5 py-2 text-base sm:text-sm/6', 'text-gray-900 outline dark:outline outline-1 -outline-offset-1', 'outline-gray-300 dark:outline-gray-600', 'focus:outline-blue-600 dark:focus:outline-blue-600 focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-500 placeholder:text-gray-400 dark:text-white', 'invalid:group-[.has-error]:outline-red-500', 'disabled:pointer-events-none disabled:bg-gray-100', 'dark:disabled:bg-gray-500', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center", children: rightGroup }))] }), showErrorMessage && error && (jsx("
|
|
1299
|
+
const hasRightGroup = rightGroup != null && rightGroup !== undefined;
|
|
1300
|
+
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
1301
|
+
return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsx("textarea", { ref: ref, autoComplete: "off", ...inputProps, name: name, id: id, className: buildClassName('transition-all', 'block w-full rounded-lg bg-white px-3.5 py-2 text-base sm:text-sm/6', 'text-gray-900 outline dark:outline outline-1 -outline-offset-1', 'outline-gray-300 dark:outline-gray-600', 'focus:outline-blue-600 dark:focus:outline-blue-600 focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-500 placeholder:text-gray-400 dark:text-white', 'invalid:group-[.has-error]:outline-red-500', 'disabled:pointer-events-none disabled:bg-gray-100', 'dark:disabled:bg-gray-500', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center", children: rightGroup }))] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
829
1302
|
});
|
|
830
1303
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1304
|
+
/**
|
|
1305
|
+
* Icons for each toast type
|
|
1306
|
+
*/
|
|
1307
|
+
const iconMap = {
|
|
1308
|
+
success: jsx(SvgCircleCheck, { className: "size-5 text-green-600 dark:text-green-400" }),
|
|
1309
|
+
error: jsx(SvgCircleAlert, { className: "size-5 text-red-600 dark:text-red-400" }),
|
|
1310
|
+
info: jsx(SvgInfo, { className: "size-5 text-blue-600 dark:text-blue-400" }),
|
|
1311
|
+
warning: jsx(SvgTriangleAlert, { className: "size-5 text-yellow-600 dark:text-yellow-400" }),
|
|
1312
|
+
};
|
|
1313
|
+
/**
|
|
1314
|
+
* Subtle icon + text color adjustments
|
|
1315
|
+
*/
|
|
1316
|
+
const textColorMap = {
|
|
1317
|
+
success: 'text-green-900 dark:text-green-200',
|
|
1318
|
+
error: 'text-red-900 dark:text-red-200',
|
|
1319
|
+
info: 'text-blue-900 dark:text-blue-200',
|
|
1320
|
+
warning: 'text-yellow-900 dark:text-yellow-200',
|
|
836
1321
|
};
|
|
837
|
-
|
|
838
|
-
|
|
1322
|
+
/**
|
|
1323
|
+
* Pleasant colored *top border only*
|
|
1324
|
+
* No background color — soft neutral surface
|
|
1325
|
+
*/
|
|
1326
|
+
const borderStyle = {
|
|
1327
|
+
success: 'border-t-4 border-green-600 dark:border-green-400',
|
|
1328
|
+
error: 'border-t-4 border-red-600 dark:border-red-400',
|
|
1329
|
+
info: 'border-t-4 border-blue-600 dark:border-blue-400',
|
|
1330
|
+
warning: 'border-t-4 border-yellow-600 dark:border-yellow-400',
|
|
1331
|
+
};
|
|
1332
|
+
const Toast = ({ message, type, onClose, className, id, isTop = true, }) => {
|
|
1333
|
+
const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95';
|
|
1334
|
+
const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95';
|
|
1335
|
+
return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxs("div", { role: "status", className: buildClassName('flex items-center gap-3 rounded-lg px-4 py-3 shadow-lg min-w-[260px] max-w-sm', 'bg-white dark:bg-gray-900 dark:border-gray-700', borderStyle[type], className), id: id.toString(), children: [jsx(TextContent, { className: buildClassName(textColorMap[type]), children: iconMap[type] }), jsx(TextContent, { className: "flex-1 text-sm font-medium", children: message }), jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: "opacity-60 hover:opacity-100 transition", iconOnly: true, size: "xs", variant: "plain", theme: "secondary", noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4" }) })] }) }));
|
|
839
1336
|
};
|
|
840
1337
|
|
|
841
1338
|
const ToastContext = createContext(undefined);
|
|
1339
|
+
let toastCounter = 0;
|
|
842
1340
|
const useToast = () => {
|
|
843
1341
|
const context = useContext(ToastContext);
|
|
844
1342
|
if (!context)
|
|
845
1343
|
throw new Error('useToast must be used within ToastProvider');
|
|
846
1344
|
return context;
|
|
847
1345
|
};
|
|
848
|
-
|
|
1346
|
+
// Maximum visible toasts at once
|
|
1347
|
+
const MAX_TOASTS = 5;
|
|
1348
|
+
const placementClass = {
|
|
1349
|
+
'top-right': 'top-10 right-4 items-end',
|
|
1350
|
+
'top-left': 'top-10 left-4 items-start',
|
|
1351
|
+
'top-center': 'top-10 left-1/2 -translate-x-1/2 items-center',
|
|
1352
|
+
'bottom-right': 'bottom-10 right-4 items-end',
|
|
1353
|
+
'bottom-left': 'bottom-10 left-4 items-start',
|
|
1354
|
+
'bottom-center': 'bottom-10 left-1/2 -translate-x-1/2 items-center',
|
|
1355
|
+
};
|
|
1356
|
+
const ToastProvider = ({ children, placement = 'top-center' }) => {
|
|
849
1357
|
const [toasts, setToasts] = useState([]);
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
const
|
|
1358
|
+
const timeoutRefs = useRef(new Map());
|
|
1359
|
+
const clearTimer = (id) => {
|
|
1360
|
+
const timeout = timeoutRefs.current.get(id);
|
|
1361
|
+
if (timeout) {
|
|
1362
|
+
clearTimeout(timeout);
|
|
1363
|
+
timeoutRefs.current.delete(id);
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
const scheduleClose = (toast) => {
|
|
1367
|
+
if (!toast.autoClose)
|
|
1368
|
+
return;
|
|
1369
|
+
clearTimer(toast.id);
|
|
1370
|
+
const timeout = setTimeout(() => {
|
|
1371
|
+
closeToast(toast.id);
|
|
1372
|
+
}, toast.duration);
|
|
1373
|
+
timeoutRefs.current.set(toast.id, timeout);
|
|
1374
|
+
};
|
|
1375
|
+
const showToast = useCallback((message, options) => {
|
|
1376
|
+
var _a, _b, _c, _d;
|
|
1377
|
+
const id = ++toastCounter;
|
|
853
1378
|
const newToast = {
|
|
854
1379
|
id,
|
|
855
1380
|
message,
|
|
856
1381
|
type: (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'info',
|
|
857
1382
|
duration: (_b = options === null || options === void 0 ? void 0 : options.duration) !== null && _b !== void 0 ? _b : 3000,
|
|
858
1383
|
autoClose: (_c = options === null || options === void 0 ? void 0 : options.autoClose) !== null && _c !== void 0 ? _c : true,
|
|
1384
|
+
pauseOnHover: (_d = options === null || options === void 0 ? void 0 : options.pauseOnHover) !== null && _d !== void 0 ? _d : true,
|
|
859
1385
|
};
|
|
860
|
-
setToasts((prev) =>
|
|
861
|
-
|
|
862
|
-
|
|
1386
|
+
setToasts((prev) => {
|
|
1387
|
+
const updated = [...prev, newToast];
|
|
1388
|
+
if (updated.length > MAX_TOASTS)
|
|
1389
|
+
updated.shift();
|
|
1390
|
+
return updated;
|
|
1391
|
+
});
|
|
1392
|
+
scheduleClose(newToast);
|
|
1393
|
+
}, []);
|
|
1394
|
+
const closeToast = useCallback((id) => {
|
|
1395
|
+
clearTimer(id);
|
|
1396
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1397
|
+
}, []);
|
|
1398
|
+
return (jsxs(ToastContext.Provider, { value: { showToast, closeToast }, children: [children, jsx("div", { className: buildClassName('fixed z-50 flex flex-col gap-3 pointer-events-none', placementClass[placement]), children: toasts.map((toast, index) => (jsx("div", { className: "pointer-events-auto", onMouseEnter: () => toast.pauseOnHover && clearTimer(toast.id), onMouseLeave: () => toast.pauseOnHover && scheduleClose(toast), children: jsx(Toast, { ...toast, isTop: placement.startsWith('top'), onClose: () => closeToast(toast.id) }) }, toast.id))) })] }));
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
const q = typeof window < "u" ? useLayoutEffect : useEffect;
|
|
1402
|
+
function oe(e) {
|
|
1403
|
+
if (e !== void 0)
|
|
1404
|
+
switch (typeof e) {
|
|
1405
|
+
case "number":
|
|
1406
|
+
return e;
|
|
1407
|
+
case "string": {
|
|
1408
|
+
if (e.endsWith("px"))
|
|
1409
|
+
return parseFloat(e);
|
|
1410
|
+
break;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
function Ie({
|
|
1415
|
+
box: e,
|
|
1416
|
+
defaultHeight: t,
|
|
1417
|
+
defaultWidth: s,
|
|
1418
|
+
disabled: r,
|
|
1419
|
+
element: n,
|
|
1420
|
+
mode: o,
|
|
1421
|
+
style: i
|
|
1422
|
+
}) {
|
|
1423
|
+
const { styleHeight: f, styleWidth: l } = useMemo(
|
|
1424
|
+
() => ({
|
|
1425
|
+
styleHeight: oe(i?.height),
|
|
1426
|
+
styleWidth: oe(i?.width)
|
|
1427
|
+
}),
|
|
1428
|
+
[i?.height, i?.width]
|
|
1429
|
+
), [c, d] = useState({
|
|
1430
|
+
height: t,
|
|
1431
|
+
width: s
|
|
1432
|
+
}), a = r || f !== void 0 || o === "only-width" || f !== void 0 && l !== void 0;
|
|
1433
|
+
return q(() => {
|
|
1434
|
+
if (n === null || a)
|
|
1435
|
+
return;
|
|
1436
|
+
const u = new ResizeObserver((I) => {
|
|
1437
|
+
for (const m of I) {
|
|
1438
|
+
const { contentRect: b, target: g } = m;
|
|
1439
|
+
n === g && d((w) => w.height === b.height && w.width === b.width ? w : {
|
|
1440
|
+
height: b.height,
|
|
1441
|
+
width: b.width
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
return u.observe(n, { box: e }), () => {
|
|
1446
|
+
u?.unobserve(n);
|
|
1447
|
+
};
|
|
1448
|
+
}, [e, a, n, f, l]), useMemo(
|
|
1449
|
+
() => ({
|
|
1450
|
+
height: f ?? c.height,
|
|
1451
|
+
width: l ?? c.width
|
|
1452
|
+
}),
|
|
1453
|
+
[c, f, l]
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
function ae(e) {
|
|
1457
|
+
const t = useRef(() => {
|
|
1458
|
+
throw new Error("Cannot call during render.");
|
|
1459
|
+
});
|
|
1460
|
+
return q(() => {
|
|
1461
|
+
t.current = e;
|
|
1462
|
+
}, [e]), useCallback((s) => t.current?.(s), [t]);
|
|
1463
|
+
}
|
|
1464
|
+
function Z({
|
|
1465
|
+
containerElement: e,
|
|
1466
|
+
direction: t,
|
|
1467
|
+
isRtl: s,
|
|
1468
|
+
scrollOffset: r
|
|
1469
|
+
}) {
|
|
1470
|
+
return r;
|
|
1471
|
+
}
|
|
1472
|
+
function $(e, t = "Assertion error") {
|
|
1473
|
+
if (!e)
|
|
1474
|
+
throw console.error(t), Error(t);
|
|
1475
|
+
}
|
|
1476
|
+
function Y(e, t) {
|
|
1477
|
+
if (e === t)
|
|
1478
|
+
return true;
|
|
1479
|
+
if (!!e != !!t || ($(e !== void 0), $(t !== void 0), Object.keys(e).length !== Object.keys(t).length))
|
|
1480
|
+
return false;
|
|
1481
|
+
for (const s in e)
|
|
1482
|
+
if (!Object.is(t[s], e[s]))
|
|
1483
|
+
return false;
|
|
1484
|
+
return true;
|
|
1485
|
+
}
|
|
1486
|
+
function fe({
|
|
1487
|
+
cachedBounds: e,
|
|
1488
|
+
itemCount: t,
|
|
1489
|
+
itemSize: s
|
|
1490
|
+
}) {
|
|
1491
|
+
if (t === 0)
|
|
1492
|
+
return 0;
|
|
1493
|
+
if (typeof s == "number")
|
|
1494
|
+
return t * s;
|
|
1495
|
+
{
|
|
1496
|
+
const r = e.get(
|
|
1497
|
+
e.size === 0 ? 0 : e.size - 1
|
|
1498
|
+
);
|
|
1499
|
+
$(r !== void 0, "Unexpected bounds cache miss");
|
|
1500
|
+
const n = (r.scrollOffset + r.size) / e.size;
|
|
1501
|
+
return t * n;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
function we({
|
|
1505
|
+
align: e,
|
|
1506
|
+
cachedBounds: t,
|
|
1507
|
+
index: s,
|
|
1508
|
+
itemCount: r,
|
|
1509
|
+
itemSize: n,
|
|
1510
|
+
containerScrollOffset: o,
|
|
1511
|
+
containerSize: i
|
|
1512
|
+
}) {
|
|
1513
|
+
if (s < 0 || s >= r)
|
|
1514
|
+
throw RangeError(`Invalid index specified: ${s}`, {
|
|
1515
|
+
cause: `Index ${s} is not within the range of 0 - ${r - 1}`
|
|
1516
|
+
});
|
|
1517
|
+
const f = fe({
|
|
1518
|
+
cachedBounds: t,
|
|
1519
|
+
itemCount: r,
|
|
1520
|
+
itemSize: n
|
|
1521
|
+
}), l = t.get(s), c = Math.max(
|
|
1522
|
+
0,
|
|
1523
|
+
Math.min(f - i, l.scrollOffset)
|
|
1524
|
+
), d = Math.max(
|
|
1525
|
+
0,
|
|
1526
|
+
l.scrollOffset - i + l.size
|
|
1527
|
+
);
|
|
1528
|
+
switch (e === "smart" && (o >= d && o <= c ? e = "auto" : e = "center"), e) {
|
|
1529
|
+
case "start":
|
|
1530
|
+
return c;
|
|
1531
|
+
case "end":
|
|
1532
|
+
return d;
|
|
1533
|
+
case "center":
|
|
1534
|
+
return l.scrollOffset <= i / 2 ? 0 : l.scrollOffset + l.size / 2 >= f - i / 2 ? f - i : l.scrollOffset + l.size / 2 - i / 2;
|
|
1535
|
+
case "auto":
|
|
1536
|
+
default:
|
|
1537
|
+
return o >= d && o <= c ? o : o < d ? d : c;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
function ie({
|
|
1541
|
+
cachedBounds: e,
|
|
1542
|
+
containerScrollOffset: t,
|
|
1543
|
+
containerSize: s,
|
|
1544
|
+
itemCount: r,
|
|
1545
|
+
overscanCount: n
|
|
1546
|
+
}) {
|
|
1547
|
+
const o = r - 1;
|
|
1548
|
+
let i = 0, f = -1, l = 0, c = -1, d = 0;
|
|
1549
|
+
for (; d < o; ) {
|
|
1550
|
+
const a = e.get(d);
|
|
1551
|
+
if (a.scrollOffset + a.size > t)
|
|
1552
|
+
break;
|
|
1553
|
+
d++;
|
|
1554
|
+
}
|
|
1555
|
+
for (i = d, l = Math.max(0, i - n); d < o; ) {
|
|
1556
|
+
const a = e.get(d);
|
|
1557
|
+
if (a.scrollOffset + a.size >= t + s)
|
|
1558
|
+
break;
|
|
1559
|
+
d++;
|
|
1560
|
+
}
|
|
1561
|
+
return f = Math.min(o, d), c = Math.min(r - 1, f + n), i < 0 && (i = 0, f = -1, l = 0, c = -1), {
|
|
1562
|
+
startIndexVisible: i,
|
|
1563
|
+
stopIndexVisible: f,
|
|
1564
|
+
startIndexOverscan: l,
|
|
1565
|
+
stopIndexOverscan: c
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
function me({
|
|
1569
|
+
itemCount: e,
|
|
1570
|
+
itemProps: t,
|
|
1571
|
+
itemSize: s
|
|
1572
|
+
}) {
|
|
1573
|
+
const r = /* @__PURE__ */ new Map();
|
|
1574
|
+
return {
|
|
1575
|
+
get(n) {
|
|
1576
|
+
for ($(n < e, `Invalid index ${n}`); r.size - 1 < n; ) {
|
|
1577
|
+
const i = r.size;
|
|
1578
|
+
let f;
|
|
1579
|
+
switch (typeof s) {
|
|
1580
|
+
case "function": {
|
|
1581
|
+
f = s(i, t);
|
|
1582
|
+
break;
|
|
1583
|
+
}
|
|
1584
|
+
case "number": {
|
|
1585
|
+
f = s;
|
|
1586
|
+
break;
|
|
1587
|
+
}
|
|
863
1588
|
}
|
|
1589
|
+
if (i === 0)
|
|
1590
|
+
r.set(i, {
|
|
1591
|
+
size: f,
|
|
1592
|
+
scrollOffset: 0
|
|
1593
|
+
});
|
|
1594
|
+
else {
|
|
1595
|
+
const l = r.get(i - 1);
|
|
1596
|
+
$(
|
|
1597
|
+
l !== void 0,
|
|
1598
|
+
`Unexpected bounds cache miss for index ${n}`
|
|
1599
|
+
), r.set(i, {
|
|
1600
|
+
scrollOffset: l.scrollOffset + l.size,
|
|
1601
|
+
size: f
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
const o = r.get(n);
|
|
1606
|
+
return $(
|
|
1607
|
+
o !== void 0,
|
|
1608
|
+
`Unexpected bounds cache miss for index ${n}`
|
|
1609
|
+
), o;
|
|
1610
|
+
},
|
|
1611
|
+
set(n, o) {
|
|
1612
|
+
r.set(n, o);
|
|
1613
|
+
},
|
|
1614
|
+
get size() {
|
|
1615
|
+
return r.size;
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
function Oe({
|
|
1620
|
+
itemCount: e,
|
|
1621
|
+
itemProps: t,
|
|
1622
|
+
itemSize: s
|
|
1623
|
+
}) {
|
|
1624
|
+
return useMemo(
|
|
1625
|
+
() => me({
|
|
1626
|
+
itemCount: e,
|
|
1627
|
+
itemProps: t,
|
|
1628
|
+
itemSize: s
|
|
1629
|
+
}),
|
|
1630
|
+
[e, t, s]
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
function ye({
|
|
1634
|
+
containerSize: e,
|
|
1635
|
+
itemSize: t
|
|
1636
|
+
}) {
|
|
1637
|
+
let s;
|
|
1638
|
+
switch (typeof t) {
|
|
1639
|
+
case "string": {
|
|
1640
|
+
$(
|
|
1641
|
+
t.endsWith("%"),
|
|
1642
|
+
`Invalid item size: "${t}"; string values must be percentages (e.g. "100%")`
|
|
1643
|
+
), $(
|
|
1644
|
+
e !== void 0,
|
|
1645
|
+
"Container size must be defined if a percentage item size is specified"
|
|
1646
|
+
), s = e * parseInt(t) / 100;
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
default: {
|
|
1650
|
+
s = t;
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return s;
|
|
1655
|
+
}
|
|
1656
|
+
function ee({
|
|
1657
|
+
containerElement: e,
|
|
1658
|
+
containerStyle: t,
|
|
1659
|
+
defaultContainerSize: s = 0,
|
|
1660
|
+
direction: r,
|
|
1661
|
+
isRtl: n = false,
|
|
1662
|
+
itemCount: o,
|
|
1663
|
+
itemProps: i,
|
|
1664
|
+
itemSize: f,
|
|
1665
|
+
onResize: l,
|
|
1666
|
+
overscanCount: c
|
|
1667
|
+
}) {
|
|
1668
|
+
const [d, a] = useState({
|
|
1669
|
+
startIndexVisible: 0,
|
|
1670
|
+
startIndexOverscan: 0,
|
|
1671
|
+
stopIndexVisible: -1,
|
|
1672
|
+
stopIndexOverscan: -1
|
|
1673
|
+
}), {
|
|
1674
|
+
startIndexVisible: u,
|
|
1675
|
+
startIndexOverscan: I,
|
|
1676
|
+
stopIndexVisible: m,
|
|
1677
|
+
stopIndexOverscan: b
|
|
1678
|
+
} = {
|
|
1679
|
+
startIndexVisible: Math.min(o - 1, d.startIndexVisible),
|
|
1680
|
+
startIndexOverscan: Math.min(o - 1, d.startIndexOverscan),
|
|
1681
|
+
stopIndexVisible: Math.min(o - 1, d.stopIndexVisible),
|
|
1682
|
+
stopIndexOverscan: Math.min(o - 1, d.stopIndexOverscan)
|
|
1683
|
+
}, { height: g = s, width: w = s } = Ie({
|
|
1684
|
+
defaultHeight: s ,
|
|
1685
|
+
defaultWidth: void 0,
|
|
1686
|
+
element: e,
|
|
1687
|
+
mode: "only-height" ,
|
|
1688
|
+
style: t
|
|
1689
|
+
}), y = useRef({
|
|
1690
|
+
height: 0,
|
|
1691
|
+
width: 0
|
|
1692
|
+
}), V = g , h = ye({ containerSize: V, itemSize: f });
|
|
1693
|
+
useLayoutEffect(() => {
|
|
1694
|
+
if (typeof l == "function") {
|
|
1695
|
+
const p = y.current;
|
|
1696
|
+
(p.height !== g || p.width !== w) && (l({ height: g, width: w }, { ...p }), p.height = g, p.width = w);
|
|
1697
|
+
}
|
|
1698
|
+
}, [g, l, w]);
|
|
1699
|
+
const z = Oe({
|
|
1700
|
+
itemCount: o,
|
|
1701
|
+
itemProps: i,
|
|
1702
|
+
itemSize: h
|
|
1703
|
+
}), k = useCallback(
|
|
1704
|
+
(p) => z.get(p),
|
|
1705
|
+
[z]
|
|
1706
|
+
), S = useCallback(
|
|
1707
|
+
() => fe({
|
|
1708
|
+
cachedBounds: z,
|
|
1709
|
+
itemCount: o,
|
|
1710
|
+
itemSize: h
|
|
1711
|
+
}),
|
|
1712
|
+
[z, o, h]
|
|
1713
|
+
), W = useCallback(
|
|
1714
|
+
(p) => {
|
|
1715
|
+
const T = Z({
|
|
1716
|
+
containerElement: e,
|
|
1717
|
+
direction: r,
|
|
1718
|
+
isRtl: n,
|
|
1719
|
+
scrollOffset: p
|
|
1720
|
+
});
|
|
1721
|
+
return ie({
|
|
1722
|
+
cachedBounds: z,
|
|
1723
|
+
containerScrollOffset: T,
|
|
1724
|
+
containerSize: V,
|
|
1725
|
+
itemCount: o,
|
|
1726
|
+
overscanCount: c
|
|
1727
|
+
});
|
|
1728
|
+
},
|
|
1729
|
+
[
|
|
1730
|
+
z,
|
|
1731
|
+
e,
|
|
1732
|
+
V,
|
|
1733
|
+
r,
|
|
1734
|
+
n,
|
|
1735
|
+
o,
|
|
1736
|
+
c
|
|
1737
|
+
]
|
|
1738
|
+
);
|
|
1739
|
+
q(() => {
|
|
1740
|
+
const p = (e?.scrollTop ) ?? 0;
|
|
1741
|
+
a(W(p));
|
|
1742
|
+
}, [e, r, W]), q(() => {
|
|
1743
|
+
if (!e)
|
|
1744
|
+
return;
|
|
1745
|
+
const p = () => {
|
|
1746
|
+
a((T) => {
|
|
1747
|
+
const { scrollLeft: R, scrollTop: v } = e, x = Z({
|
|
1748
|
+
containerElement: e,
|
|
1749
|
+
direction: r,
|
|
1750
|
+
isRtl: n,
|
|
1751
|
+
scrollOffset: v
|
|
1752
|
+
}), A = ie({
|
|
1753
|
+
cachedBounds: z,
|
|
1754
|
+
containerScrollOffset: x,
|
|
1755
|
+
containerSize: V,
|
|
1756
|
+
itemCount: o,
|
|
1757
|
+
overscanCount: c
|
|
1758
|
+
});
|
|
1759
|
+
return Y(A, T) ? T : A;
|
|
1760
|
+
});
|
|
864
1761
|
};
|
|
865
|
-
|
|
866
|
-
|
|
1762
|
+
return e.addEventListener("scroll", p), () => {
|
|
1763
|
+
e.removeEventListener("scroll", p);
|
|
867
1764
|
};
|
|
868
|
-
|
|
1765
|
+
}, [
|
|
1766
|
+
z,
|
|
1767
|
+
e,
|
|
1768
|
+
V,
|
|
1769
|
+
r,
|
|
1770
|
+
o,
|
|
1771
|
+
c
|
|
1772
|
+
]);
|
|
1773
|
+
const O = ae(
|
|
1774
|
+
({
|
|
1775
|
+
align: p = "auto",
|
|
1776
|
+
containerScrollOffset: T,
|
|
1777
|
+
index: R
|
|
1778
|
+
}) => {
|
|
1779
|
+
let v = we({
|
|
1780
|
+
align: p,
|
|
1781
|
+
cachedBounds: z,
|
|
1782
|
+
containerScrollOffset: T,
|
|
1783
|
+
containerSize: V,
|
|
1784
|
+
index: R,
|
|
1785
|
+
itemCount: o,
|
|
1786
|
+
itemSize: h
|
|
1787
|
+
});
|
|
1788
|
+
if (e) {
|
|
1789
|
+
if (v = Z({
|
|
1790
|
+
containerElement: e,
|
|
1791
|
+
direction: r,
|
|
1792
|
+
isRtl: n,
|
|
1793
|
+
scrollOffset: v
|
|
1794
|
+
}), typeof e.scrollTo != "function") {
|
|
1795
|
+
const x = W(v);
|
|
1796
|
+
Y(d, x) || a(x);
|
|
1797
|
+
}
|
|
1798
|
+
return v;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
);
|
|
1802
|
+
return {
|
|
1803
|
+
getCellBounds: k,
|
|
1804
|
+
getEstimatedSize: S,
|
|
1805
|
+
scrollToIndex: O,
|
|
1806
|
+
startIndexOverscan: I,
|
|
1807
|
+
startIndexVisible: u,
|
|
1808
|
+
stopIndexOverscan: b,
|
|
1809
|
+
stopIndexVisible: m
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
function de(e) {
|
|
1813
|
+
return useMemo(() => e, Object.values(e));
|
|
1814
|
+
}
|
|
1815
|
+
function ue(e, t) {
|
|
1816
|
+
const {
|
|
1817
|
+
ariaAttributes: s,
|
|
1818
|
+
style: r,
|
|
1819
|
+
...n
|
|
1820
|
+
} = e, {
|
|
1821
|
+
ariaAttributes: o,
|
|
1822
|
+
style: i,
|
|
1823
|
+
...f
|
|
1824
|
+
} = t;
|
|
1825
|
+
return Y(s, o) && Y(r, i) && Y(n, f);
|
|
1826
|
+
}
|
|
1827
|
+
function ze(e) {
|
|
1828
|
+
return e != null && typeof e == "object" && "getAverageRowHeight" in e && typeof e.getAverageRowHeight == "function";
|
|
1829
|
+
}
|
|
1830
|
+
const te = "data-react-window-index";
|
|
1831
|
+
function Ae({
|
|
1832
|
+
children: e,
|
|
1833
|
+
className: t,
|
|
1834
|
+
defaultHeight: s = 0,
|
|
1835
|
+
listRef: r,
|
|
1836
|
+
onResize: n,
|
|
1837
|
+
onRowsRendered: o,
|
|
1838
|
+
overscanCount: i = 3,
|
|
1839
|
+
rowComponent: f,
|
|
1840
|
+
rowCount: l,
|
|
1841
|
+
rowHeight: c,
|
|
1842
|
+
rowProps: d,
|
|
1843
|
+
tagName: a = "div",
|
|
1844
|
+
style: u,
|
|
1845
|
+
...I
|
|
1846
|
+
}) {
|
|
1847
|
+
const m = de(d), b = useMemo(
|
|
1848
|
+
() => memo(f, ue),
|
|
1849
|
+
[f]
|
|
1850
|
+
), [g, w] = useState(null), y = ze(c), V = useMemo(() => y ? (v) => c.getRowHeight(v) ?? c.getAverageRowHeight() : c, [y, c]), {
|
|
1851
|
+
getCellBounds: h,
|
|
1852
|
+
getEstimatedSize: z,
|
|
1853
|
+
scrollToIndex: k,
|
|
1854
|
+
startIndexOverscan: S,
|
|
1855
|
+
startIndexVisible: W,
|
|
1856
|
+
stopIndexOverscan: O,
|
|
1857
|
+
stopIndexVisible: p
|
|
1858
|
+
} = ee({
|
|
1859
|
+
containerElement: g,
|
|
1860
|
+
containerStyle: u,
|
|
1861
|
+
defaultContainerSize: s,
|
|
1862
|
+
direction: "vertical",
|
|
1863
|
+
itemCount: l,
|
|
1864
|
+
itemProps: m,
|
|
1865
|
+
itemSize: V,
|
|
1866
|
+
onResize: n,
|
|
1867
|
+
overscanCount: i
|
|
1868
|
+
});
|
|
1869
|
+
useImperativeHandle(
|
|
1870
|
+
r,
|
|
1871
|
+
() => ({
|
|
1872
|
+
get element() {
|
|
1873
|
+
return g;
|
|
1874
|
+
},
|
|
1875
|
+
scrollToRow({
|
|
1876
|
+
align: v = "auto",
|
|
1877
|
+
behavior: x = "auto",
|
|
1878
|
+
index: A
|
|
1879
|
+
}) {
|
|
1880
|
+
const M = k({
|
|
1881
|
+
align: v,
|
|
1882
|
+
containerScrollOffset: g?.scrollTop ?? 0,
|
|
1883
|
+
index: A
|
|
1884
|
+
});
|
|
1885
|
+
typeof g?.scrollTo == "function" && g.scrollTo({
|
|
1886
|
+
behavior: x,
|
|
1887
|
+
top: M
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
}),
|
|
1891
|
+
[g, k]
|
|
1892
|
+
), q(() => {
|
|
1893
|
+
if (!g)
|
|
1894
|
+
return;
|
|
1895
|
+
const v = Array.from(g.children).filter((x, A) => {
|
|
1896
|
+
if (x.hasAttribute("aria-hidden"))
|
|
1897
|
+
return false;
|
|
1898
|
+
const M = `${S + A}`;
|
|
1899
|
+
return x.setAttribute(te, M), true;
|
|
1900
|
+
});
|
|
1901
|
+
if (y)
|
|
1902
|
+
return c.observeRowElements(v);
|
|
1903
|
+
}, [
|
|
1904
|
+
g,
|
|
1905
|
+
y,
|
|
1906
|
+
c,
|
|
1907
|
+
S,
|
|
1908
|
+
O
|
|
1909
|
+
]), useEffect(() => {
|
|
1910
|
+
S >= 0 && O >= 0 && o && o(
|
|
1911
|
+
{
|
|
1912
|
+
startIndex: W,
|
|
1913
|
+
stopIndex: p
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
startIndex: S,
|
|
1917
|
+
stopIndex: O
|
|
1918
|
+
}
|
|
1919
|
+
);
|
|
1920
|
+
}, [
|
|
1921
|
+
o,
|
|
1922
|
+
S,
|
|
1923
|
+
W,
|
|
1924
|
+
O,
|
|
1925
|
+
p
|
|
1926
|
+
]);
|
|
1927
|
+
const T = useMemo(() => {
|
|
1928
|
+
const v = [];
|
|
1929
|
+
if (l > 0)
|
|
1930
|
+
for (let x = S; x <= O; x++) {
|
|
1931
|
+
const A = h(x);
|
|
1932
|
+
v.push(
|
|
1933
|
+
/* @__PURE__ */ createElement(
|
|
1934
|
+
b,
|
|
1935
|
+
{
|
|
1936
|
+
...m,
|
|
1937
|
+
ariaAttributes: {
|
|
1938
|
+
"aria-posinset": x + 1,
|
|
1939
|
+
"aria-setsize": l,
|
|
1940
|
+
role: "listitem"
|
|
1941
|
+
},
|
|
1942
|
+
key: x,
|
|
1943
|
+
index: x,
|
|
1944
|
+
style: {
|
|
1945
|
+
position: "absolute",
|
|
1946
|
+
left: 0,
|
|
1947
|
+
transform: `translateY(${A.scrollOffset}px)`,
|
|
1948
|
+
// In case of dynamic row heights, don't specify a height style
|
|
1949
|
+
// otherwise a default/estimated height would mask the actual height
|
|
1950
|
+
height: y ? void 0 : A.size,
|
|
1951
|
+
width: "100%"
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
)
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
return v;
|
|
1958
|
+
}, [
|
|
1959
|
+
b,
|
|
1960
|
+
h,
|
|
1961
|
+
y,
|
|
1962
|
+
l,
|
|
1963
|
+
m,
|
|
1964
|
+
S,
|
|
1965
|
+
O
|
|
1966
|
+
]), R = /* @__PURE__ */ jsx(
|
|
1967
|
+
"div",
|
|
1968
|
+
{
|
|
1969
|
+
"aria-hidden": true,
|
|
1970
|
+
style: {
|
|
1971
|
+
height: z(),
|
|
1972
|
+
width: "100%",
|
|
1973
|
+
zIndex: -1
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
);
|
|
1977
|
+
return createElement(
|
|
1978
|
+
a,
|
|
1979
|
+
{
|
|
1980
|
+
role: "list",
|
|
1981
|
+
...I,
|
|
1982
|
+
className: t,
|
|
1983
|
+
ref: w,
|
|
1984
|
+
style: {
|
|
1985
|
+
position: "relative",
|
|
1986
|
+
maxHeight: "100%",
|
|
1987
|
+
flexGrow: 1,
|
|
1988
|
+
overflowY: "auto",
|
|
1989
|
+
...u
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1992
|
+
T,
|
|
1993
|
+
e,
|
|
1994
|
+
R
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
const VirtualizedRow = ({ index, style, items, columns, gridTemplate, }) => {
|
|
1999
|
+
const item = items[index];
|
|
2000
|
+
return (jsx("div", { role: "row", className: "grid border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 group", style: {
|
|
2001
|
+
...style,
|
|
2002
|
+
display: 'grid',
|
|
2003
|
+
gridTemplateColumns: gridTemplate,
|
|
2004
|
+
minWidth: 'fit-content',
|
|
2005
|
+
}, children: columns.map((col) => {
|
|
2006
|
+
var _a;
|
|
2007
|
+
const content = col.render ? col.render(item) : ((_a = get(item, col.name)) !== null && _a !== void 0 ? _a : 'Not set');
|
|
2008
|
+
return (jsx("div", { role: "cell", className: buildClassName('px-3 py-3 whitespace-nowrap border-r last:border-r-0 border-gray-200 dark:border-gray-700', col.align === 'center' && 'text-center', col.align === 'right' && 'text-right', col.sticky === 'left' &&
|
|
2009
|
+
'sticky left-0 z-10 bg-white dark:bg-gray-900 shadow-[2px_0_4px_rgba(0,0,0,0.05)]', col.sticky === 'right' &&
|
|
2010
|
+
'sticky right-0 z-10 bg-white dark:bg-gray-900 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]'), children: content }, col.name));
|
|
2011
|
+
}) }));
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
/* -------------------------------------------------------
|
|
2015
|
+
* Convert column definitions into CSS grid template
|
|
2016
|
+
* ------------------------------------------------------- */
|
|
2017
|
+
const useGridTemplate = (columns) => useMemo(() => columns.map((col) => (col.width ? `${col.width}px` : '1fr')).join(' '), [columns]);
|
|
2018
|
+
const VirtualizedDataTable = ({ items, columns, sorting = {}, onSort, loading = false, emptyMessage = 'No data available', containerClass = '', wrapperClass = '', rowHeight = 48, maxHeight = 400, }) => {
|
|
2019
|
+
const scrollRef = useRef(null);
|
|
2020
|
+
const headerRef = useRef(null);
|
|
2021
|
+
const gridTemplate = useGridTemplate(columns);
|
|
2022
|
+
/** Sync body → header scroll */
|
|
2023
|
+
const handleScrollSync = (scrollOffset) => {
|
|
2024
|
+
if (headerRef.current) {
|
|
2025
|
+
headerRef.current.scrollLeft = scrollOffset;
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
const handleSort = useCallback((column) => {
|
|
2029
|
+
if (column.sortable)
|
|
2030
|
+
onSort === null || onSort === void 0 ? void 0 : onSort(column);
|
|
2031
|
+
}, [onSort]);
|
|
2032
|
+
/* -------------------------------------------------------
|
|
2033
|
+
* RENDER: Header row (real DOM for accessibility)
|
|
2034
|
+
* ------------------------------------------------------- */
|
|
2035
|
+
const Header = (jsx("div", { ref: headerRef, className: "overflow-hidden w-full", children: jsx("div", { className: "grid bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700", style: {
|
|
2036
|
+
gridTemplateColumns: gridTemplate,
|
|
2037
|
+
minWidth: 'fit-content',
|
|
2038
|
+
}, children: columns.map((col) => (jsx("div", { role: "columnheader", className: buildClassName('px-3 py-3 font-semibold text-sm whitespace-nowrap flex items-center', 'border-r last:border-r-0 border-gray-200 dark:border-gray-700', col.sticky === 'left' &&
|
|
2039
|
+
'sticky left-0 z-20 bg-gray-50 dark:bg-gray-800 shadow-[2px_0_4px_rgba(0,0,0,0.05)]', col.sticky === 'right' &&
|
|
2040
|
+
'sticky right-0 z-20 bg-gray-50 dark:bg-gray-800 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]'), children: jsxs(TextContent, { role: col.sortable ? 'button' : undefined, className: buildClassName(col.sortable && 'inline-flex items-center gap-2 cursor-pointer select-none'), onClick: () => handleSort(col), children: [col.label, col.sortable ? getSortIcon(sorting[col.name]) : null] }) }, col.name))) }) }));
|
|
2041
|
+
/* -------------------------------------------------------
|
|
2042
|
+
* LOADING STATE (non-virtualized placeholder)
|
|
2043
|
+
* ------------------------------------------------------- */
|
|
2044
|
+
if (loading) {
|
|
2045
|
+
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className: buildClassName('rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden', wrapperClass), children: [Header, jsx("div", { style: { height: maxHeight }, className: "flex flex-col", children: [...Array(6)].map((_, idx) => (jsx("div", { "data-testid": "skeleton-row", className: "grid animate-pulse bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800", style: {
|
|
2046
|
+
gridTemplateColumns: gridTemplate,
|
|
2047
|
+
height: rowHeight,
|
|
2048
|
+
}, children: columns.map((col) => (jsx("div", { className: "px-3 py-3", children: jsx("div", { className: "h-4 w-3/4 bg-gray-300 dark:bg-gray-700 rounded" }) }, col.name))) }, idx))) })] }) }));
|
|
2049
|
+
}
|
|
2050
|
+
/* -------------------------------------------------------
|
|
2051
|
+
* EMPTY STATE (non-virtualized → single row)
|
|
2052
|
+
* ------------------------------------------------------- */
|
|
2053
|
+
if (!loading && items.length === 0) {
|
|
2054
|
+
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className: buildClassName('rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden', wrapperClass), children: [Header, jsx("div", { className: "flex items-center justify-center text-gray-500 dark:text-gray-300", style: { height: maxHeight }, children: emptyMessage })] }) }));
|
|
2055
|
+
}
|
|
2056
|
+
/* -------------------------------------------------------
|
|
2057
|
+
* MAIN (virtualized body)
|
|
2058
|
+
* ------------------------------------------------------- */
|
|
2059
|
+
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className: buildClassName('rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden', wrapperClass), children: [Header, jsx("div", { style: {
|
|
2060
|
+
height: maxHeight,
|
|
2061
|
+
overflowY: 'auto',
|
|
2062
|
+
overflowX: 'auto',
|
|
2063
|
+
}, children: jsx(Ae, { listRef: scrollRef, rowComponent: VirtualizedRow, rowCount: items.length, rowHeight: rowHeight, rowProps: { items, columns, gridTemplate }, onScroll: (e) => handleScrollSync(e.currentTarget.scrollLeft) }) })] }) }));
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
const useIsMobile = () => {
|
|
2067
|
+
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
|
|
2068
|
+
useEffect(() => {
|
|
2069
|
+
function handleResize() {
|
|
2070
|
+
setIsMobile(window.innerWidth < 768);
|
|
2071
|
+
}
|
|
2072
|
+
window.addEventListener('resize', handleResize);
|
|
2073
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
2074
|
+
}, []);
|
|
2075
|
+
return isMobile;
|
|
2076
|
+
};
|
|
2077
|
+
|
|
2078
|
+
const useIsTablet = () => {
|
|
2079
|
+
const [isTablet, setIsTablet] = useState(window.innerWidth < 768 && window.innerHeight > 960);
|
|
2080
|
+
useEffect(() => {
|
|
2081
|
+
function handleResize() {
|
|
2082
|
+
setIsTablet(window.innerWidth > 768 && window.innerHeight < 960);
|
|
2083
|
+
}
|
|
2084
|
+
window.addEventListener('resize', handleResize);
|
|
2085
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
2086
|
+
}, []);
|
|
2087
|
+
return isTablet;
|
|
869
2088
|
};
|
|
870
2089
|
|
|
871
|
-
export { Alert, Badge, Banner, BodyText, Breadcrumb, Button, Card, Checkbox, CheckboxGroup, DOTS, DetailedInformation, Dot, Drawer, Dropdown, EmptyState, EmptyTableRow, HeadingText, Input, Label, Loader, PASSWORD_PLACEHOLDER, PASSWORD_TYPE, Pagination, PasswordInput,
|
|
2090
|
+
export { Alert, Badge, Banner, BodyText, Breadcrumb, Button, Card, Checkbox, CheckboxGroup, DOTS, DataTable, DetailedInformation, Dot, Drawer, Dropdown, EmptyState, EmptyTableRow, HeadingText, Input, Label, Loader, PASSWORD_PLACEHOLDER, PASSWORD_TYPE, Pagination, PasswordInput, Radio, RadioGroup, RadioSwitch, RangeInput, RangeSlider, SelectBox, SortIconMap, Table, TableBody, TableCell, TableFoot, TableHead, TableHeaderCell, TableRow, Tabs, TextContent, Textarea, Toast, ToastProvider, VirtualizedDataTable, buildClassName, buildInputClass, get, getSortIcon, isArray, isEmpty, nextSortDirection, updateSortQuery, useDebounce, useDebounceFn, useIsMobile, useIsTablet, usePagination, useToast };
|
|
872
2091
|
//# sourceMappingURL=react-tailwind.es.js.map
|