@neuctra/ui 0.2.1 → 0.2.3

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 (98) hide show
  1. package/dist/components/basic/Accordation.d.ts +27 -18
  2. package/dist/components/basic/Alert.d.ts +15 -2
  3. package/dist/components/basic/Avatar.d.ts +5 -3
  4. package/dist/components/basic/Badge.d.ts +3 -3
  5. package/dist/components/basic/Button.d.ts +15 -17
  6. package/dist/components/basic/Card.d.ts +7 -49
  7. package/dist/components/basic/CheckRadioInput.d.ts +3 -1
  8. package/dist/components/basic/Container.d.ts +28 -26
  9. package/dist/components/basic/Drawer.d.ts +20 -11
  10. package/dist/components/basic/Flexbox.d.ts +18 -10
  11. package/dist/components/basic/GridView.d.ts +7 -5
  12. package/dist/components/basic/Image.d.ts +31 -6
  13. package/dist/components/basic/Input.d.ts +18 -10
  14. package/dist/components/basic/List.d.ts +11 -3
  15. package/dist/components/basic/Modal.d.ts +15 -2
  16. package/dist/components/basic/Section.d.ts +36 -0
  17. package/dist/components/basic/Stack.d.ts +27 -0
  18. package/dist/components/basic/Table.d.ts +18 -54
  19. package/dist/components/basic/Tabs.d.ts +28 -28
  20. package/dist/components/basic/Text.d.ts +19 -32
  21. package/dist/index.cjs.js +68 -223
  22. package/dist/index.cjs.js.map +1 -0
  23. package/dist/index.d.ts +17 -19
  24. package/dist/index.es.js +3169 -6313
  25. package/dist/index.es.js.map +1 -0
  26. package/dist/src/components/avatar/AvatarGroup.js +9 -0
  27. package/dist/src/components/avatar/AvatarWithStatus.js +18 -0
  28. package/dist/src/components/basic/Accordation.js +74 -0
  29. package/dist/src/components/basic/Alert.js +126 -0
  30. package/dist/src/components/basic/AudioGallery.js +425 -0
  31. package/dist/src/components/basic/AudioPlayer.js +116 -0
  32. package/dist/src/components/basic/Avatar.js +181 -0
  33. package/dist/src/components/basic/Badge.js +66 -0
  34. package/dist/src/components/basic/Button.js +101 -0
  35. package/dist/src/components/basic/Card.js +45 -0
  36. package/dist/src/components/basic/CheckRadioInput.js +83 -0
  37. package/dist/src/components/basic/Container.js +45 -0
  38. package/dist/src/components/basic/Drawer.js +94 -0
  39. package/dist/src/components/basic/DropDown.js +316 -0
  40. package/dist/src/components/basic/Flexbox.js +67 -0
  41. package/dist/src/components/basic/GridView.js +51 -0
  42. package/dist/src/components/basic/Image.js +95 -0
  43. package/dist/src/components/basic/Input.js +123 -0
  44. package/dist/src/components/basic/List.js +71 -0
  45. package/dist/src/components/basic/Modal.js +88 -0
  46. package/dist/src/components/basic/Section.js +100 -0
  47. package/dist/src/components/basic/Stack.js +75 -0
  48. package/dist/src/components/basic/Table.js +32 -0
  49. package/dist/src/components/basic/Tabs.js +149 -0
  50. package/dist/src/components/basic/Text.js +117 -0
  51. package/dist/src/index.js +44 -0
  52. package/dist/types/src/components/basic/Accordation.d.ts +44 -0
  53. package/dist/types/{components → src/components}/basic/Alert.d.ts +15 -2
  54. package/dist/types/{components → src/components}/basic/Avatar.d.ts +5 -3
  55. package/dist/types/{components → src/components}/basic/Badge.d.ts +3 -3
  56. package/dist/types/src/components/basic/Button.d.ts +26 -0
  57. package/dist/types/src/components/basic/Card.d.ts +28 -0
  58. package/dist/types/{components → src/components}/basic/CheckRadioInput.d.ts +3 -1
  59. package/dist/types/src/components/basic/Container.d.ts +32 -0
  60. package/dist/types/src/components/basic/Drawer.d.ts +33 -0
  61. package/dist/types/src/components/basic/Flexbox.d.ts +25 -0
  62. package/dist/types/{components → src/components}/basic/GridView.d.ts +7 -5
  63. package/dist/types/src/components/basic/Image.d.ts +58 -0
  64. package/dist/types/{components → src/components}/basic/Input.d.ts +18 -10
  65. package/dist/types/{components → src/components}/basic/List.d.ts +11 -3
  66. package/dist/types/src/components/basic/Modal.d.ts +24 -0
  67. package/dist/types/src/components/basic/Section.d.ts +36 -0
  68. package/dist/types/src/components/basic/Stack.d.ts +27 -0
  69. package/dist/types/src/components/basic/Table.d.ts +23 -0
  70. package/dist/types/src/components/basic/Tabs.d.ts +47 -0
  71. package/dist/types/src/components/basic/Text.d.ts +26 -0
  72. package/dist/types/{index.d.ts → src/index.d.ts} +17 -19
  73. package/dist/types/vite.config.d.ts +2 -0
  74. package/dist/ui.css +1 -1
  75. package/dist/vite.config.js +34 -0
  76. package/package.json +2 -1
  77. package/dist/components/basic/ImageGallery.d.ts +0 -21
  78. package/dist/components/basic/VideoGallery.d.ts +0 -136
  79. package/dist/components/basic/VideoPlayer.d.ts +0 -36
  80. package/dist/types/components/basic/Accordation.d.ts +0 -35
  81. package/dist/types/components/basic/Button.d.ts +0 -28
  82. package/dist/types/components/basic/Card.d.ts +0 -70
  83. package/dist/types/components/basic/Container.d.ts +0 -30
  84. package/dist/types/components/basic/Drawer.d.ts +0 -24
  85. package/dist/types/components/basic/Flexbox.d.ts +0 -17
  86. package/dist/types/components/basic/Image.d.ts +0 -33
  87. package/dist/types/components/basic/ImageGallery.d.ts +0 -21
  88. package/dist/types/components/basic/Modal.d.ts +0 -11
  89. package/dist/types/components/basic/Table.d.ts +0 -59
  90. package/dist/types/components/basic/Tabs.d.ts +0 -47
  91. package/dist/types/components/basic/Text.d.ts +0 -39
  92. package/dist/types/components/basic/VideoGallery.d.ts +0 -136
  93. package/dist/types/components/basic/VideoPlayer.d.ts +0 -36
  94. /package/dist/types/{components → src/components}/avatar/AvatarGroup.d.ts +0 -0
  95. /package/dist/types/{components → src/components}/avatar/AvatarWithStatus.d.ts +0 -0
  96. /package/dist/types/{components → src/components}/basic/AudioGallery.d.ts +0 -0
  97. /package/dist/types/{components → src/components}/basic/AudioPlayer.d.ts +0 -0
  98. /package/dist/types/{components → src/components}/basic/DropDown.d.ts +0 -0
@@ -0,0 +1,94 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from "react";
3
+ import { X } from "lucide-react";
4
+ export const DrawerButton = ({ label = "Open Drawer", icon, iconPosition = "left", onClick, color = "#2563eb", textColor = "#fff", borderRadius = "6px", padding = "10px 16px", fontSize = "14px", gap = "8px", style, className = "", }) => (_jsxs("button", { onClick: onClick, style: {
5
+ display: "inline-flex",
6
+ alignItems: "center",
7
+ justifyContent: "center",
8
+ backgroundColor: color,
9
+ color: textColor,
10
+ border: "none",
11
+ borderRadius,
12
+ padding,
13
+ fontSize,
14
+ gap,
15
+ cursor: "pointer",
16
+ fontWeight: 500,
17
+ transition: "all 0.2s ease",
18
+ boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
19
+ ...style,
20
+ }, className: className, children: [icon && iconPosition === "left" && icon, label, icon && iconPosition === "right" && icon] }));
21
+ export const Drawer = ({ open, onClose, position = "right", width = "320px", height = "320px", backgroundColor = "#fff", backdropColor = "rgba(0,0,0,0.5)", transitionDuration = 300, style, className = "", children, showCloseButton = true, closeIconColor = "#000", closeButtonStyle, }) => {
22
+ const [visible, setVisible] = useState(open);
23
+ // Handle mount/unmount delay for smooth fade-out
24
+ useEffect(() => {
25
+ if (open)
26
+ setVisible(true);
27
+ else
28
+ setTimeout(() => setVisible(false), transitionDuration);
29
+ }, [open, transitionDuration]);
30
+ // Drawer transform direction
31
+ const transform = useMemo(() => {
32
+ if (open)
33
+ return "translate(0, 0)";
34
+ switch (position) {
35
+ case "left":
36
+ return "translateX(-100%)";
37
+ case "right":
38
+ return "translateX(100%)";
39
+ case "top":
40
+ return "translateY(-100%)";
41
+ case "bottom":
42
+ return "translateY(100%)";
43
+ default:
44
+ return "translate(0, 0)";
45
+ }
46
+ }, [open, position]);
47
+ const drawerStyle = {
48
+ position: "fixed",
49
+ backgroundColor,
50
+ transition: `transform ${transitionDuration}ms ease, opacity ${transitionDuration}ms ease`,
51
+ transform,
52
+ opacity: open ? 1 : 0,
53
+ zIndex: 1001,
54
+ ...style,
55
+ ...(position === "left" || position === "right"
56
+ ? { top: 0, bottom: 0, [position]: 0, width, height: "100%" }
57
+ : { left: 0, right: 0, [position]: 0, height, width: "100%" }),
58
+ };
59
+ const overlayStyle = {
60
+ position: "fixed",
61
+ inset: 0,
62
+ backgroundColor: backdropColor,
63
+ opacity: open ? 1 : 0,
64
+ transition: `opacity ${transitionDuration}ms ease`,
65
+ zIndex: 1000,
66
+ display: visible ? "block" : "none",
67
+ pointerEvents: open ? "auto" : "none",
68
+ };
69
+ const defaultCloseButtonStyle = {
70
+ position: "absolute",
71
+ top: "12px",
72
+ right: "12px",
73
+ background: "none",
74
+ border: "none",
75
+ cursor: "pointer",
76
+ display: "flex",
77
+ alignItems: "center",
78
+ justifyContent: "center",
79
+ transition: "transform 0.2s ease, opacity 0.2s ease",
80
+ };
81
+ return (_jsxs(_Fragment, { children: [_jsx("div", { style: overlayStyle, onClick: onClose }), _jsxs("div", { style: {
82
+ ...drawerStyle,
83
+ display: "flex",
84
+ flexDirection: "column",
85
+ visibility: visible ? "visible" : "hidden",
86
+ pointerEvents: open ? "auto" : "none",
87
+ boxShadow: "0 0 20px rgba(0,0,0,0.15)",
88
+ }, className: className, children: [showCloseButton && (_jsx("button", { onClick: onClose, style: { ...defaultCloseButtonStyle, ...closeButtonStyle }, "aria-label": "Close drawer", children: _jsx(X, { size: 22, color: closeIconColor }) })), _jsx("div", { style: {
89
+ flex: 1,
90
+ overflowY: "auto",
91
+ padding: "16px",
92
+ scrollbarWidth: "thin",
93
+ }, children: children })] })] }));
94
+ };
@@ -0,0 +1,316 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback } from "react";
3
+ export const Dropdown = ({ options, value, defaultValue, onChange, placeholder = "Select an option", disabled = false, searchable = false, multiSelect = false, clearable = false, virtualized = false, optionHeight = 36, visibleOptions = 5,
4
+ // Styling defaults
5
+ width = "100%", height = "auto", borderColor = "#d1d5db", focusBorderColor = "#2563eb", errorBorderColor = "#dc2626", backgroundColor = "#ffffff", textColor = "#111827", placeholderColor = "#9ca3af", hoverColor = "#f3f4f6", selectedColor = "#eff6ff", disabledColor = "#f3f4f6", padding = "0.5rem 0.75rem", margin = "0", borderRadius = "0.375rem", boxShadow = "0 1px 2px 0 rgba(0, 0, 0, 0.05)", optionPadding = "0.5rem 0.75rem", optionGap = "0.5rem", transitionDuration = "200ms", dropdownMaxHeight = "300px", dropdownMinWidth = "100%",
6
+ // Custom classes
7
+ className = "", dropdownClassName = "", optionClassName = "", inputClassName = "",
8
+ // Custom styles
9
+ style, dropdownStyle, optionStyle, inputStyle,
10
+ // Icons
11
+ iconPrefix, iconSuffix, clearIcon = "×", dropdownIcon = "▼", checkIcon = "✓",
12
+ // Accessibility
13
+ ariaLabel, ariaLabelledby, ariaDescribedby,
14
+ // Callbacks
15
+ onFocus, onBlur, onOpen, onClose, }) => {
16
+ const [selectedValues, setSelectedValues] = useState([]);
17
+ const [isOpen, setIsOpen] = useState(false);
18
+ const [searchTerm, setSearchTerm] = useState("");
19
+ const [focusedIndex, setFocusedIndex] = useState(null);
20
+ const dropdownRef = useRef(null);
21
+ const inputRef = useRef(null);
22
+ const optionsRef = useRef([]);
23
+ // Initialize selected values
24
+ useEffect(() => {
25
+ if (value) {
26
+ setSelectedValues(multiSelect ? value.split(",") : [value]);
27
+ }
28
+ else if (defaultValue) {
29
+ setSelectedValues(multiSelect ? defaultValue.split(",") : [defaultValue]);
30
+ }
31
+ else {
32
+ setSelectedValues([]);
33
+ }
34
+ }, [value, defaultValue, multiSelect]);
35
+ // Filter options based on search term
36
+ const filteredOptions = searchable
37
+ ? options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()))
38
+ : options;
39
+ // Handle click outside
40
+ useEffect(() => {
41
+ const handleClickOutside = (event) => {
42
+ if (dropdownRef.current &&
43
+ !dropdownRef.current.contains(event.target)) {
44
+ setIsOpen(false);
45
+ onClose?.();
46
+ }
47
+ };
48
+ document.addEventListener("mousedown", handleClickOutside);
49
+ return () => document.removeEventListener("mousedown", handleClickOutside);
50
+ }, [onClose]);
51
+ // Keyboard navigation
52
+ useEffect(() => {
53
+ const handleKeyDown = (event) => {
54
+ if (!isOpen)
55
+ return;
56
+ switch (event.key) {
57
+ case "ArrowDown":
58
+ event.preventDefault();
59
+ setFocusedIndex((prev) => {
60
+ const nextIndex = prev === null
61
+ ? 0
62
+ : Math.min(prev + 1, filteredOptions.length - 1);
63
+ scrollToOption(nextIndex);
64
+ return nextIndex;
65
+ });
66
+ break;
67
+ case "ArrowUp":
68
+ event.preventDefault();
69
+ setFocusedIndex((prev) => {
70
+ const nextIndex = prev === null ? 0 : Math.max(prev - 1, 0);
71
+ scrollToOption(nextIndex);
72
+ return nextIndex;
73
+ });
74
+ break;
75
+ case "Enter":
76
+ event.preventDefault();
77
+ if (focusedIndex !== null) {
78
+ handleSelect(filteredOptions[focusedIndex].value);
79
+ }
80
+ break;
81
+ case "Escape":
82
+ event.preventDefault();
83
+ setIsOpen(false);
84
+ onClose?.();
85
+ break;
86
+ case "Tab":
87
+ setIsOpen(false);
88
+ onClose?.();
89
+ break;
90
+ }
91
+ };
92
+ document.addEventListener("keydown", handleKeyDown);
93
+ return () => document.removeEventListener("keydown", handleKeyDown);
94
+ }, [isOpen, focusedIndex, filteredOptions]);
95
+ const scrollToOption = useCallback((index) => {
96
+ optionsRef.current[index]?.scrollIntoView({
97
+ block: "nearest",
98
+ behavior: "smooth",
99
+ });
100
+ }, []);
101
+ const handleSelect = (value) => {
102
+ let newSelectedValues;
103
+ if (multiSelect) {
104
+ newSelectedValues = selectedValues.includes(value)
105
+ ? selectedValues.filter((v) => v !== value)
106
+ : [...selectedValues, value];
107
+ }
108
+ else {
109
+ newSelectedValues = [value];
110
+ setIsOpen(false);
111
+ onClose?.();
112
+ }
113
+ setSelectedValues(newSelectedValues);
114
+ onChange?.(multiSelect ? newSelectedValues.join(",") : value);
115
+ };
116
+ const handleClear = (e) => {
117
+ e.stopPropagation();
118
+ setSelectedValues([]);
119
+ onChange?.("");
120
+ setSearchTerm("");
121
+ };
122
+ const toggleDropdown = () => {
123
+ if (disabled)
124
+ return;
125
+ const newState = !isOpen;
126
+ setIsOpen(newState);
127
+ if (newState) {
128
+ onOpen?.();
129
+ if (searchable) {
130
+ setTimeout(() => inputRef.current?.focus(), 0);
131
+ }
132
+ }
133
+ else {
134
+ onClose?.();
135
+ }
136
+ };
137
+ const selectedOption = options.find((option) => option.value === selectedValues[0]);
138
+ const selectedOptions = options.filter((option) => selectedValues.includes(option.value));
139
+ // Virtualization setup
140
+ const [startIndex, setStartIndex] = useState(0);
141
+ const visibleOptionCount = Math.min(visibleOptions, filteredOptions.length);
142
+ const endIndex = Math.min(startIndex + visibleOptionCount, filteredOptions.length);
143
+ const visibleOptionsList = virtualized
144
+ ? filteredOptions.slice(startIndex, endIndex)
145
+ : filteredOptions;
146
+ return (_jsxs("div", { ref: dropdownRef, className: `dropdown-container ${className}`, style: {
147
+ position: "relative",
148
+ width,
149
+ margin,
150
+ fontFamily: "'Inter', sans-serif",
151
+ ...style,
152
+ }, children: [_jsx("style", { children: `
153
+ .dropdown-container {
154
+ --border-color: ${borderColor};
155
+ --focus-border-color: ${focusBorderColor};
156
+ --error-border-color: ${errorBorderColor};
157
+ --bg-color: ${backgroundColor};
158
+ --text-color: ${textColor};
159
+ --placeholder-color: ${placeholderColor};
160
+ --hover-color: ${hoverColor};
161
+ --selected-color: ${selectedColor};
162
+ --disabled-color: ${disabledColor};
163
+ --transition-duration: ${transitionDuration};
164
+ }
165
+ ` }), _jsxs("div", { role: "button", onClick: toggleDropdown, "aria-disabled": disabled, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, className: `dropdown-control ${inputClassName}`, style: {
166
+ width: "100%",
167
+ minHeight: height,
168
+ padding,
169
+ backgroundColor: disabled ? disabledColor : backgroundColor,
170
+ color: textColor,
171
+ borderWidth: "1px",
172
+ borderStyle: "solid",
173
+ borderColor: isOpen ? focusBorderColor : borderColor,
174
+ borderRadius,
175
+ boxShadow,
176
+ display: "flex",
177
+ alignItems: "center",
178
+ justifyContent: "space-between",
179
+ cursor: disabled ? "not-allowed" : "pointer",
180
+ opacity: disabled ? 0.7 : 1,
181
+ transition: `all ${transitionDuration} ease-in-out`,
182
+ textAlign: "left",
183
+ ...inputStyle,
184
+ ...(isOpen && {
185
+ boxShadow: `0 0 0 1px ${focusBorderColor}`,
186
+ }),
187
+ }, children: [_jsxs("div", { style: {
188
+ display: "flex",
189
+ alignItems: "center",
190
+ gap: optionGap,
191
+ flex: 1,
192
+ overflow: "hidden",
193
+ }, children: [iconPrefix && (_jsx("span", { className: "dropdown-icon-prefix", style: { flexShrink: 0 }, children: iconPrefix })), multiSelect ? (_jsx("div", { style: {
194
+ display: "flex",
195
+ gap: "0.25rem",
196
+ flexWrap: "wrap",
197
+ flex: 1,
198
+ overflow: "hidden",
199
+ }, children: selectedOptions.length > 0 ? (selectedOptions.map((option) => (_jsxs("span", { style: {
200
+ backgroundColor: selectedColor,
201
+ padding: "0.25rem 0.5rem",
202
+ borderRadius: "0.25rem",
203
+ fontSize: "0.875rem",
204
+ display: "flex",
205
+ alignItems: "center",
206
+ gap: "0.25rem",
207
+ flexShrink: 0,
208
+ }, children: [option.icon && (_jsx("span", { style: { flexShrink: 0 }, children: option.icon })), _jsx("span", { style: {
209
+ whiteSpace: "nowrap",
210
+ overflow: "hidden",
211
+ textOverflow: "ellipsis",
212
+ }, children: option.label })] }, option.value)))) : (_jsx("span", { style: { color: placeholderColor }, children: placeholder })) })) : (_jsxs("span", { style: {
213
+ color: selectedOption ? textColor : placeholderColor,
214
+ overflow: "hidden",
215
+ textOverflow: "ellipsis",
216
+ whiteSpace: "nowrap",
217
+ display: "flex",
218
+ alignItems: "center",
219
+ gap: optionGap,
220
+ }, children: [selectedOption?.icon && (_jsx("span", { style: { flexShrink: 0 }, children: selectedOption.icon })), selectedOption ? selectedOption.label : placeholder] }))] }), _jsxs("div", { style: {
221
+ display: "flex",
222
+ alignItems: "center",
223
+ gap: "0.5rem",
224
+ marginLeft: "0.5rem",
225
+ flexShrink: 0,
226
+ }, children: [clearable && selectedValues.length > 0 && (_jsx("span", { onClick: handleClear, style: {
227
+ cursor: disabled ? "not-allowed" : "pointer",
228
+ fontSize: "1rem",
229
+ color: textColor,
230
+ opacity: 0.7,
231
+ display: "flex",
232
+ alignItems: "center",
233
+ justifyContent: "center",
234
+ }, "aria-label": "Clear selection", children: clearIcon })), _jsx("span", { style: {
235
+ transition: `transform ${transitionDuration}`,
236
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)",
237
+ fontSize: "0.75rem",
238
+ color: textColor,
239
+ opacity: 0.7,
240
+ }, children: dropdownIcon })] })] }), isOpen && (_jsxs("div", { className: `dropdown-menu ${dropdownClassName}`, style: {
241
+ position: "absolute",
242
+ top: "100%",
243
+ left: 0,
244
+ zIndex: 1000,
245
+ width: "100%",
246
+ minWidth: dropdownMinWidth,
247
+ maxHeight: dropdownMaxHeight,
248
+ overflowY: "auto",
249
+ backgroundColor,
250
+ border: `1px solid ${borderColor}`,
251
+ borderRadius,
252
+ boxShadow: `0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)`,
253
+ marginTop: "0.25rem",
254
+ transition: `opacity ${transitionDuration}, transform ${transitionDuration}`,
255
+ opacity: 0,
256
+ transform: "translateY(-0.5rem)",
257
+ animation: `dropdownFadeIn ${transitionDuration} ease-out forwards`,
258
+ ...dropdownStyle,
259
+ }, role: "listbox", "aria-multiselectable": multiSelect, children: [searchable && (_jsx("div", { style: {
260
+ padding: "0.5rem",
261
+ borderBottom: `1px solid ${borderColor}`,
262
+ }, children: _jsx("input", { ref: inputRef, type: "text", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: "Search...", style: {
263
+ width: "100%",
264
+ padding: "0.5rem 0.75rem",
265
+ borderWidth: "1px",
266
+ borderStyle: "solid",
267
+ color: textColor,
268
+ borderColor: isOpen ? focusBorderColor : borderColor,
269
+ borderRadius: "0.25rem",
270
+ outline: "none",
271
+ transition: `border-color ${transitionDuration}`,
272
+ ...(isOpen && {
273
+ boxShadow: `0 0 0 1px ${focusBorderColor}`,
274
+ }),
275
+ }, onFocus: onFocus, onBlur: onBlur }) })), _jsx("ul", { style: { margin: 0, padding: "0.25rem 0", listStyle: "none" }, children: filteredOptions.length > 0 ? (visibleOptionsList.map((option, index) => {
276
+ const isSelected = selectedValues.includes(option.value);
277
+ const isFocused = focusedIndex === (virtualized ? startIndex + index : index);
278
+ const isDisabled = option.disabled;
279
+ return (_jsxs("li", { ref: (el) => {
280
+ if (el) {
281
+ const refIndex = virtualized
282
+ ? startIndex + index
283
+ : index;
284
+ optionsRef.current[refIndex] = el;
285
+ }
286
+ }, onClick: () => !isDisabled && handleSelect(option.value), onMouseEnter: () => !isDisabled &&
287
+ setFocusedIndex(virtualized ? startIndex + index : index), className: `dropdown-option ${optionClassName} ${isDisabled ? "disabled" : ""}`, style: {
288
+ padding: optionPadding,
289
+ cursor: isDisabled ? "not-allowed" : "pointer",
290
+ backgroundColor: isSelected
291
+ ? selectedColor
292
+ : isFocused
293
+ ? hoverColor
294
+ : backgroundColor,
295
+ color: isDisabled ? disabledColor : textColor,
296
+ display: "flex",
297
+ alignItems: "center",
298
+ gap: optionGap,
299
+ transition: `background-color ${transitionDuration}`,
300
+ ...optionStyle,
301
+ }, role: "option", "aria-selected": isSelected, "aria-disabled": isDisabled, children: [multiSelect && (_jsx("span", { style: { flexShrink: 0 }, children: isSelected ? checkIcon : "○" })), option.icon && (_jsx("span", { style: { flexShrink: 0 }, children: option.icon })), _jsx("span", { style: { flex: 1 }, children: option.label })] }, option.value));
302
+ })) : (_jsx("li", { style: {
303
+ padding: optionPadding,
304
+ color: placeholderColor,
305
+ textAlign: "center",
306
+ }, children: "No options found" })) }), virtualized && filteredOptions.length > visibleOptionCount && (_jsx("div", { style: {
307
+ height: `${(filteredOptions.length - visibleOptionCount) * optionHeight}px`,
308
+ } }))] })), _jsx("style", { children: `
309
+ @keyframes dropdownFadeIn {
310
+ to {
311
+ opacity: 1;
312
+ transform: translateY(0);
313
+ }
314
+ }
315
+ ` })] }));
316
+ };
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from "react";
3
+ const getScreenSize = (width) => {
4
+ if (width < 768)
5
+ return "sm";
6
+ if (width < 1024)
7
+ return "md";
8
+ return "lg";
9
+ };
10
+ const resolveResponsive = (prop, screen, fallback) => {
11
+ if (prop == null)
12
+ return fallback;
13
+ if (typeof prop !== "object")
14
+ return prop;
15
+ return prop[screen] ?? fallback;
16
+ };
17
+ export const Flexbox = ({ direction = { sm: "column", md: "row", lg: "row" }, align = "center", justify = "space-between", wrap = "wrap", gap = 16, padding, margin, backgroundColor = "transparent", width = "100%", maxWidth = "100%", height = "auto", borderRadius, border, boxShadow, overflow, children, style, className, }) => {
18
+ const [screen, setScreen] = useState("lg");
19
+ useEffect(() => {
20
+ const updateScreen = () => setScreen(getScreenSize(window.innerWidth));
21
+ updateScreen();
22
+ window.addEventListener("resize", updateScreen);
23
+ return () => window.removeEventListener("resize", updateScreen);
24
+ }, []);
25
+ const computedStyle = useMemo(() => {
26
+ const toCssValue = (val) => typeof val === "number" ? `${val}px` : val;
27
+ return {
28
+ display: "flex",
29
+ flexDirection: resolveResponsive(direction, screen, "row"),
30
+ alignItems: resolveResponsive(align, screen, "center"),
31
+ justifyContent: resolveResponsive(justify, screen, "flex-start"),
32
+ flexWrap: resolveResponsive(wrap, screen, "wrap"),
33
+ gap: toCssValue(resolveResponsive(gap, screen, undefined)),
34
+ padding: toCssValue(resolveResponsive(padding, screen, undefined)),
35
+ margin: toCssValue(resolveResponsive(margin, screen, undefined)),
36
+ backgroundColor: resolveResponsive(backgroundColor, screen, undefined),
37
+ width: resolveResponsive(width, screen, "100%"),
38
+ maxWidth: resolveResponsive(maxWidth, screen, undefined),
39
+ height: resolveResponsive(height, screen, undefined),
40
+ borderRadius: resolveResponsive(borderRadius, screen, undefined),
41
+ border: resolveResponsive(border, screen, undefined),
42
+ boxShadow: resolveResponsive(boxShadow, screen, undefined),
43
+ overflow: resolveResponsive(overflow, screen, undefined),
44
+ boxSizing: "border-box",
45
+ ...style,
46
+ };
47
+ }, [
48
+ direction,
49
+ align,
50
+ justify,
51
+ wrap,
52
+ gap,
53
+ padding,
54
+ margin,
55
+ backgroundColor,
56
+ width,
57
+ maxWidth,
58
+ height,
59
+ borderRadius,
60
+ border,
61
+ boxShadow,
62
+ overflow,
63
+ screen,
64
+ style,
65
+ ]);
66
+ return (_jsx("div", { className: className, style: computedStyle, children: children }));
67
+ };
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from "react";
3
+ const getScreenSize = (width) => {
4
+ if (width < 768)
5
+ return "sm";
6
+ if (width < 1024)
7
+ return "md";
8
+ return "lg";
9
+ };
10
+ export const GridView = ({ columns = { sm: 1, md: 2, lg: 3 }, gap = 16, padding = 0, alignItems = "stretch", justifyItems = "stretch", backgroundColor = "transparent", width = "100%", maxWidth = "100%", height = "auto", margin = 0, style, className = "", children, }) => {
11
+ const [screenSize, setScreenSize] = useState("lg");
12
+ useEffect(() => {
13
+ setScreenSize(getScreenSize(window.innerWidth));
14
+ const onResize = () => setScreenSize(getScreenSize(window.innerWidth));
15
+ window.addEventListener("resize", onResize);
16
+ return () => window.removeEventListener("resize", onResize);
17
+ }, []);
18
+ const resolvedColumns = useMemo(() => {
19
+ if (typeof columns === "number")
20
+ return columns;
21
+ return columns[screenSize] ?? 1;
22
+ }, [columns, screenSize]);
23
+ const styles = useMemo(() => ({
24
+ display: "grid",
25
+ gridTemplateColumns: `repeat(${resolvedColumns}, 1fr)`,
26
+ gap: typeof gap === "number" ? `${gap}px` : gap,
27
+ padding: typeof padding === "number" ? `${padding}px` : padding,
28
+ margin: typeof margin === "number" ? `${margin}px` : margin,
29
+ alignItems,
30
+ justifyItems,
31
+ backgroundColor,
32
+ width,
33
+ maxWidth,
34
+ height,
35
+ boxSizing: "border-box",
36
+ ...style,
37
+ }), [
38
+ resolvedColumns,
39
+ gap,
40
+ padding,
41
+ margin,
42
+ alignItems,
43
+ justifyItems,
44
+ backgroundColor,
45
+ width,
46
+ maxWidth,
47
+ height,
48
+ style,
49
+ ]);
50
+ return (_jsx("div", { style: styles, className: className, children: children }));
51
+ };
@@ -0,0 +1,95 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useCallback } from "react";
3
+ /**
4
+ * ✅ Industry-Standard Image Component
5
+ * - SEO & accessibility optimized
6
+ * - Lazy loading & responsive
7
+ * - Fully customizable styling
8
+ * - Smooth hover transitions
9
+ * - Overlay and SVG support
10
+ */
11
+ export const Image = ({ src, alt = "Image", title, width = "100%", height = "auto", borderRadius = "8px", borderColor = "transparent", borderStyle = "solid", borderWidth = "0px", shadow = false, boxShadow, opacity = 1, objectFit = "cover", overlayText, overlayColor = "rgba(0,0,0,0.5)", svgIcon, responsive = false, padding, margin, lazyLoad = true, hoverOpacity, hoverShadow = false, hoverScale = 1, hoverRotate = 0, transitionDuration = "0.3s", overflow = "hidden", className, style, onClick, onLoad, onError, }) => {
12
+ // ✅ Memoized base style for performance
13
+ const baseStyle = useMemo(() => ({
14
+ width: responsive ? "100%" : width,
15
+ height: responsive ? "auto" : height,
16
+ borderRadius,
17
+ border: `${borderWidth} ${borderStyle} ${borderColor}`,
18
+ objectFit,
19
+ opacity,
20
+ boxShadow: shadow
21
+ ? boxShadow || "0 4px 12px rgba(0,0,0,0.15)"
22
+ : "none",
23
+ transition: `all ${transitionDuration} ease`,
24
+ display: "block",
25
+ }), [
26
+ responsive,
27
+ width,
28
+ height,
29
+ borderRadius,
30
+ borderColor,
31
+ borderStyle,
32
+ borderWidth,
33
+ objectFit,
34
+ opacity,
35
+ shadow,
36
+ boxShadow,
37
+ transitionDuration,
38
+ ]);
39
+ // ✅ Hover effects
40
+ const handleMouseEnter = useCallback((e) => {
41
+ e.currentTarget.style.opacity =
42
+ hoverOpacity !== undefined ? hoverOpacity.toString() : "1";
43
+ e.currentTarget.style.boxShadow = hoverShadow
44
+ ? "0 8px 20px rgba(0,0,0,0.3)"
45
+ : baseStyle.boxShadow || "none";
46
+ e.currentTarget.style.transform = `scale(${hoverScale}) rotate(${hoverRotate}deg)`;
47
+ }, [hoverOpacity, hoverShadow, hoverScale, hoverRotate, baseStyle.boxShadow]);
48
+ const handleMouseLeave = useCallback((e) => {
49
+ e.currentTarget.style.opacity = baseStyle.opacity?.toString() || "1";
50
+ e.currentTarget.style.boxShadow = baseStyle.boxShadow || "none";
51
+ e.currentTarget.style.transform = "scale(1) rotate(0deg)";
52
+ }, [baseStyle]);
53
+ // ✅ Overflow control
54
+ const overflowStyles = useMemo(() => {
55
+ switch (overflow) {
56
+ case "x":
57
+ return { overflowX: "hidden" };
58
+ case "y":
59
+ return { overflowY: "hidden" };
60
+ default:
61
+ return { overflow };
62
+ }
63
+ }, [overflow]);
64
+ return (_jsxs("div", { className: className, role: "img", "aria-label": alt, title: title || alt, onClick: onClick, style: {
65
+ width: responsive ? "100%" : width,
66
+ height: responsive ? "auto" : height,
67
+ padding,
68
+ margin,
69
+ position: "relative",
70
+ cursor: onClick ? "pointer" : "default",
71
+ display: "inline-block",
72
+ transition: `all ${transitionDuration} ease`,
73
+ ...overflowStyles,
74
+ ...style,
75
+ }, children: [svgIcon ? (_jsx("div", { style: {
76
+ width: "100%",
77
+ height: "100%",
78
+ display: "flex",
79
+ alignItems: "center",
80
+ justifyContent: "center",
81
+ }, children: svgIcon })) : (_jsx("img", { src: src, alt: alt, title: title || alt, loading: lazyLoad ? "lazy" : "eager", style: baseStyle, onClick: onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onLoad: onLoad, onError: onError, decoding: "async", fetchPriority: "high" })), overlayText && (_jsx("div", { style: {
82
+ position: "absolute",
83
+ inset: 0,
84
+ backgroundColor: overlayColor,
85
+ color: "#fff",
86
+ display: "flex",
87
+ alignItems: "center",
88
+ justifyContent: "center",
89
+ fontWeight: "bold",
90
+ fontSize: "1.1rem",
91
+ textAlign: "center",
92
+ padding: "1rem",
93
+ boxSizing: "border-box",
94
+ }, children: overlayText }))] }));
95
+ };