uiplex 1.0.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 (104) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +207 -0
  3. package/dist/Box/Box.d.ts +52 -0
  4. package/dist/Box/Box.d.ts.map +1 -0
  5. package/dist/Box/index.d.ts +3 -0
  6. package/dist/Box/index.d.ts.map +1 -0
  7. package/dist/Button/Button.d.ts +17 -0
  8. package/dist/Button/Button.d.ts.map +1 -0
  9. package/dist/Button/index.d.ts +3 -0
  10. package/dist/Button/index.d.ts.map +1 -0
  11. package/dist/CircularProgress/CircularProgress.d.ts +23 -0
  12. package/dist/CircularProgress/CircularProgress.d.ts.map +1 -0
  13. package/dist/CircularProgress/index.d.ts +3 -0
  14. package/dist/CircularProgress/index.d.ts.map +1 -0
  15. package/dist/Flex/Flex.d.ts +57 -0
  16. package/dist/Flex/Flex.d.ts.map +1 -0
  17. package/dist/Flex/index.d.ts +3 -0
  18. package/dist/Flex/index.d.ts.map +1 -0
  19. package/dist/FormControl/FormControl.d.ts +20 -0
  20. package/dist/FormControl/FormControl.d.ts.map +1 -0
  21. package/dist/FormControl/FormErrorMessage.d.ts +8 -0
  22. package/dist/FormControl/FormErrorMessage.d.ts.map +1 -0
  23. package/dist/FormControl/FormLabel.d.ts +9 -0
  24. package/dist/FormControl/FormLabel.d.ts.map +1 -0
  25. package/dist/FormControl/index.d.ts +7 -0
  26. package/dist/FormControl/index.d.ts.map +1 -0
  27. package/dist/Grid/Grid.d.ts +42 -0
  28. package/dist/Grid/Grid.d.ts.map +1 -0
  29. package/dist/Grid/index.d.ts +3 -0
  30. package/dist/Grid/index.d.ts.map +1 -0
  31. package/dist/IconButton/IconButton.d.ts +15 -0
  32. package/dist/IconButton/IconButton.d.ts.map +1 -0
  33. package/dist/IconButton/index.d.ts +3 -0
  34. package/dist/IconButton/index.d.ts.map +1 -0
  35. package/dist/Input/Input.d.ts +13 -0
  36. package/dist/Input/Input.d.ts.map +1 -0
  37. package/dist/Input/index.d.ts +3 -0
  38. package/dist/Input/index.d.ts.map +1 -0
  39. package/dist/Link/Link.d.ts +13 -0
  40. package/dist/Link/Link.d.ts.map +1 -0
  41. package/dist/Link/index.d.ts +3 -0
  42. package/dist/Link/index.d.ts.map +1 -0
  43. package/dist/Loader/Loader.d.ts +12 -0
  44. package/dist/Loader/Loader.d.ts.map +1 -0
  45. package/dist/Loader/index.d.ts +3 -0
  46. package/dist/Loader/index.d.ts.map +1 -0
  47. package/dist/Modal/Modal.d.ts +45 -0
  48. package/dist/Modal/Modal.d.ts.map +1 -0
  49. package/dist/Modal/index.d.ts +3 -0
  50. package/dist/Modal/index.d.ts.map +1 -0
  51. package/dist/Popover/Popover.d.ts +43 -0
  52. package/dist/Popover/Popover.d.ts.map +1 -0
  53. package/dist/Popover/index.d.ts +3 -0
  54. package/dist/Popover/index.d.ts.map +1 -0
  55. package/dist/Radio/Radio.d.ts +34 -0
  56. package/dist/Radio/Radio.d.ts.map +1 -0
  57. package/dist/Radio/index.d.ts +3 -0
  58. package/dist/Radio/index.d.ts.map +1 -0
  59. package/dist/Text/Text.d.ts +17 -0
  60. package/dist/Text/Text.d.ts.map +1 -0
  61. package/dist/Text/index.d.ts +3 -0
  62. package/dist/Text/index.d.ts.map +1 -0
  63. package/dist/Textarea/Textarea.d.ts +14 -0
  64. package/dist/Textarea/Textarea.d.ts.map +1 -0
  65. package/dist/Textarea/index.d.ts +3 -0
  66. package/dist/Textarea/index.d.ts.map +1 -0
  67. package/dist/Theme/ThemeProvider.d.ts +15 -0
  68. package/dist/Theme/ThemeProvider.d.ts.map +1 -0
  69. package/dist/Theme/ThemeScript.d.ts +31 -0
  70. package/dist/Theme/ThemeScript.d.ts.map +1 -0
  71. package/dist/Theme/ThemeToggle.d.ts +10 -0
  72. package/dist/Theme/ThemeToggle.d.ts.map +1 -0
  73. package/dist/Theme/index.d.ts +5 -0
  74. package/dist/Theme/index.d.ts.map +1 -0
  75. package/dist/Toast/Toast.d.ts +26 -0
  76. package/dist/Toast/Toast.d.ts.map +1 -0
  77. package/dist/Toast/ToastContainerGlobal.d.ts +3 -0
  78. package/dist/Toast/ToastContainerGlobal.d.ts.map +1 -0
  79. package/dist/Toast/ToastProvider.d.ts +11 -0
  80. package/dist/Toast/ToastProvider.d.ts.map +1 -0
  81. package/dist/Toast/ToastStatic.d.ts +18 -0
  82. package/dist/Toast/ToastStatic.d.ts.map +1 -0
  83. package/dist/Toast/index.d.ts +6 -0
  84. package/dist/Toast/index.d.ts.map +1 -0
  85. package/dist/Toast/toastManager.d.ts +28 -0
  86. package/dist/Toast/toastManager.d.ts.map +1 -0
  87. package/dist/Toast/useToast.d.ts +17 -0
  88. package/dist/Toast/useToast.d.ts.map +1 -0
  89. package/dist/Tooltip/Tooltip.d.ts +14 -0
  90. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  91. package/dist/Tooltip/index.d.ts +3 -0
  92. package/dist/Tooltip/index.d.ts.map +1 -0
  93. package/dist/hooks/index.d.ts +5 -0
  94. package/dist/hooks/index.d.ts.map +1 -0
  95. package/dist/hooks/useDisclosure.d.ts +8 -0
  96. package/dist/hooks/useDisclosure.d.ts.map +1 -0
  97. package/dist/hooks/useOutsideClick.d.ts +8 -0
  98. package/dist/hooks/useOutsideClick.d.ts.map +1 -0
  99. package/dist/index.cjs +1097 -0
  100. package/dist/index.css +2339 -0
  101. package/dist/index.d.ts +59 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +1056 -0
  104. package/package.json +72 -0
package/dist/index.js ADDED
@@ -0,0 +1,1056 @@
1
+ import './index.css';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import React, { useRef, useEffect, createContext, useContext, useState, useCallback, useMemo } from 'react';
4
+
5
+ const Button = ({ children, variant = "primary", size = "md", colorScheme = "", disabled = false, loading = false, leftIcon, rightIcon, onClick, className = "", style, ...props }) => {
6
+ const buttonClasses = [
7
+ "ui-button",
8
+ `ui-button--${variant}`,
9
+ `ui-button--${size}`,
10
+ `ui-button--${colorScheme}`,
11
+ loading && "ui-button--loading",
12
+ className,
13
+ ]
14
+ .filter(Boolean)
15
+ .join(" ");
16
+ const isDisabled = disabled || loading;
17
+ return (jsxs("button", { className: buttonClasses, disabled: isDisabled, onClick: onClick, style: style, ...props, children: [loading && (jsx("svg", { className: "ui-button__spinner", viewBox: "0 0 24 24", children: jsxs("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none", strokeDasharray: "31.416", strokeDashoffset: "31.416", children: [jsx("animate", { attributeName: "stroke-dasharray", dur: "2s", values: "0 31.416;15.708 15.708;0 31.416", repeatCount: "indefinite" }), jsx("animate", { attributeName: "stroke-dashoffset", dur: "2s", values: "0;-15.708;-31.416", repeatCount: "indefinite" })] }) })), !loading && leftIcon && (jsx("span", { className: "ui-button__left-icon", children: leftIcon })), jsx("span", { className: "ui-button__content", children: children }), !loading && rightIcon && (jsx("span", { className: "ui-button__right-icon", children: rightIcon }))] }));
18
+ };
19
+
20
+ const Loader = ({ width, height, isCentered = false, className = "", size = "md", variant = "spinner", }) => {
21
+ const loaderClasses = [
22
+ "ui-loader",
23
+ `ui-loader--${size}`,
24
+ `ui-loader--${variant}`,
25
+ isCentered && "ui-loader--centered",
26
+ className,
27
+ ]
28
+ .filter(Boolean)
29
+ .join(" ");
30
+ const getSize = () => {
31
+ if (width && height)
32
+ return { width, height };
33
+ const sizeMap = {
34
+ xs: { width: 16, height: 16 },
35
+ sm: { width: 24, height: 24 },
36
+ md: { width: 32, height: 32 },
37
+ lg: { width: 44, height: 44 },
38
+ };
39
+ return sizeMap[size];
40
+ };
41
+ const { width: finalWidth, height: finalHeight } = getSize();
42
+ const renderLoader = () => {
43
+ switch (variant) {
44
+ case "dots":
45
+ return (jsxs("div", { className: "ui-loader__dots", children: [jsx("div", { className: "ui-loader__dot" }), jsx("div", { className: "ui-loader__dot" }), jsx("div", { className: "ui-loader__dot" })] }));
46
+ case "pulse":
47
+ return (jsx("div", { className: "ui-loader__pulse", style: { width: finalWidth, height: finalHeight } }));
48
+ case "spinner":
49
+ default:
50
+ return (jsx("svg", { className: "ui-loader__spinner", width: finalWidth, height: finalHeight, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxs("circle", { cx: "12", cy: "12", r: "10", stroke: "#fbbf24", strokeWidth: "3", fill: "none", strokeDasharray: "31.416", strokeDashoffset: "31.416", children: [jsx("animate", { attributeName: "stroke-dasharray", dur: "1s", values: "0 31.416;15.708 15.708;0 31.416", repeatCount: "indefinite" }), jsx("animate", { attributeName: "stroke-dashoffset", dur: "1s", values: "0;-15.708;-31.416", repeatCount: "indefinite" })] }) }));
51
+ }
52
+ };
53
+ return jsx("div", { className: loaderClasses, children: renderLoader() });
54
+ };
55
+
56
+ const Radio = ({ id, name, value, checked = false, disabled = false, label, description, size = "md", colorScheme = "blue", onChange, className = "", }) => {
57
+ const radioId = id || `${name}-${value}`;
58
+ const radioClasses = [
59
+ "ui-radio",
60
+ `ui-radio--${size}`,
61
+ `ui-radio--${colorScheme}`,
62
+ disabled && "ui-radio--disabled",
63
+ className,
64
+ ]
65
+ .filter(Boolean)
66
+ .join(" ");
67
+ const handleChange = (event) => {
68
+ if (!disabled && onChange) {
69
+ onChange(event.target.value);
70
+ }
71
+ };
72
+ return (jsxs("label", { className: radioClasses, children: [jsx("input", { type: "radio", id: radioId, name: name, value: value, checked: checked, disabled: disabled, onChange: handleChange, className: "ui-radio__input" }), jsx("span", { className: "ui-radio__checkmark" }), (label || description) && (jsxs("div", { className: "ui-radio__content", children: [label && jsx("span", { className: "ui-radio__label", children: label }), description && (jsx("span", { className: "ui-radio__description", children: description }))] }))] }));
73
+ };
74
+ const RadioGroup = ({ name, value, options, disabled = false, size = "md", colorScheme = "blue", onChange, className = "", orientation = "vertical", }) => {
75
+ const groupClasses = [
76
+ "ui-radio-group",
77
+ `ui-radio-group--${orientation}`,
78
+ className,
79
+ ]
80
+ .filter(Boolean)
81
+ .join(" ");
82
+ return (jsx("div", { className: groupClasses, children: options.map((option, index) => (jsx(Radio, { name: name, value: option.value, checked: value === option.value, disabled: disabled || option.disabled, label: option.label, description: option.description, size: size, colorScheme: colorScheme, onChange: onChange }, `${name}-${option.value}-${index}`))) }));
83
+ };
84
+
85
+ const Text = ({ as = "p", size = "md", weight = "regular", color = "primary", align = "left", className = "", children, style, ...props }) => {
86
+ const Component = as;
87
+ const classes = [
88
+ "ui-text",
89
+ `ui-text--${size}`,
90
+ `ui-text--${weight}`,
91
+ `ui-text--${color}`,
92
+ `ui-text--align-${align}`,
93
+ className,
94
+ ]
95
+ .filter(Boolean)
96
+ .join(" ");
97
+ return (jsx(Component, { className: classes, style: style, ...props, children: children }));
98
+ };
99
+
100
+ const ModalOverlay = ({ onClick, className = "", }) => {
101
+ return (jsx("div", { className: `ui-modal-overlay ${className}`, onClick: onClick, "aria-hidden": "true" }));
102
+ };
103
+ const ModalContent = ({ children, className = "", size = "md", isCentered = true, }) => {
104
+ const contentClasses = [
105
+ "ui-modal-content",
106
+ `ui-modal-content--${size}`,
107
+ isCentered && "ui-modal-content--centered",
108
+ className,
109
+ ]
110
+ .filter(Boolean)
111
+ .join(" ");
112
+ return jsx("div", { className: contentClasses, children: children });
113
+ };
114
+ const ModalHeader = ({ children, className = "", }) => {
115
+ return jsx("div", { className: `ui-modal-header ${className}`, children: children });
116
+ };
117
+ const ModalBody = ({ children, className = "", }) => {
118
+ return jsx("div", { className: `ui-modal-body ${className}`, children: children });
119
+ };
120
+ const ModalFooter = ({ children, className = "", }) => {
121
+ return jsx("div", { className: `ui-modal-footer ${className}`, children: children });
122
+ };
123
+ const ModalCloseButton = ({ onClose, className = "", }) => {
124
+ return (jsx("button", { className: `ui-modal-close-button ${className}`, onClick: onClose, "aria-label": "Close modal", children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
125
+ };
126
+ const Modal = ({ isOpen, onClose, children, size = "md", closeOnOverlayClick = true, closeOnEsc = true, isCentered = true, }) => {
127
+ const modalRef = useRef(null);
128
+ useEffect(() => {
129
+ if (!isOpen)
130
+ return;
131
+ const handleEsc = (e) => {
132
+ if (closeOnEsc && e.key === "Escape") {
133
+ onClose();
134
+ }
135
+ };
136
+ document.addEventListener("keydown", handleEsc);
137
+ document.body.style.overflow = "hidden";
138
+ return () => {
139
+ document.removeEventListener("keydown", handleEsc);
140
+ document.body.style.overflow = "";
141
+ };
142
+ }, [isOpen, closeOnEsc, onClose]);
143
+ if (!isOpen)
144
+ return null;
145
+ const handleOverlayClick = (e) => {
146
+ if (closeOnOverlayClick && e.target === e.currentTarget) {
147
+ onClose();
148
+ }
149
+ };
150
+ return (jsxs("div", { className: "ui-modal", ref: modalRef, children: [jsx(ModalOverlay, { onClick: handleOverlayClick }), jsx(ModalContent, { size: size, isCentered: isCentered, children: children })] }));
151
+ };
152
+
153
+ const normalizeValue$2 = (value) => {
154
+ if (value === undefined)
155
+ return undefined;
156
+ if (typeof value === "number")
157
+ return `${value}px`;
158
+ return value;
159
+ };
160
+ const Box = ({ children, as = "div", className = "", position, top, right, bottom, left, zIndex, display, width, height, minWidth, maxWidth, minHeight, maxHeight, padding, paddingTop, paddingRight, paddingBottom, paddingLeft, margin, marginTop, marginRight, marginBottom, marginLeft, overflow, overflowX, overflowY, color, backgroundColor, bg, opacity, border, borderWidth, borderStyle, borderColor, borderRadius, boxShadow, shadow, fontSize, fontWeight, lineHeight, textAlign, textDecoration, style, ...props }) => {
161
+ const Component = as;
162
+ const boxClasses = ["ui-box", className].filter(Boolean).join(" ");
163
+ const computedStyle = {
164
+ ...(position && { position }),
165
+ ...(top !== undefined && { top: normalizeValue$2(top) }),
166
+ ...(right !== undefined && { right: normalizeValue$2(right) }),
167
+ ...(bottom !== undefined && { bottom: normalizeValue$2(bottom) }),
168
+ ...(left !== undefined && { left: normalizeValue$2(left) }),
169
+ ...(zIndex !== undefined && { zIndex }),
170
+ ...(display && { display }),
171
+ ...(width !== undefined && { width: normalizeValue$2(width) }),
172
+ ...(height !== undefined && { height: normalizeValue$2(height) }),
173
+ ...(minWidth !== undefined && { minWidth: normalizeValue$2(minWidth) }),
174
+ ...(maxWidth !== undefined && { maxWidth: normalizeValue$2(maxWidth) }),
175
+ ...(minHeight !== undefined && { minHeight: normalizeValue$2(minHeight) }),
176
+ ...(maxHeight !== undefined && { maxHeight: normalizeValue$2(maxHeight) }),
177
+ ...(padding !== undefined && { padding: normalizeValue$2(padding) }),
178
+ ...(paddingTop !== undefined && { paddingTop: normalizeValue$2(paddingTop) }),
179
+ ...(paddingRight !== undefined && { paddingRight: normalizeValue$2(paddingRight) }),
180
+ ...(paddingBottom !== undefined && { paddingBottom: normalizeValue$2(paddingBottom) }),
181
+ ...(paddingLeft !== undefined && { paddingLeft: normalizeValue$2(paddingLeft) }),
182
+ ...(margin !== undefined && { margin: normalizeValue$2(margin) }),
183
+ ...(marginTop !== undefined && { marginTop: normalizeValue$2(marginTop) }),
184
+ ...(marginRight !== undefined && { marginRight: normalizeValue$2(marginRight) }),
185
+ ...(marginBottom !== undefined && { marginBottom: normalizeValue$2(marginBottom) }),
186
+ ...(marginLeft !== undefined && { marginLeft: normalizeValue$2(marginLeft) }),
187
+ ...(overflow && { overflow }),
188
+ ...(overflowX && { overflowX }),
189
+ ...(overflowY && { overflowY }),
190
+ ...(color && { color }),
191
+ ...(backgroundColor && { backgroundColor }),
192
+ ...(bg && { backgroundColor: bg }),
193
+ ...(opacity !== undefined && { opacity }),
194
+ ...(border && { border }),
195
+ ...(borderWidth !== undefined && { borderWidth: normalizeValue$2(borderWidth) }),
196
+ ...(borderStyle && { borderStyle }),
197
+ ...(borderColor && { borderColor }),
198
+ ...(borderRadius !== undefined && { borderRadius: normalizeValue$2(borderRadius) }),
199
+ ...(boxShadow && { boxShadow }),
200
+ ...(shadow && { boxShadow: shadow }),
201
+ ...(fontSize !== undefined && { fontSize: normalizeValue$2(fontSize) }),
202
+ ...(fontWeight !== undefined && { fontWeight }),
203
+ ...(lineHeight !== undefined && { lineHeight: normalizeValue$2(lineHeight) }),
204
+ ...(textAlign && { textAlign }),
205
+ ...(textDecoration && { textDecoration }),
206
+ ...style,
207
+ };
208
+ return (jsx(Component, { className: boxClasses, style: computedStyle, ...props, children: children }));
209
+ };
210
+
211
+ const normalizeValue$1 = (value) => {
212
+ if (value === undefined)
213
+ return undefined;
214
+ if (typeof value === "number")
215
+ return `${value}px`;
216
+ return value;
217
+ };
218
+ const mapAlignToFlex = (align) => {
219
+ if (!align)
220
+ return undefined;
221
+ const map = {
222
+ start: "flex-start",
223
+ end: "flex-end",
224
+ center: "center",
225
+ stretch: "stretch",
226
+ baseline: "baseline",
227
+ };
228
+ return map[align];
229
+ };
230
+ const mapJustifyToFlex = (justify) => {
231
+ if (!justify)
232
+ return undefined;
233
+ const map = {
234
+ start: "flex-start",
235
+ end: "flex-end",
236
+ center: "center",
237
+ between: "space-between",
238
+ around: "space-around",
239
+ evenly: "space-evenly",
240
+ };
241
+ return map[justify];
242
+ };
243
+ const Flex = ({ children, direction = "row", align, alignItems, justify, justifyContent, wrap = "nowrap", gap, className = "", position, top, right, bottom, left, zIndex, width, height, minWidth, maxWidth, minHeight, maxHeight, padding, paddingTop, paddingRight, paddingBottom, paddingLeft, margin, marginTop, marginRight, marginBottom, marginLeft, overflow, overflowX, overflowY, color, backgroundColor, bg, opacity, border, borderWidth, borderStyle, borderColor, borderRadius, boxShadow, shadow, fontSize, fontWeight, lineHeight, textAlign, textDecoration, style, ...props }) => {
244
+ const flexClasses = [
245
+ "ui-flex",
246
+ `ui-flex--${direction}`,
247
+ align && `ui-flex--align-${align}`,
248
+ justify && `ui-flex--justify-${justify}`,
249
+ `ui-flex--wrap-${wrap}`,
250
+ className,
251
+ ]
252
+ .filter(Boolean)
253
+ .join(" ");
254
+ const flexStyle = {
255
+ ...(position && { position }),
256
+ ...(top !== undefined && { top: normalizeValue$1(top) }),
257
+ ...(right !== undefined && { right: normalizeValue$1(right) }),
258
+ ...(bottom !== undefined && { bottom: normalizeValue$1(bottom) }),
259
+ ...(left !== undefined && { left: normalizeValue$1(left) }),
260
+ ...(zIndex !== undefined && { zIndex }),
261
+ ...(width !== undefined && { width: normalizeValue$1(width) }),
262
+ ...(height !== undefined && { height: normalizeValue$1(height) }),
263
+ ...(minWidth !== undefined && { minWidth: normalizeValue$1(minWidth) }),
264
+ ...(maxWidth !== undefined && { maxWidth: normalizeValue$1(maxWidth) }),
265
+ ...(minHeight !== undefined && { minHeight: normalizeValue$1(minHeight) }),
266
+ ...(maxHeight !== undefined && { maxHeight: normalizeValue$1(maxHeight) }),
267
+ ...(padding !== undefined && { padding: normalizeValue$1(padding) }),
268
+ ...(paddingTop !== undefined && { paddingTop: normalizeValue$1(paddingTop) }),
269
+ ...(paddingRight !== undefined && {
270
+ paddingRight: normalizeValue$1(paddingRight),
271
+ }),
272
+ ...(paddingBottom !== undefined && {
273
+ paddingBottom: normalizeValue$1(paddingBottom),
274
+ }),
275
+ ...(paddingLeft !== undefined && {
276
+ paddingLeft: normalizeValue$1(paddingLeft),
277
+ }),
278
+ ...(margin !== undefined && { margin: normalizeValue$1(margin) }),
279
+ ...(marginTop !== undefined && { marginTop: normalizeValue$1(marginTop) }),
280
+ ...(marginRight !== undefined && {
281
+ marginRight: normalizeValue$1(marginRight),
282
+ }),
283
+ ...(marginBottom !== undefined && {
284
+ marginBottom: normalizeValue$1(marginBottom),
285
+ }),
286
+ ...(marginLeft !== undefined && { marginLeft: normalizeValue$1(marginLeft) }),
287
+ ...(overflow && { overflow }),
288
+ ...(overflowX && { overflowX }),
289
+ ...(overflowY && { overflowY }),
290
+ ...(color && { color }),
291
+ ...(backgroundColor && { backgroundColor }),
292
+ ...(bg && { backgroundColor: bg }),
293
+ ...(opacity !== undefined && { opacity }),
294
+ ...(border && { border }),
295
+ ...(borderWidth !== undefined && {
296
+ borderWidth: normalizeValue$1(borderWidth),
297
+ }),
298
+ ...(borderStyle && { borderStyle }),
299
+ ...(borderColor && { borderColor }),
300
+ ...(borderRadius !== undefined && {
301
+ borderRadius: normalizeValue$1(borderRadius),
302
+ }),
303
+ ...(boxShadow && { boxShadow }),
304
+ ...(shadow && { boxShadow: shadow }),
305
+ ...(fontSize !== undefined && { fontSize: normalizeValue$1(fontSize) }),
306
+ ...(fontWeight !== undefined && { fontWeight }),
307
+ ...(lineHeight !== undefined && { lineHeight: normalizeValue$1(lineHeight) }),
308
+ ...(textAlign && { textAlign }),
309
+ ...(textDecoration && { textDecoration }),
310
+ // Flexbox specific
311
+ ...(alignItems && { alignItems }),
312
+ ...(align && !alignItems && { alignItems: mapAlignToFlex(align) }),
313
+ ...(justifyContent && { justifyContent }),
314
+ ...(justify &&
315
+ !justifyContent && { justifyContent: mapJustifyToFlex(justify) }),
316
+ ...(gap !== undefined && {
317
+ gap: typeof gap === "number" ? `${gap}px` : gap,
318
+ }),
319
+ ...style,
320
+ };
321
+ return (jsx("div", { className: flexClasses, style: flexStyle, ...props, children: children }));
322
+ };
323
+
324
+ const FormControlContext = createContext({});
325
+ const useFormControlContext = () => useContext(FormControlContext);
326
+ const FormControl = ({ children, isInvalid = false, isRequired = false, isDisabled = false, id, className = "", }) => {
327
+ const contextValue = {
328
+ isInvalid,
329
+ isRequired,
330
+ isDisabled,
331
+ id,
332
+ };
333
+ return (jsx(FormControlContext.Provider, { value: contextValue, children: jsx("div", { className: `ui-form-control ${className}`, children: children }) }));
334
+ };
335
+
336
+ const FormLabel = ({ children, htmlFor, className = "", style, }) => {
337
+ const { isRequired, id } = useFormControlContext();
338
+ const labelId = id ? `${id}-label` : undefined;
339
+ const labelFor = htmlFor || id;
340
+ return (jsxs("label", { htmlFor: labelFor, id: labelId, className: `ui-form-label ${className}`, style: style, children: [children, isRequired && jsx("span", { className: "ui-form-label__required", children: "*" })] }));
341
+ };
342
+
343
+ const FormErrorMessage = ({ children, className = "", style, }) => {
344
+ const { isInvalid, id } = useFormControlContext();
345
+ const errorId = id ? `${id}-error` : undefined;
346
+ if (!isInvalid)
347
+ return null;
348
+ return (jsx("div", { id: errorId, className: `ui-form-error-message ${className}`, role: "alert", style: style, children: children }));
349
+ };
350
+
351
+ const Input = ({ size = "md", variant = "outline", isInvalid, isDisabled, isReadOnly, className = "", style, id, ...props }) => {
352
+ const formControl = useFormControlContext();
353
+ const invalid = isInvalid ?? formControl.isInvalid ?? false;
354
+ const disabled = isDisabled ?? formControl.isDisabled ?? false;
355
+ const inputId = id || formControl.id;
356
+ const inputClasses = [
357
+ "ui-input",
358
+ `ui-input--${size}`,
359
+ `ui-input--${variant}`,
360
+ invalid && "ui-input--invalid",
361
+ disabled && "ui-input--disabled",
362
+ isReadOnly && "ui-input--readonly",
363
+ className,
364
+ ]
365
+ .filter(Boolean)
366
+ .join(" ");
367
+ return (jsx("input", { id: inputId, className: inputClasses, disabled: disabled, readOnly: isReadOnly, "aria-invalid": invalid, "aria-describedby": invalid && formControl.id ? `${formControl.id}-error` : undefined, style: style, ...props }));
368
+ };
369
+
370
+ const Textarea = ({ size = "md", variant = "outline", isInvalid, isDisabled, isReadOnly, resize = "vertical", className = "", style, id, ...props }) => {
371
+ const formControl = useFormControlContext();
372
+ const invalid = isInvalid ?? formControl.isInvalid ?? false;
373
+ const disabled = isDisabled ?? formControl.isDisabled ?? false;
374
+ const textareaId = id || formControl.id;
375
+ const textareaClasses = [
376
+ "ui-textarea",
377
+ `ui-textarea--${size}`,
378
+ `ui-textarea--${variant}`,
379
+ `ui-textarea--resize-${resize}`,
380
+ invalid && "ui-textarea--invalid",
381
+ disabled && "ui-textarea--disabled",
382
+ isReadOnly && "ui-textarea--readonly",
383
+ className,
384
+ ]
385
+ .filter(Boolean)
386
+ .join(" ");
387
+ return (jsx("textarea", { id: textareaId, className: textareaClasses, disabled: disabled, readOnly: isReadOnly, "aria-invalid": invalid, "aria-describedby": invalid && formControl.id ? `${formControl.id}-error` : undefined, style: style, ...props }));
388
+ };
389
+
390
+ const normalizeValue = (value) => {
391
+ if (value === undefined)
392
+ return undefined;
393
+ if (typeof value === "number")
394
+ return `${value}px`;
395
+ return value;
396
+ };
397
+ const Grid = ({ children, templateColumns, templateRows, gap, columnGap, rowGap, autoFlow, autoColumns, autoRows, className = "", position, top, right, bottom, left, zIndex, width, height, minWidth, maxWidth, minHeight, maxHeight, padding, paddingTop, paddingRight, paddingBottom, paddingLeft, margin, marginTop, marginRight, marginBottom, marginLeft, backgroundColor, bg, borderRadius, style, ...props }) => {
398
+ const gridClasses = ["ui-grid", className].filter(Boolean).join(" ");
399
+ const gridStyle = {
400
+ ...(position && { position }),
401
+ ...(top !== undefined && { top: normalizeValue(top) }),
402
+ ...(right !== undefined && { right: normalizeValue(right) }),
403
+ ...(bottom !== undefined && { bottom: normalizeValue(bottom) }),
404
+ ...(left !== undefined && { left: normalizeValue(left) }),
405
+ ...(zIndex !== undefined && { zIndex }),
406
+ ...(width !== undefined && { width: normalizeValue(width) }),
407
+ ...(height !== undefined && { height: normalizeValue(height) }),
408
+ ...(minWidth !== undefined && { minWidth: normalizeValue(minWidth) }),
409
+ ...(maxWidth !== undefined && { maxWidth: normalizeValue(maxWidth) }),
410
+ ...(minHeight !== undefined && { minHeight: normalizeValue(minHeight) }),
411
+ ...(maxHeight !== undefined && { maxHeight: normalizeValue(maxHeight) }),
412
+ ...(padding !== undefined && { padding: normalizeValue(padding) }),
413
+ ...(paddingTop !== undefined && { paddingTop: normalizeValue(paddingTop) }),
414
+ ...(paddingRight !== undefined && {
415
+ paddingRight: normalizeValue(paddingRight),
416
+ }),
417
+ ...(paddingBottom !== undefined && {
418
+ paddingBottom: normalizeValue(paddingBottom),
419
+ }),
420
+ ...(paddingLeft !== undefined && {
421
+ paddingLeft: normalizeValue(paddingLeft),
422
+ }),
423
+ ...(margin !== undefined && { margin: normalizeValue(margin) }),
424
+ ...(marginTop !== undefined && { marginTop: normalizeValue(marginTop) }),
425
+ ...(marginRight !== undefined && { marginRight: normalizeValue(marginRight) }),
426
+ ...(marginBottom !== undefined && {
427
+ marginBottom: normalizeValue(marginBottom),
428
+ }),
429
+ ...(marginLeft !== undefined && { marginLeft: normalizeValue(marginLeft) }),
430
+ ...(backgroundColor && { backgroundColor }),
431
+ ...(bg && { backgroundColor: bg }),
432
+ ...(borderRadius !== undefined && {
433
+ borderRadius: normalizeValue(borderRadius),
434
+ }),
435
+ // Grid specific
436
+ ...(templateColumns && { gridTemplateColumns: templateColumns }),
437
+ ...(templateRows && { gridTemplateRows: templateRows }),
438
+ ...(gap !== undefined && {
439
+ gap: typeof gap === "number" ? `${gap}px` : gap,
440
+ }),
441
+ ...(columnGap !== undefined && {
442
+ columnGap: typeof columnGap === "number" ? `${columnGap}px` : columnGap,
443
+ }),
444
+ ...(rowGap !== undefined && {
445
+ rowGap: typeof rowGap === "number" ? `${rowGap}px` : rowGap,
446
+ }),
447
+ ...(autoFlow && { gridAutoFlow: autoFlow }),
448
+ ...(autoColumns && { gridAutoColumns: autoColumns }),
449
+ ...(autoRows && { gridAutoRows: autoRows }),
450
+ ...style,
451
+ };
452
+ return (jsx("div", { className: gridClasses, style: gridStyle, ...props, children: children }));
453
+ };
454
+
455
+ const Link = ({ children, href, isExternal = false, variant = "link", color = "primary", className = "", style, ...props }) => {
456
+ const linkClasses = [
457
+ "ui-link",
458
+ `ui-link--${variant}`,
459
+ `ui-link--${color}`,
460
+ className,
461
+ ]
462
+ .filter(Boolean)
463
+ .join(" ");
464
+ return (jsx("a", { href: href, className: linkClasses, target: isExternal ? "_blank" : undefined, rel: isExternal ? "noopener noreferrer" : undefined, style: style, ...props, children: children }));
465
+ };
466
+
467
+ const IconButton = ({ icon, size = "md", variant = "ghost", colorScheme, isRound = false, isDisabled = false, className = "", style, ...props }) => {
468
+ const iconButtonClasses = [
469
+ "ui-icon-button",
470
+ `ui-icon-button--${size}`,
471
+ `ui-icon-button--${variant}`,
472
+ colorScheme && `ui-icon-button--${colorScheme}`,
473
+ isRound && "ui-icon-button--round",
474
+ isDisabled && "ui-icon-button--disabled",
475
+ className,
476
+ ]
477
+ .filter(Boolean)
478
+ .join(" ");
479
+ return (jsx("button", { className: iconButtonClasses, disabled: isDisabled, style: style, ...props, children: icon }));
480
+ };
481
+
482
+ const CircularProgress = ({ value = 0, min = 0, max = 100, size = 48, thickness = 4, color = "var(--accent-primary, #bb00ed)", trackColor = "var(--bg-secondary, #f3f4f6)", isIndeterminate = false, className = "", style, children, }) => {
483
+ const normalizedSize = typeof size === "number" ? `${size}px` : size;
484
+ const radius = (parseFloat(normalizedSize) - thickness) / 2;
485
+ const circumference = 2 * Math.PI * radius;
486
+ const normalizedValue = Math.min(Math.max((value - min) / (max - min), 0), 1);
487
+ const strokeDasharray = isIndeterminate ? undefined : circumference;
488
+ const strokeDashoffset = isIndeterminate
489
+ ? undefined
490
+ : circumference * (1 - normalizedValue);
491
+ return (jsxs("div", { className: `ui-circular-progress ${className}`, style: { width: normalizedSize, height: normalizedSize, ...style }, role: "progressbar", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": isIndeterminate ? undefined : value, children: [jsxs("svg", { className: "ui-circular-progress__svg", width: normalizedSize, height: normalizedSize, viewBox: `0 0 ${parseFloat(normalizedSize)} ${parseFloat(normalizedSize)}`, children: [jsx("circle", { className: "ui-circular-progress__track", cx: parseFloat(normalizedSize) / 2, cy: parseFloat(normalizedSize) / 2, r: radius, fill: "none", stroke: trackColor, strokeWidth: thickness }), jsx("circle", { className: `ui-circular-progress__indicator ${isIndeterminate
492
+ ? "ui-circular-progress__indicator--indeterminate"
493
+ : ""}`, cx: parseFloat(normalizedSize) / 2, cy: parseFloat(normalizedSize) / 2, r: radius, fill: "none", stroke: color, strokeWidth: thickness, strokeDasharray: strokeDasharray, strokeDashoffset: strokeDashoffset, strokeLinecap: "round", transform: `rotate(-90 ${parseFloat(normalizedSize) / 2} ${parseFloat(normalizedSize) / 2})` })] }), children && (jsx("div", { className: "ui-circular-progress__label", children: children }))] }));
494
+ };
495
+ const CircularProgressLabel = ({ children, className = "", style, }) => {
496
+ return (jsx("div", { className: `ui-circular-progress-label ${className}`, style: style, children: children }));
497
+ };
498
+
499
+ const useOutsideClick = ({ handler, refs, enabled = true, }) => {
500
+ useEffect(() => {
501
+ if (!enabled)
502
+ return;
503
+ const handleClickOutside = (event) => {
504
+ const target = event.target;
505
+ const isOutside = refs.every((ref) => {
506
+ return ref.current && !ref.current.contains(target);
507
+ });
508
+ if (isOutside) {
509
+ handler(event);
510
+ }
511
+ };
512
+ document.addEventListener("mousedown", handleClickOutside);
513
+ document.addEventListener("touchstart", handleClickOutside);
514
+ return () => {
515
+ document.removeEventListener("mousedown", handleClickOutside);
516
+ document.removeEventListener("touchstart", handleClickOutside);
517
+ };
518
+ }, [handler, refs, enabled]);
519
+ };
520
+
521
+ const Tooltip = ({ children, label, placement = "top", isOpen: controlledIsOpen, defaultIsOpen = false, closeOnClick = false, className = "", style, }) => {
522
+ const [internalIsOpen, setInternalIsOpen] = useState(defaultIsOpen);
523
+ const tooltipRef = useRef(null);
524
+ const triggerRef = useRef(null);
525
+ const isControlled = controlledIsOpen !== undefined;
526
+ const isOpen = isControlled ? controlledIsOpen : internalIsOpen;
527
+ useOutsideClick({
528
+ handler: () => {
529
+ if (!isControlled) {
530
+ setInternalIsOpen(false);
531
+ }
532
+ },
533
+ refs: [tooltipRef],
534
+ enabled: isOpen && !isControlled,
535
+ });
536
+ const handleMouseEnter = () => {
537
+ if (!isControlled) {
538
+ setInternalIsOpen(true);
539
+ }
540
+ };
541
+ const handleMouseLeave = () => {
542
+ if (!isControlled && !closeOnClick) {
543
+ setInternalIsOpen(false);
544
+ }
545
+ };
546
+ const handleClick = () => {
547
+ if (closeOnClick && !isControlled) {
548
+ setInternalIsOpen((prev) => !prev);
549
+ }
550
+ };
551
+ const clonedChild = React.cloneElement(children, {
552
+ ref: triggerRef,
553
+ onMouseEnter: handleMouseEnter,
554
+ onMouseLeave: handleMouseLeave,
555
+ onClick: handleClick,
556
+ });
557
+ return (jsxs("div", { className: `ui-tooltip-wrapper ${className}`, style: style, children: [clonedChild, isOpen && (jsx("div", { ref: tooltipRef, className: `ui-tooltip ui-tooltip--${placement}`, role: "tooltip", children: label }))] }));
558
+ };
559
+
560
+ const PopoverContent = ({ children, className = "", }) => {
561
+ return jsx("div", { className: `ui-popover__content ${className}`, children: children });
562
+ };
563
+ const PopoverHeader = ({ children, className = "", }) => {
564
+ return jsx("div", { className: `ui-popover__header ${className}`, children: children });
565
+ };
566
+ const PopoverBody = ({ children, className = "", }) => {
567
+ return jsx("div", { className: `ui-popover__body ${className}`, children: children });
568
+ };
569
+ const PopoverFooter = ({ children, className = "", }) => {
570
+ return jsx("div", { className: `ui-popover__footer ${className}`, children: children });
571
+ };
572
+ const PopoverCloseButton = ({ onClose, className = "", }) => {
573
+ return (jsx("button", { className: `ui-popover__close ${className}`, onClick: onClose, "aria-label": "Close popover", children: jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M13 1L1 13M1 1L13 13", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
574
+ };
575
+ const Popover = ({ children, content, placement = "bottom", isOpen: controlledIsOpen, defaultIsOpen = false, onOpenChange, trigger = "click", closeOnBlur = true, showArrow = true, className = "", style, }) => {
576
+ const [internalIsOpen, setInternalIsOpen] = useState(defaultIsOpen);
577
+ const popoverRef = useRef(null);
578
+ const triggerRef = useRef(null);
579
+ const isControlled = controlledIsOpen !== undefined;
580
+ const isOpen = isControlled ? controlledIsOpen : internalIsOpen;
581
+ const handleOpenChange = useCallback((open) => {
582
+ if (!isControlled) {
583
+ setInternalIsOpen(open);
584
+ }
585
+ onOpenChange?.(open);
586
+ }, [isControlled, onOpenChange]);
587
+ // Handle outside click
588
+ useEffect(() => {
589
+ if (!isOpen || !closeOnBlur)
590
+ return;
591
+ const handleClickOutside = (event) => {
592
+ const target = event.target;
593
+ // Check if refs are available
594
+ if (!popoverRef.current || !triggerRef.current) {
595
+ return;
596
+ }
597
+ // Check if click is inside popover or trigger
598
+ const clickedInsidePopover = popoverRef.current.contains(target);
599
+ const clickedInsideTrigger = triggerRef.current.contains(target);
600
+ // Close if clicked outside both
601
+ if (!clickedInsidePopover && !clickedInsideTrigger) {
602
+ handleOpenChange(false);
603
+ }
604
+ };
605
+ // Use click event in bubble phase (not capture) so button clicks fire first
606
+ document.addEventListener("click", handleClickOutside, false);
607
+ document.addEventListener("touchend", handleClickOutside, false);
608
+ return () => {
609
+ document.removeEventListener("click", handleClickOutside, false);
610
+ document.removeEventListener("touchend", handleClickOutside, false);
611
+ };
612
+ }, [isOpen, closeOnBlur, handleOpenChange]);
613
+ // Handle click
614
+ const handleClick = () => {
615
+ if (trigger === "click") {
616
+ handleOpenChange(!isOpen);
617
+ }
618
+ };
619
+ // Handle hover
620
+ const handleMouseEnter = () => {
621
+ if (trigger === "hover") {
622
+ handleOpenChange(true);
623
+ }
624
+ };
625
+ const handleMouseLeave = () => {
626
+ if (trigger === "hover") {
627
+ handleOpenChange(false);
628
+ }
629
+ };
630
+ // Clone trigger element
631
+ const triggerElement = React.cloneElement(children, {
632
+ ref: (node) => {
633
+ triggerRef.current = node;
634
+ const originalRef = children.ref;
635
+ if (typeof originalRef === "function") {
636
+ originalRef(node);
637
+ }
638
+ else if (originalRef) {
639
+ try {
640
+ originalRef.current = node;
641
+ }
642
+ catch {
643
+ // Ignore read-only refs
644
+ }
645
+ }
646
+ },
647
+ onClick: trigger === "click" ? handleClick : undefined,
648
+ onMouseEnter: trigger === "hover" ? handleMouseEnter : undefined,
649
+ onMouseLeave: trigger === "hover" ? handleMouseLeave : undefined,
650
+ });
651
+ return (jsxs("div", { className: `ui-popover-wrapper ${className}`, style: style, children: [triggerElement, isOpen && (jsx("div", { ref: popoverRef, className: `ui-popover ui-popover--${placement} ${showArrow ? "ui-popover--with-arrow" : ""}`, role: "dialog", "aria-modal": "false", onClick: (e) => {
652
+ // Stop propagation to prevent outside click handler from firing
653
+ e.stopPropagation();
654
+ }, onMouseDown: (e) => {
655
+ // Stop propagation to prevent outside click handler from firing
656
+ e.stopPropagation();
657
+ }, children: content }))] }));
658
+ };
659
+
660
+ const Toast = ({ id, title, description, variant = "info", duration = 5000, isClosable = true, onClose, className = "", style, }) => {
661
+ const [isVisible, setIsVisible] = useState(false);
662
+ const [isExiting, setIsExiting] = useState(false);
663
+ useEffect(() => {
664
+ // Trigger entrance animation
665
+ setTimeout(() => setIsVisible(true), 10);
666
+ // Auto-close after duration
667
+ if (duration > 0) {
668
+ const timer = setTimeout(() => {
669
+ handleClose();
670
+ }, duration);
671
+ return () => clearTimeout(timer);
672
+ }
673
+ }, [duration]);
674
+ const handleClose = () => {
675
+ setIsExiting(true);
676
+ setTimeout(() => {
677
+ onClose?.(id);
678
+ }, 300); // Match CSS transition duration
679
+ };
680
+ const toastClasses = [
681
+ "ui-toast",
682
+ `ui-toast--${variant}`,
683
+ isVisible && !isExiting ? "ui-toast--visible" : "",
684
+ isExiting ? "ui-toast--exiting" : "",
685
+ className,
686
+ ]
687
+ .filter(Boolean)
688
+ .join(" ");
689
+ return (jsxs("div", { className: toastClasses, style: style, children: [jsxs("div", { className: "ui-toast__content", children: [title && jsx("div", { className: "ui-toast__title", children: title }), description && (jsx("div", { className: "ui-toast__description", children: description }))] }), isClosable && (jsx("button", { className: "ui-toast__close", onClick: handleClose, "aria-label": "Close toast", children: "\u00D7" }))] }));
690
+ };
691
+ const ToastContainer = ({ toasts, position = "top-right", onClose, className = "", style, }) => {
692
+ const containerClasses = [
693
+ "ui-toast-container",
694
+ `ui-toast-container--${position}`,
695
+ className,
696
+ ]
697
+ .filter(Boolean)
698
+ .join(" ");
699
+ return (jsx("div", { className: containerClasses, style: style, children: toasts.map((toast) => (jsx(Toast, { ...toast, onClose: onClose }, toast.id))) }));
700
+ };
701
+
702
+ class ToastManager {
703
+ constructor() {
704
+ this.toasts = [];
705
+ this.listeners = new Set();
706
+ this.defaultPosition = "top-center";
707
+ }
708
+ subscribe(listener) {
709
+ this.listeners.add(listener);
710
+ return () => {
711
+ this.listeners.delete(listener);
712
+ };
713
+ }
714
+ notify() {
715
+ this.listeners.forEach((listener) => listener([...this.toasts]));
716
+ }
717
+ getToasts() {
718
+ return [...this.toasts];
719
+ }
720
+ addToast(message, variant, options = {}) {
721
+ const id = `toast-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
722
+ // Support both 'duration' and 'hideAfter' options
723
+ const duration = options.hideAfter !== undefined
724
+ ? options.hideAfter
725
+ : options.duration ?? 5000;
726
+ // If title is provided, use it; otherwise use message as title
727
+ // If title is provided, use description from options; otherwise no description
728
+ const title = options.title || message;
729
+ const description = options.title ? options.description : undefined;
730
+ const toast = {
731
+ id,
732
+ title,
733
+ description,
734
+ variant,
735
+ duration,
736
+ isClosable: options.isClosable ?? true,
737
+ position: options.position || this.defaultPosition,
738
+ };
739
+ this.toasts.push(toast);
740
+ this.notify();
741
+ return id;
742
+ }
743
+ success(message, options) {
744
+ return this.addToast(message, "success", options);
745
+ }
746
+ error(message, options) {
747
+ return this.addToast(message, "error", options);
748
+ }
749
+ warning(message, options) {
750
+ return this.addToast(message, "warning", options);
751
+ }
752
+ info(message, options) {
753
+ return this.addToast(message, "info", options);
754
+ }
755
+ close(id) {
756
+ this.toasts = this.toasts.filter((toast) => toast.id !== id);
757
+ this.notify();
758
+ }
759
+ closeAll() {
760
+ this.toasts = [];
761
+ this.notify();
762
+ }
763
+ }
764
+ const toastManager = new ToastManager();
765
+
766
+ const ToastStatic = {
767
+ success: (messageOrOptions, options) => {
768
+ if (typeof messageOrOptions === "string") {
769
+ return toastManager.success(messageOrOptions, options);
770
+ }
771
+ else {
772
+ return toastManager.success("", messageOrOptions);
773
+ }
774
+ },
775
+ error: (messageOrOptions, options) => {
776
+ if (typeof messageOrOptions === "string") {
777
+ return toastManager.error(messageOrOptions, options);
778
+ }
779
+ else {
780
+ return toastManager.error("", messageOrOptions);
781
+ }
782
+ },
783
+ warning: (messageOrOptions, options) => {
784
+ if (typeof messageOrOptions === "string") {
785
+ return toastManager.warning(messageOrOptions, options);
786
+ }
787
+ else {
788
+ return toastManager.warning("", messageOrOptions);
789
+ }
790
+ },
791
+ info: (messageOrOptions, options) => {
792
+ if (typeof messageOrOptions === "string") {
793
+ return toastManager.info(messageOrOptions, options);
794
+ }
795
+ else {
796
+ return toastManager.info("", messageOrOptions);
797
+ }
798
+ },
799
+ close: (id) => {
800
+ toastManager.close(id);
801
+ },
802
+ closeAll: () => {
803
+ toastManager.closeAll();
804
+ },
805
+ };
806
+
807
+ const ToastContainerGlobal = () => {
808
+ const [toasts, setToasts] = useState([]);
809
+ useEffect(() => {
810
+ const unsubscribe = toastManager.subscribe((newToasts) => {
811
+ setToasts(newToasts);
812
+ });
813
+ // Set initial toasts
814
+ setToasts(toastManager.getToasts());
815
+ return unsubscribe;
816
+ }, []);
817
+ const handleClose = (id) => {
818
+ toastManager.close(id);
819
+ };
820
+ // Group toasts by position
821
+ const toastsByPosition = useMemo(() => {
822
+ const grouped = {
823
+ "top-left": [],
824
+ "top-right": [],
825
+ "top-center": [],
826
+ "bottom-left": [],
827
+ "bottom-right": [],
828
+ "bottom-center": [],
829
+ };
830
+ toasts.forEach((toast) => {
831
+ const position = toast.position || "top-right";
832
+ if (grouped[position]) {
833
+ grouped[position].push(toast);
834
+ }
835
+ });
836
+ return grouped;
837
+ }, [toasts]);
838
+ // Get all positions that have toasts
839
+ const positionsWithToasts = useMemo(() => {
840
+ return Object.keys(toastsByPosition).filter((position) => toastsByPosition[position].length > 0);
841
+ }, [toastsByPosition]);
842
+ return (jsx(Fragment, { children: positionsWithToasts.map((position) => (jsx(ToastContainer, { toasts: toastsByPosition[position], position: position, onClose: handleClose }, position))) }));
843
+ };
844
+
845
+ const useDisclosure = (defaultIsOpen = false) => {
846
+ const [isOpen, setIsOpen] = useState(defaultIsOpen);
847
+ const onOpen = useCallback(() => {
848
+ setIsOpen(true);
849
+ }, []);
850
+ const onClose = useCallback(() => {
851
+ setIsOpen(false);
852
+ }, []);
853
+ const onToggle = useCallback(() => {
854
+ setIsOpen((prev) => !prev);
855
+ }, []);
856
+ return {
857
+ isOpen,
858
+ onOpen,
859
+ onClose,
860
+ onToggle,
861
+ };
862
+ };
863
+
864
+ const ThemeContext = createContext(undefined);
865
+ const useTheme = () => {
866
+ const context = useContext(ThemeContext);
867
+ if (context === undefined) {
868
+ throw new Error("useTheme must be used within a ThemeProvider");
869
+ }
870
+ return context;
871
+ };
872
+ const ThemeProvider = ({ children, defaultTheme = "system", storageKey = "uilab-theme" }) => {
873
+ const [theme, setTheme] = useState(defaultTheme);
874
+ const [resolvedTheme, setResolvedTheme] = useState("light");
875
+ // Get system preference
876
+ const getSystemTheme = () => {
877
+ if (typeof window !== "undefined") {
878
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
879
+ ? "dark"
880
+ : "light";
881
+ }
882
+ return "light";
883
+ };
884
+ // Apply theme to document
885
+ const applyTheme = (newTheme) => {
886
+ const root = document.documentElement;
887
+ const resolved = newTheme === "system" ? getSystemTheme() : newTheme;
888
+ root.classList.remove("light", "dark");
889
+ root.classList.add(resolved);
890
+ root.setAttribute("data-theme", resolved);
891
+ setResolvedTheme(resolved);
892
+ };
893
+ // Initialize theme from localStorage or use default
894
+ useEffect(() => {
895
+ const savedTheme = localStorage.getItem(storageKey);
896
+ if (savedTheme && ["light", "dark", "system"].includes(savedTheme)) {
897
+ setTheme(savedTheme);
898
+ }
899
+ else {
900
+ setTheme(defaultTheme);
901
+ }
902
+ }, [defaultTheme, storageKey]);
903
+ // Apply theme when theme state changes
904
+ useEffect(() => {
905
+ applyTheme(theme);
906
+ localStorage.setItem(storageKey, theme);
907
+ }, [theme, storageKey]);
908
+ // Listen for system theme changes
909
+ useEffect(() => {
910
+ if (theme === "system") {
911
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
912
+ const handleChange = () => applyTheme("system");
913
+ mediaQuery.addEventListener("change", handleChange);
914
+ return () => mediaQuery.removeEventListener("change", handleChange);
915
+ }
916
+ }, [theme]);
917
+ const value = {
918
+ theme,
919
+ setTheme,
920
+ resolvedTheme,
921
+ };
922
+ return (jsx(ThemeContext.Provider, { value: value, children: children }));
923
+ };
924
+
925
+ const ThemeToggle = ({ className = "", showLabel = true, variant = "dropdown", size = "md", }) => {
926
+ const { theme, setTheme, resolvedTheme } = useTheme();
927
+ const [isOpen, setIsOpen] = useState(false);
928
+ const dropdownRef = useRef(null);
929
+ // Close dropdown when clicking outside
930
+ useEffect(() => {
931
+ const handleClickOutside = (event) => {
932
+ if (dropdownRef.current &&
933
+ !dropdownRef.current.contains(event.target)) {
934
+ setIsOpen(false);
935
+ }
936
+ };
937
+ document.addEventListener("mousedown", handleClickOutside);
938
+ return () => document.removeEventListener("mousedown", handleClickOutside);
939
+ }, []);
940
+ const getThemeIcon = () => {
941
+ if (theme === "system") {
942
+ return "🖥️";
943
+ }
944
+ return theme === "dark" ? "🌙" : "☀️";
945
+ };
946
+ const getThemeLabel = () => {
947
+ switch (theme) {
948
+ case "light":
949
+ return "Light";
950
+ case "dark":
951
+ return "Dark";
952
+ case "system":
953
+ return "System";
954
+ default:
955
+ return "System";
956
+ }
957
+ };
958
+ const handleThemeChange = (newTheme) => {
959
+ setTheme(newTheme);
960
+ setIsOpen(false);
961
+ };
962
+ const handleToggle = () => {
963
+ if (variant === "button") {
964
+ // Cycle through themes: light -> dark -> system -> light
965
+ const themes = [
966
+ "light",
967
+ "dark",
968
+ "system",
969
+ ];
970
+ const currentIndex = themes.indexOf(theme);
971
+ const nextIndex = (currentIndex + 1) % themes.length;
972
+ setTheme(themes[nextIndex]);
973
+ }
974
+ else {
975
+ setIsOpen(!isOpen);
976
+ }
977
+ };
978
+ const sizeClasses = {
979
+ sm: "text-sm px-2 py-1",
980
+ md: "text-base px-3 py-2",
981
+ lg: "text-lg px-4 py-3",
982
+ };
983
+ const baseClasses = `inline-flex items-center gap-2 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 ${sizeClasses[size]} ${className}`;
984
+ if (variant === "button") {
985
+ return (jsxs("button", { onClick: handleToggle, className: baseClasses, title: `Current theme: ${getThemeLabel()}`, children: [jsx("span", { children: getThemeIcon() }), showLabel && jsx("span", { children: getThemeLabel() })] }));
986
+ }
987
+ return (jsxs("div", { className: "relative", ref: dropdownRef, children: [jsxs("button", { onClick: handleToggle, className: baseClasses, title: `Theme: ${getThemeLabel()}`, children: [jsx("span", { children: getThemeIcon() }), showLabel && jsx("span", { children: getThemeLabel() }), jsx("span", { className: "ml-1", children: "\u25BC" })] }), isOpen && (jsxs("div", { className: "absolute right-0 mt-2 w-48 rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 dark:bg-gray-800 dark:ring-gray-700", children: [jsx("button", { className: `block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700 ${theme === "light" ? "bg-gray-100 dark:bg-gray-700" : ""}`, onClick: () => handleThemeChange("light"), children: "\u2600\uFE0F Light" }), jsx("button", { className: `block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700 ${theme === "dark" ? "bg-gray-100 dark:bg-gray-700" : ""}`, onClick: () => handleThemeChange("dark"), children: "\uD83C\uDF19 Dark" }), jsxs("button", { className: `block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700 ${theme === "system" ? "bg-gray-100 dark:bg-gray-700" : ""}`, onClick: () => handleThemeChange("system"), children: ["\uD83D\uDDA5\uFE0F System", theme === "system" && (jsxs("span", { className: "ml-2 text-xs text-gray-500", children: ["(", resolvedTheme === "dark" ? "Dark" : "Light", ")"] }))] })] }))] }));
988
+ };
989
+
990
+ /**
991
+ * ThemeScript - Blocking script to prevent flash of light content in dark mode
992
+ *
993
+ * Add this component to your Next.js layout.tsx file before the ThemeProvider.
994
+ * Make sure to add `suppressHydrationWarning` to both `<html>` and `<body>` tags
995
+ * to prevent hydration mismatches.
996
+ *
997
+ * @example
998
+ * ```tsx
999
+ * import { ThemeScript, ThemeProvider } from 'uiplex';
1000
+ *
1001
+ * export default function RootLayout({ children }) {
1002
+ * return (
1003
+ * <html suppressHydrationWarning>
1004
+ * <body suppressHydrationWarning>
1005
+ * <ThemeScript />
1006
+ * <ThemeProvider>
1007
+ * {children}
1008
+ * </ThemeProvider>
1009
+ * </body>
1010
+ * </html>
1011
+ * );
1012
+ * }
1013
+ * ```
1014
+ */
1015
+ const ThemeScript = ({ storageKey = "uiplex-theme", defaultTheme = "system" }) => {
1016
+ return (jsx("script", { dangerouslySetInnerHTML: {
1017
+ __html: `
1018
+ (function() {
1019
+ try {
1020
+ // Disable transitions immediately to prevent flash
1021
+ var noTransitionStyle = document.createElement('style');
1022
+ noTransitionStyle.textContent = '* { transition: none !important; }';
1023
+ document.head.appendChild(noTransitionStyle);
1024
+
1025
+ var theme = localStorage.getItem('${storageKey}') || '${defaultTheme}';
1026
+ var resolvedTheme = theme;
1027
+
1028
+ if (theme === 'system') {
1029
+ resolvedTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
1030
+ }
1031
+
1032
+ document.documentElement.classList.remove('light', 'dark');
1033
+ document.documentElement.classList.add(resolvedTheme);
1034
+ document.documentElement.setAttribute('data-theme', resolvedTheme);
1035
+
1036
+ // Set initial background color to prevent flash - use !important
1037
+ if (resolvedTheme === 'dark') {
1038
+ var darkStyle = document.createElement('style');
1039
+ darkStyle.textContent = 'html, body { background-color: #111827 !important; color: #f9fafb !important; }';
1040
+ document.head.appendChild(darkStyle);
1041
+ }
1042
+
1043
+ // Re-enable transitions after a short delay
1044
+ setTimeout(function() {
1045
+ noTransitionStyle.remove();
1046
+ }, 100);
1047
+ } catch (e) {
1048
+ document.documentElement.classList.add('light');
1049
+ document.documentElement.setAttribute('data-theme', 'light');
1050
+ }
1051
+ })();
1052
+ `,
1053
+ } }));
1054
+ };
1055
+
1056
+ export { Box, Button, CircularProgress, CircularProgressLabel, Flex, FormControl, FormErrorMessage, FormLabel, Grid, IconButton, Input, Link, Loader, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Popover, PopoverBody, PopoverCloseButton, PopoverContent, PopoverFooter, PopoverHeader, Radio, RadioGroup, Text, Textarea, ThemeProvider, ThemeScript, ThemeToggle, ToastStatic as Toast, Toast as ToastComponent, ToastContainer, ToastContainerGlobal, Tooltip, useDisclosure, useOutsideClick, useTheme };