@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.
Files changed (144) hide show
  1. package/dist/react-tailwind.cjs.js +1593 -362
  2. package/dist/react-tailwind.cjs.js.map +1 -1
  3. package/dist/react-tailwind.es.js +1579 -360
  4. package/dist/react-tailwind.es.js.map +1 -1
  5. package/dist/types/components/Alert/Alert.d.ts +3 -0
  6. package/dist/types/components/Alert/Alert.test.d.ts +1 -0
  7. package/dist/types/components/Alert/Alert.types.d.ts +34 -0
  8. package/dist/types/components/Alert/index.d.ts +2 -35
  9. package/dist/types/components/Badge/Badge.d.ts +3 -0
  10. package/dist/types/components/Badge/Badge.test.d.ts +1 -0
  11. package/dist/types/components/Badge/Badge.types.d.ts +22 -0
  12. package/dist/types/components/Badge/index.d.ts +2 -24
  13. package/dist/types/components/Banner/Banner.d.ts +3 -0
  14. package/dist/types/components/Banner/Banner.test.d.ts +1 -0
  15. package/dist/types/components/Banner/Banner.types.d.ts +14 -0
  16. package/dist/types/components/Banner/index.d.ts +2 -16
  17. package/dist/types/components/BodyText/BodyText.d.ts +3 -0
  18. package/dist/types/components/BodyText/BodyText.test.d.ts +1 -0
  19. package/dist/types/components/BodyText/BodyText.types.d.ts +27 -0
  20. package/dist/types/components/BodyText/index.d.ts +2 -28
  21. package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +3 -0
  22. package/dist/types/components/Breadcrumb/Breadcrumb.test.d.ts +1 -0
  23. package/dist/types/components/Breadcrumb/Breadcrumb.types.d.ts +29 -0
  24. package/dist/types/components/Breadcrumb/index.d.ts +2 -30
  25. package/dist/types/components/Button/Button.d.ts +2 -3
  26. package/dist/types/components/Button/Button.test.d.ts +1 -0
  27. package/dist/types/components/Button/Button.types.d.ts +171 -0
  28. package/dist/types/components/Button/index.d.ts +1 -1
  29. package/dist/types/components/Card/Card.d.ts +3 -0
  30. package/dist/types/components/Card/Card.test.d.ts +1 -0
  31. package/dist/types/components/Card/Card.types.d.ts +23 -0
  32. package/dist/types/components/Card/index.d.ts +2 -9
  33. package/dist/types/components/Checkbox/Checkbox.d.ts +2 -0
  34. package/dist/types/components/Checkbox/Checkbox.test.d.ts +1 -0
  35. package/dist/types/components/Checkbox/Checkbox.types.d.ts +14 -0
  36. package/dist/types/components/Checkbox/index.d.ts +2 -13
  37. package/dist/types/components/CheckboxGroup/CheckboxGroup.d.ts +5 -0
  38. package/dist/types/components/CheckboxGroup/CheckboxGroup.test.d.ts +1 -0
  39. package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +55 -0
  40. package/dist/types/components/CheckboxGroup/index.d.ts +2 -56
  41. package/dist/types/components/DataTable/DataTable.d.ts +2 -0
  42. package/dist/types/components/DataTable/DataTable.icons.d.ts +4 -0
  43. package/dist/types/components/DataTable/DataTable.test.d.ts +1 -0
  44. package/dist/types/components/DataTable/DataTable.types.d.ts +63 -0
  45. package/dist/types/components/DataTable/DataTable.utils.d.ts +12 -0
  46. package/dist/types/components/DataTable/DataTable.utils.test.d.ts +1 -0
  47. package/dist/types/components/DataTable/index.d.ts +4 -0
  48. package/dist/types/components/DetailedInformation/DetailedInformation.d.ts +2 -0
  49. package/dist/types/components/DetailedInformation/DetailedInformation.test.d.ts +1 -0
  50. package/dist/types/components/DetailedInformation/DetailedInformation.types.d.ts +54 -0
  51. package/dist/types/components/DetailedInformation/index.d.ts +2 -63
  52. package/dist/types/components/Drawer/Drawer.d.ts +1 -1
  53. package/dist/types/components/Drawer/Drawer.test.d.ts +1 -0
  54. package/dist/types/components/Drawer/{type.d.ts → Drawer.types.d.ts} +1 -1
  55. package/dist/types/components/Drawer/index.d.ts +1 -1
  56. package/dist/types/components/Dropdown/Dropdown.d.ts +3 -0
  57. package/dist/types/components/Dropdown/Dropdown.test.d.ts +1 -0
  58. package/dist/types/components/Dropdown/Dropdown.types.d.ts +107 -0
  59. package/dist/types/components/Dropdown/index.d.ts +2 -62
  60. package/dist/types/components/HeadingText/HeadingText.d.ts +9 -0
  61. package/dist/types/components/HeadingText/HeadingText.types.d.ts +5 -0
  62. package/dist/types/components/HeadingText/index.d.ts +2 -12
  63. package/dist/types/components/Input/Input.d.ts +6 -0
  64. package/dist/types/components/Input/Input.test.d.ts +1 -0
  65. package/dist/types/components/Input/Input.types.d.ts +12 -0
  66. package/dist/types/components/Input/index.d.ts +2 -13
  67. package/dist/types/components/Label/Label.d.ts +6 -0
  68. package/dist/types/components/Label/index.d.ts +1 -5
  69. package/dist/types/components/Pagination/Pagination.d.ts +1 -31
  70. package/dist/types/components/Pagination/Pagination.test.d.ts +1 -0
  71. package/dist/types/components/Pagination/Pagination.types.d.ts +36 -0
  72. package/dist/types/components/Pagination/index.d.ts +1 -0
  73. package/dist/types/components/Pagination/usePagination.d.ts +1 -5
  74. package/dist/types/components/PasswordInput/PasswordInput.d.ts +6 -0
  75. package/dist/types/components/PasswordInput/PasswordInput.test.d.ts +1 -0
  76. package/dist/types/components/PasswordInput/index.d.ts +1 -6
  77. package/dist/types/components/Radio/Radio.d.ts +2 -0
  78. package/dist/types/components/Radio/Radio.test.d.ts +1 -0
  79. package/dist/types/components/Radio/Radio.types.d.ts +14 -0
  80. package/dist/types/components/Radio/index.d.ts +2 -0
  81. package/dist/types/components/RadioGroup/RadioGroup.d.ts +3 -0
  82. package/dist/types/components/RadioGroup/RadioGroup.test.d.ts +1 -0
  83. package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +65 -0
  84. package/dist/types/components/RadioGroup/index.d.ts +2 -0
  85. package/dist/types/components/RadioSwitch/RadioSwitch.d.ts +7 -0
  86. package/dist/types/components/RadioSwitch/RadioSwitch.test.d.ts +1 -0
  87. package/dist/types/components/RadioSwitch/RadioSwitch.types.d.ts +47 -0
  88. package/dist/types/components/RadioSwitch/index.d.ts +2 -0
  89. package/dist/types/components/RangeInput/-backup.d.ts +10 -0
  90. package/dist/types/components/RangeInput/RangeInput.d.ts +2 -0
  91. package/dist/types/components/RangeInput/RangeInput.test.d.ts +1 -0
  92. package/dist/types/components/RangeInput/RangeInput.types.d.ts +14 -0
  93. package/dist/types/components/RangeInput/index.d.ts +1 -10
  94. package/dist/types/components/RangeSlider/RangeSlider.d.ts +3 -0
  95. package/dist/types/components/RangeSlider/RangeSlider.test.d.ts +1 -0
  96. package/dist/types/components/RangeSlider/RangeSlider.types.d.ts +68 -0
  97. package/dist/types/components/RangeSlider/index.d.ts +2 -0
  98. package/dist/types/components/SelectBox/SelectBox.d.ts +2 -0
  99. package/dist/types/components/SelectBox/SelectBox.test.d.ts +1 -0
  100. package/dist/types/components/SelectBox/SelectBox.types.d.ts +94 -0
  101. package/dist/types/components/SelectBox/index.d.ts +2 -40
  102. package/dist/types/components/Table/Table.d.ts +10 -0
  103. package/dist/types/components/Table/Table.test.d.ts +1 -0
  104. package/dist/types/components/Table/Table.types.d.ts +91 -0
  105. package/dist/types/components/Table/index.d.ts +2 -48
  106. package/dist/types/components/Tabs/Tabs.d.ts +3 -0
  107. package/dist/types/components/Tabs/Tabs.test.d.ts +1 -0
  108. package/dist/types/components/Tabs/Tabs.types.d.ts +48 -0
  109. package/dist/types/components/Tabs/index.d.ts +1 -2
  110. package/dist/types/components/TextContent/TextContent.d.ts +3 -0
  111. package/dist/types/components/TextContent/TextContent.test.d.ts +1 -0
  112. package/dist/types/components/TextContent/TextContent.types.d.ts +23 -0
  113. package/dist/types/components/TextContent/index.d.ts +2 -10
  114. package/dist/types/components/Textarea/Textarea.d.ts +2 -0
  115. package/dist/types/components/Textarea/Textarea.test.d.ts +1 -0
  116. package/dist/types/components/Textarea/Textarea.types.d.ts +6 -0
  117. package/dist/types/components/Textarea/index.d.ts +2 -7
  118. package/dist/types/components/Toast/Toast.d.ts +4 -8
  119. package/dist/types/components/Toast/Toast.test.d.ts +1 -0
  120. package/dist/types/components/Toast/Toast.types.d.ts +117 -0
  121. package/dist/types/components/Toast/ToastProvider.d.ts +2 -11
  122. package/dist/types/components/Toast/ToastProvider.test.d.ts +1 -0
  123. package/dist/types/components/Toast/index.d.ts +1 -0
  124. package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.d.ts +3 -0
  125. package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.test.d.ts +1 -0
  126. package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.types.d.ts +18 -0
  127. package/dist/types/components/VirtualizedDataTable/VirtualizedRow.d.ts +5 -0
  128. package/dist/types/components/VirtualizedDataTable/index.d.ts +2 -0
  129. package/dist/types/components/_Dialog/Dialog.d.ts +4 -0
  130. package/dist/types/components/_Dialog/Dialog.types.d.ts +53 -0
  131. package/dist/types/components/_Dialog/index.d.ts +2 -0
  132. package/dist/types/components/common-type.d.ts +2 -1
  133. package/dist/types/hooks/use-debounce.d.ts +2 -0
  134. package/dist/types/hooks/use-is-mobile.d.ts +1 -0
  135. package/dist/types/hooks/use-is-tablet.d.ts +1 -0
  136. package/dist/types/index.d.ts +11 -6
  137. package/dist/types/utils/get.d.ts +1 -0
  138. package/package.json +28 -5
  139. package/dist/types/components/Button/type.d.ts +0 -58
  140. package/dist/types/components/RadioButton/index.d.ts +0 -7
  141. package/dist/types/components/RadioButtonGroup/index.d.ts +0 -19
  142. package/dist/types/components/RadioSwtich/index.d.ts +0 -14
  143. package/dist/types/components/Tag/index.d.ts +0 -12
  144. package/dist/types/components/hooks/use-debounce.d.ts +0 -1
@@ -1,15 +1,15 @@
1
- import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
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, isValidElement, useRef, createContext, useContext } from 'react';
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$d, _path2;
10
- 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); }
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$d({
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$d || (_path$d = /*#__PURE__*/React.createElement("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$c;
31
- 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); }
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$c({
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$c || (_path$c = /*#__PURE__*/React.createElement("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$b;
54
- 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); }
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$b({
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$b || (_path$b = /*#__PURE__*/React.createElement("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$a;
77
- 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); }
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$a({
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$a || (_path$a = /*#__PURE__*/React.createElement("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$9;
100
- 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); }
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$9({
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$9 || (_path$9 = /*#__PURE__*/React.createElement("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 { as = 'span', xsmall = false, small = false, strong = false, muted = false, error = false, className, ...rest } = props;
130
- return React__default.createElement(as, {
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 `text-xs ${iconOnly ? 'p-1 rounded-sm' : 'px-2 py-1 rounded-sm gap-1'}`;
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 `text-sm ${iconOnly ? 'p-1.5 rounded' : ' px-3 py-1.5 rounded gap-1.5'}`;
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 `text-sm ${iconOnly ? 'p-2 rounded-md' : 'px-4 py-2 rounded-md gap-2'}`;
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 `text-base ${iconOnly ? 'p-2.5 rounded-lg' : 'px-5 py-2.5 rounded-lg gap-2'}`;
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
- const Button = forwardRef((props, ref) => {
167
- const { className = '', children, theme = 'primary', variant = 'default', size = 'md', as: Element = 'button', loading = false, loadingText = '', type = 'button', rounded = false, disabled, iconOnly = false, ...buttonProps } = props;
168
- return (jsx(Element, { ref: ref, className: buildClassName(THEMED_BTN_CLASS[theme][variant], getBtnSizeClasses(size, iconOnly), 'focus:outline focus:outline-2 focus:outline-offset-2', 'font-medium flex items-center justify-center shrink-0', 'cursor-pointer disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75', 'transition duration-200 ease-in-out', rounded && 'rounded-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', className), type: type, disabled: disabled || loading, ...buttonProps, children: loading ? (jsxs("span", { className: "flex items-center gap-2", children: [jsx(Loader, { className: "text-white", size: "xs" }), loadingText && jsx(TextContent, { children: loadingText })] })) : (children) }));
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("span", { 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'), ...restProps, children: [jsx("span", { 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", role: "button", onClick: () => onRemove() })) : null] }));
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$8;
202
- 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); }
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$8({
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$8 || (_path$8 = /*#__PURE__*/React.createElement("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$7;
221
- 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); }
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$7({
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$7 || (_path$7 = /*#__PURE__*/React.createElement("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 mb-4 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 border border-orange-200 text-orange-700' : '');
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$6;
277
- 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); }
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$6({
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$6 || (_path$6 = /*#__PURE__*/React.createElement("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: [isLast(index) ? jsx("span", { className: "font-medium", children: item.text }) : render(item), !isLast(index) && jsx(SvgSlash, { className: "size-4" })] }, item.key))) }));
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 Card = forwardRef(({ className, as = 'div', hoverable = false, bordered = false, noPadding = false, ...rest }, ref) => React__default.createElement(as, {
302
- ref: ref,
303
- className: buildClassName('bg-white rounded-md dark:bg-gray-800', 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),
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-500 dark:text-gray-400', className), children: children }));
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
- const { type, name, id = name, wrapperClass = '', containerClass = '', label = null, labelClass = '', error = null, disabled = false, className = '', ...checkboxProps } = props;
314
- return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center', containerClass), children: [jsxs("label", { className: buildClassName('inline-flex items-center relative', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsx("input", { ref: ref, ...checkboxProps, id: id, name: name, type: "checkbox", disabled: disabled, className: buildClassName('peer size-4.5 cursor-pointer transition-all appearance-none rounded hover:shadow-md border border-gray-300 checked:bg-blue-600 checked:border-blue-600', 'bg-gray-50 dark:bg-gray-700', 'focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-offset-gray-900') }), jsx("span", { className: "absolute text-white opacity-0 peer-checked:opacity-100 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: id, className: `${disabled ? 'cursor-not-allowed' : 'cursor-pointer'} ml-2 font-normal text-gray-600 ${labelClass}`, children: label }))] }), error && jsx("div", { className: "mt-1 text-red-600 text-sm", children: error })] }));
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 = null, labelClass = '', labelWrapperClass = '', labelHint = null, containerClass = '', value = [], onChange, inline, } = props;
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
- setItems(() => options.map((option) => {
322
- if (typeof option === 'string') {
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
- 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) => (jsx(Checkbox, { label: item.label, value: item.value, checked: value.includes(item.value), name: name, id: item.value, onChange: onChange }, item.value))) })] }));
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 Title = ({ children, className }) => (jsx("h1", { className: buildClassName('text-3xl font-bold', className), children: children }));
332
- const SubTitle = ({ children, className }) => (jsx("h2", { className: buildClassName('text-2xl font-semibold', className), children: children }));
333
- const SubTitle2 = ({ children, className }) => (jsx("h3", { className: buildClassName('text-xl font-semibold', className), children: children }));
334
- const SubTitle3 = ({ children, className }) => (jsx("h4", { className: buildClassName('text-lg font-medium', className), children: children }));
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
- const DetailedInformation = ({ className, title, subTitle, details, compact = false, divider = true, titleClass, detailsClass, }) => {
345
- const hasTitles = title || subTitle;
346
- const filteredDetails = details.filter((detail) => !detail.hidden);
347
- return (jsxs("div", { className: buildClassName('p-2 md:p-4', compact ? 'md:p-2' : '', className), 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] }));
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 Dot = ({ as = 'span', className, ...props }) => React__default.createElement(as, {
351
- className: buildClassName('inline-block rounded-full bg-gray-500 dark:bg-gray-300', 'w-1 h-1', className),
352
- ...props,
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$5;
356
- 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); }
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$5({
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$5 || (_path$5 = /*#__PURE__*/React.createElement("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
- const alignmentMap = {
375
- center: 'items-center justify-center h-full',
376
- start: 'justify-start h-full',
377
- end: 'justify-end h-full',
378
- top: 'items-start',
379
- bottom: 'items-end h-full',
380
- 'top-center': 'items-center justify-center',
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
- const panelAlignmentMap = {
383
- 'top-center': 'data-[closed]:transform-[translateY(-50%)] rounded-b-lg',
384
- center: 'data-[closed]:transform-[scale(95%)] rounded-lg',
385
- start: 'data-[closed]:transform-[translateX(-50%)] rounded-r-lg',
386
- end: 'data-[closed]:transform-[translateX(50%)] rounded-l-lg',
387
- top: 'data-[closed]:transform-[translateY(-50%)] rounded-b-lg',
388
- bottom: 'data-[closed]:transform-[translateY(50%)] rounded-t-lg',
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
- const panelSizeMap = {
391
- xs: 'max-w-xs',
392
- sm: 'max-w-sm',
393
- md: 'max-w-md',
394
- lg: 'max-w-lg',
395
- xl: 'max-w-xl',
396
- '2xl': 'max-w-2xl',
397
- full: 'w-full',
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
- const Drawer = ({ isOpen, onClose = () => { }, align = 'end', size = 'md', backdrop = true, title, description = null, children = null, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, }) => {
400
- return (jsxs(Dialog, { open: isOpen, as: "div", className: "relative z-15 focus:outline-none", onClose: onClose, children: [backdrop && jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/20" }), jsx("div", { className: buildClassName('fixed inset-0 z-15 w-screen h-screen h-dvh'), 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 duration-200 ease-out data-[closed]:opacity-0 shadow-sm overflow-y-auto space-y-4', panelAlignmentMap[align], panelSizeMap[size], panelClass), children: [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/7 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/6 dark:text-white/50', descriptionClass), children: description }))] }), jsx("div", { className: buildClassName(contentClass), children: children })] }) }) })] }));
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
- const DROPDOWN_ITEM_CLASS = 'flex w-full px-3 py-2 cursor-pointer hover:bg-gray-50 rounded-lg';
404
- const Dropdown = ({ triggerButton, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', }) => {
405
- return (jsxs(Menu, { children: [jsx(MenuButton, { as: Fragment, children: triggerButton }), jsx(MenuItems, { transition: transition, anchor: anchor, className: buildClassName('w-52 origin-top-right rounded-xl border border-gray-100 bg-white shadow-sm p-1 text-sm/6 text-gray-800 transition duration-100 ease-out [--anchor-gap:--spacing(1)] focus:outline-none data-closed:scale-95 data-closed:opacity-0', itemsContainerClass), children: items.map((item) => (jsx(MenuItem, { children: item.divider ? (jsx("div", { className: "my-1 h-px bg-gray-200 dark:bg-white/5" })) : (renderItem(item, { className: DROPDOWN_ITEM_CLASS })) }, item.id))) })] }));
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 SvgFolderPlus = function SvgFolderPlus(props) {
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-folder-plus-icon lucide-folder-plus",
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: "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"
693
+ d: "m21 16-4 4-4-4M17 20V4M3 8l4-4 4 4M7 4v16"
424
694
  })));
425
695
  };
426
696
 
427
- const EmptyState = ({ title, description, className, action = null, }) => {
428
- 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] }) }));
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
- const Input = forwardRef((props, ref) => {
432
- const { name, id = name, type, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, autoComplete = 'off', ...inputProps } = props;
433
- const hasError = Boolean(error);
434
- const hasRightGroup = rightGroup != null && isValidElement(rightGroup);
435
- const hasLeftGroup = leftGroup != null && isValidElement(leftGroup);
436
- 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, ...inputProps, name: name, id: id, type: type, autoComplete: autoComplete, 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', 'dark:disabled:bg-gray-500 disabled:bg-gray-100', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center w-10", children: rightGroup }))] }), showErrorMessage && error && (jsx("p", { className: "mt-1 text-red-500 dark:text-red-500 text-sm", children: error }))] }));
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 DOTS = '...';
440
- const usePagination = ({ totalPages, currentPage, siblingCount = 1, // number of pages to show around currentPage
441
- }) => {
442
- const totalPageNumbers = siblingCount * 2 + 5;
443
- if (totalPageNumbers >= totalPages) {
444
- return Array.from({ length: totalPages }, (_, i) => i + 1);
445
- }
446
- const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
447
- const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
448
- const shouldShowLeftDots = leftSiblingIndex > 2;
449
- const shouldShowRightDots = rightSiblingIndex < totalPages - 1;
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 SvgChevronRight = function SvgChevronRight(props) {
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-chevron-right-icon lucide-chevron-right",
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: "m9 18 6-6-6-6"
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 Pagination = ({ page: currentPage, limit, total, onChange, className, ellipsis = true, // Default to showing ellipsis
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 RadioButton = forwardRef((props, ref) => {
517
- const { type = 'radio', name, id = name, wrapperClass = '', containerClass = '', label = null, labelClass = '', labelHint = null, error = null, ...radioProps } = props;
518
- 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, ...radioProps, type: "radio", id: id, name: name, className: "peer transition-all col-start-1 row-start-1 appearance-none shrink-0 size-4.5 border-1 border-gray-300 focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 checked:border-5 checked:border-blue-600 rounded-full bg-white" }) }), label && (jsx(Label, { htmlFor: id, className: buildClassName('cursor-pointer font-normal text-gray-600', labelClass), children: label }))] }), error && jsx("div", { className: "mt-1 text-red-600 text-sm", children: error })] }));
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 RadioButtonGroup = (props) => {
522
- const { name, label = null, labelClass = '', labelWrapperClass = '', labelHint = null, containerClass = '', options, value, row, onChange, } = props;
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-2', row ? 'flex-row items-center' : 'flex-col items-start'), children: items.map((item) => (jsx(RadioButton, { name: name, id: item.value, label: item.label, value: item.value, checked: value === item.value, onChange: onChange }, item.value))) })] }));
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
- const RangeInput = (props) => {
551
- const { name, id = name, label = null, labelClass = '', labelWrapperClass = '', labelHint = null, containerClass = '', min = 0, max = 0, value, onChange, ...rangeProps } = props;
552
- const handleRangeChange = (e) => {
553
- const { value: _value = '0', name: _name } = e.target;
554
- let changedValue = parseInt(_value);
555
- let nextValue = {
556
- ...props.value,
557
- [_name]: changedValue,
558
- };
559
- if (e.target.name === 'max' && (value === null || value === void 0 ? void 0 : value.min) && changedValue < parseInt(value.min || '0')) {
560
- nextValue[_name] = parseInt(value.min);
561
- }
562
- if (e.target.name === 'min' && (value === null || value === void 0 ? void 0 : value.max) && changedValue > parseInt(value.max || '0')) {
563
- nextValue[_name] = parseInt(value.max);
564
- }
565
- onChange &&
566
- onChange({
567
- target: {
568
- name,
569
- value: nextValue,
570
- },
571
- });
572
- };
573
- return (jsxs("div", { className: buildClassName('mb-3 relative', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint] })), jsxs("div", { className: "flex w-full gap-4 items-center", children: [jsx(Input, { ...rangeProps, id: id, name: "min", type: "number", min: min, max: value === null || value === void 0 ? void 0 : value.max, value: (value === null || value === void 0 ? void 0 : value.min) || '', className: "appearance-none", containerClass: "flex-1 shrink-0 mb-0", placeholder: "Min", onChange: handleRangeChange }), jsx("span", { children: "to" }), jsx(Input, { ...rangeProps, id: id, name: "max", type: "number", min: value === null || value === void 0 ? void 0 : value.min, max: max, value: (value === null || value === void 0 ? void 0 : value.max) || '', className: "appearance-none", containerClass: "flex-1 shrink-0 mb-0", placeholder: "Max", onChange: handleRangeChange, disabled: !(value === null || value === void 0 ? void 0 : value.min) })] })] }));
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) => buildClassName('transition-all', 'block w-full rounded-lg 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:border-gray-600 dark:placeholder-gray-500 placeholder:text-gray-400 dark:text-white', 'group-[.has-error]:outline-red-600 group-[.has-error]:focus:outline-red-600', '[&:has(:focus-visible)]:outline-2 [&:has(:focus-visible)]:outline-blue-600', 'group-[.has-error]:[&:has(:focus-visible)]:outline-red-600', disabled ? 'pointer-events-none bg-gray-100 dark:bg-gray-500' : 'bg-white dark:bg-gray-700');
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 && !debouncedQuery && !options.length;
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 filteredOptions = query === '' || async
690
- ? options
691
- : options.filter((option) => {
692
- if (typeof option === 'string')
693
- return option.toLowerCase().includes(query.toLowerCase());
694
- return option[labelKey].toLowerCase().includes(query.toLowerCase());
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
- const getDisplayValue = (option) => {
697
- return typeof option === 'string' ? option : option[labelKey];
698
- };
699
- const getValue = (option) => {
700
- return typeof option === 'string' ? option : option[valueKey];
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
- if (!isArray(selected)) {
1169
+ var _a;
1170
+ if (!isArray(selected))
704
1171
  return;
705
- }
706
- const nextSelectedValue = [...selected];
707
- nextSelectedValue.splice(idx, 1);
708
- onChange(nextSelectedValue);
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
- const handleOnChange = (value) => {
711
- let nextValue = !value ? (multiple ? [] : {}) : value;
712
- if (multiple && isArray(selected)) {
713
- if (maxSelection && selected.length >= maxSelection) {
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
- onChange(nextValue);
718
- setQuery('');
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 (error) {
727
- console.error('Error loading options:', error);
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: handleOnChange, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxs("div", { className: buildClassName(inputClass, 'flex flex-wrap gap-2 peer'), children: [isArray(selected) && (selected === null || selected === void 0 ? void 0 : selected.length) > 0 && (jsx(Fragment$1, { children: selected.map((option, idx) => (jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, getValue(option)))) })), jsx(ComboboxInput, { className: "outline-none flex-1", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (event) => setQuery(event.target.value), id: id })] })) : (jsxs("div", { className: "relative", 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, "data-disabled": disabled, id: id, readOnly: !searchable, ref: inputRef }), 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: () => handleOnChange(multiple ? [] : null), disabled: disabled, children: jsx(SvgX, { className: "size-4 fill-white/60 group-data-[hover]:fill-white" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", children: jsx(SvgChevronDown, { className: "size-4 fill-white/60 group-data-[hover]:fill-white" }) })] })), jsx(ComboboxOptions, { anchor: {
739
- to: 'bottom',
740
- gap: 5,
741
- }, 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-out data-[closed]:translate-y-[-25%] data-[closed]:opacity-0 data-[closed]:overflow-hidden', dropdownContainerClass), children: optionsLoading ? (jsx("div", { 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(Fragment$1, { children: [filteredOptions.map((option) => (jsxs(ComboboxOption, { disabled: option.disabled, value: option, className: "group flex items-center justify-between gap-2 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: [jsx("span", { className: "text-sm/6 dark:text-white", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:fill-white group-data-[selected]:visible" })] }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (jsxs("div", { 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 ? (jsxs("div", { role: "button", onClick: () => onAdd(query), className: "group flex items-center gap-2 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", children: [jsx(SvgCirclePlus, { className: "size-4.5 text-blue-600" }), jsx(TextContent, { children: addNewText }), jsx(TextContent, { strong: true, children: query })] })) : null] })) })] }), showErrorMessage && error && (jsx("p", { className: "text-red-500 dark:text-red-500 text-sm", children: error }))] }));
742
- };
743
-
744
- const TEXT_ALIGN_CLASSES = {
745
- left: 'text-left',
746
- center: 'text-center',
747
- right: 'text-right',
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 variantMap = {
812
- primary: 'bg-blue-100 text-blue-600 border border-blue-200',
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 && isValidElement(rightGroup);
827
- const hasLeftGroup = leftGroup != null && isValidElement(leftGroup);
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("p", { className: "mt-1 text-red-500 dark:text-red-500 text-sm", children: error }))] }));
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
- const typeStyles = {
832
- success: 'bg-green-500 text-white',
833
- error: 'bg-red-500 text-white',
834
- info: 'bg-blue-500 text-white',
835
- warning: 'bg-yellow-400 text-black',
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
- const Toast = ({ message, type, onClose }) => {
838
- return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300", enterFrom: "opacity-0 translate-x-4", enterTo: "opacity-100 translate-x-0", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: jsxs("div", { className: `flex items-center justify-between rounded-lg px-4 py-3 shadow-lg min-w-[240px] ${typeStyles[type]}`, children: [jsx("span", { children: message }), jsx("button", { onClick: onClose, className: "ml-3 text-xl leading-none hover:opacity-75", "aria-label": "Close toast", children: jsx(SvgX, { className: "size-5" }) })] }) }));
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
- const ToastProvider = ({ children }) => {
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 showToast = (message, options) => {
851
- var _a, _b, _c;
852
- const id = Date.now();
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) => [...prev, newToast]);
861
- if (newToast.autoClose) {
862
- setTimeout(() => closeToast(id), newToast.duration);
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
- const closeToast = (id) => {
866
- setToasts((prev) => prev.filter((t) => t.id !== id));
1762
+ return e.addEventListener("scroll", p), () => {
1763
+ e.removeEventListener("scroll", p);
867
1764
  };
868
- return (jsxs(ToastContext.Provider, { value: { showToast, closeToast }, children: [children, jsx("div", { className: "fixed top-4 right-4 z-50 space-y-3", children: toasts.map((toast) => (jsx(Toast, { ...toast, onClose: () => closeToast(toast.id) }, toast.id))) })] }));
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, RadioButton, RadioButtonGroup, RadioSwitch, RangeInput, SelectBox, TabelBody, TabelFoot, TabelHead, Table, TableCell, TableHeaderCell, TableRow, Tabs, Tag, TextContent, Textarea, ToastProvider, buildClassName, isArray, isEmpty, useDebounce, usePagination, useToast };
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