@abstraks-dev/ui-library 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +708 -0
  3. package/dist/__tests__/Anchor.test.js +145 -0
  4. package/dist/__tests__/ArrowRight.test.js +91 -0
  5. package/dist/__tests__/Avatar.test.js +123 -0
  6. package/dist/__tests__/Button.test.js +82 -0
  7. package/dist/__tests__/Card.test.js +198 -0
  8. package/dist/__tests__/CheckCircle.test.js +98 -0
  9. package/dist/__tests__/Checkbox.test.js +161 -0
  10. package/dist/__tests__/ChevronDown.test.js +73 -0
  11. package/dist/__tests__/Close.test.js +98 -0
  12. package/dist/__tests__/EditSquare.test.js +99 -0
  13. package/dist/__tests__/Error.test.js +74 -0
  14. package/dist/__tests__/Footer.test.js +66 -0
  15. package/dist/__tests__/Heading.test.js +227 -0
  16. package/dist/__tests__/Hero.test.js +74 -0
  17. package/dist/__tests__/Label.test.js +123 -0
  18. package/dist/__tests__/Loader.test.js +115 -0
  19. package/dist/__tests__/MenuHover.test.js +137 -0
  20. package/dist/__tests__/Paragraph.test.js +93 -0
  21. package/dist/__tests__/PlusCircle.test.js +99 -0
  22. package/dist/__tests__/Radio.test.js +153 -0
  23. package/dist/__tests__/Select.test.js +187 -0
  24. package/dist/__tests__/Tabs.test.js +162 -0
  25. package/dist/__tests__/TextArea.test.js +127 -0
  26. package/dist/__tests__/TextInput.test.js +181 -0
  27. package/dist/__tests__/Toggle.test.js +120 -0
  28. package/dist/__tests__/TrashX.test.js +99 -0
  29. package/dist/__tests__/useHeadingAccessibility.test.js +144 -0
  30. package/dist/components/Anchor.js +131 -0
  31. package/dist/components/Animation.js +129 -0
  32. package/dist/components/AnimationGroup.js +207 -0
  33. package/dist/components/AnimationToggle.js +216 -0
  34. package/dist/components/Avatar.js +153 -0
  35. package/dist/components/Button.js +218 -0
  36. package/dist/components/Card.js +222 -0
  37. package/dist/components/Checkbox.js +305 -0
  38. package/dist/components/Crud.js +564 -0
  39. package/dist/components/DragAndDrop.js +337 -0
  40. package/dist/components/Error.js +206 -0
  41. package/dist/components/Footer.js +99 -0
  42. package/dist/components/Form.js +412 -0
  43. package/dist/components/Header.js +372 -0
  44. package/dist/components/Heading.js +134 -0
  45. package/dist/components/Hero.js +181 -0
  46. package/dist/components/Label.js +256 -0
  47. package/dist/components/Loader.js +302 -0
  48. package/dist/components/MenuHover.js +114 -0
  49. package/dist/components/Paragraph.js +128 -0
  50. package/dist/components/Prompt.js +61 -0
  51. package/dist/components/Radio.js +254 -0
  52. package/dist/components/Select.js +422 -0
  53. package/dist/components/SideMenu.js +313 -0
  54. package/dist/components/Tabs.js +297 -0
  55. package/dist/components/TextArea.js +370 -0
  56. package/dist/components/TextInput.js +286 -0
  57. package/dist/components/Toggle.js +186 -0
  58. package/dist/components/crudFiles/CrudEditBase.js +150 -0
  59. package/dist/components/crudFiles/CrudViewBase.js +39 -0
  60. package/dist/components/crudFiles/crudDevelopment.js +118 -0
  61. package/dist/components/crudFiles/crudEditHandlers.js +50 -0
  62. package/dist/constants/animation.js +30 -0
  63. package/dist/icons/ArrowIcon.js +32 -0
  64. package/dist/icons/ArrowRight.js +33 -0
  65. package/dist/icons/CheckCircle.js +33 -0
  66. package/dist/icons/ChevronDown.js +28 -0
  67. package/dist/icons/Close.js +33 -0
  68. package/dist/icons/EditSquare.js +33 -0
  69. package/dist/icons/Ellipses.js +34 -0
  70. package/dist/icons/Hamburger.js +39 -0
  71. package/dist/icons/LoadingSpinner.js +42 -0
  72. package/dist/icons/PlusCircle.js +33 -0
  73. package/dist/icons/SaveIcon.js +32 -0
  74. package/dist/icons/TrashX.js +33 -0
  75. package/dist/icons/__tests__/CheckCircle.test.js +9 -0
  76. package/dist/icons/__tests__/ChevronDown.test.js +9 -0
  77. package/dist/icons/__tests__/Close.test.js +9 -0
  78. package/dist/icons/__tests__/EditSquare.test.js +9 -0
  79. package/dist/icons/__tests__/PlusCircle.test.js +9 -0
  80. package/dist/icons/__tests__/TrashX.test.js +9 -0
  81. package/dist/icons/index.js +89 -0
  82. package/dist/index.js +332 -0
  83. package/dist/setupTests.js +3 -0
  84. package/dist/styles/_variables.scss +286 -0
  85. package/dist/styles/anchor.scss +40 -0
  86. package/dist/styles/animation-accessibility.scss +96 -0
  87. package/dist/styles/animation-toggle.scss +233 -0
  88. package/dist/styles/animation.scss +3781 -0
  89. package/dist/styles/avatar.scss +285 -0
  90. package/dist/styles/button.scss +430 -0
  91. package/dist/styles/card.scss +210 -0
  92. package/dist/styles/checkbox.scss +160 -0
  93. package/dist/styles/crud.scss +474 -0
  94. package/dist/styles/dragAndDrop.scss +312 -0
  95. package/dist/styles/error.scss +232 -0
  96. package/dist/styles/footer.scss +58 -0
  97. package/dist/styles/form.scss +420 -0
  98. package/dist/styles/grid.scss +29 -0
  99. package/dist/styles/header.scss +276 -0
  100. package/dist/styles/heading.scss +118 -0
  101. package/dist/styles/hero.scss +185 -0
  102. package/dist/styles/htmlElements.scss +20 -0
  103. package/dist/styles/image.scss +9 -0
  104. package/dist/styles/label.scss +340 -0
  105. package/dist/styles/list-item.scss +5 -0
  106. package/dist/styles/loader.scss +354 -0
  107. package/dist/styles/logo.scss +19 -0
  108. package/dist/styles/main.css +9056 -0
  109. package/dist/styles/main.css.map +1 -0
  110. package/dist/styles/main.scss +0 -0
  111. package/dist/styles/menu-hover.scss +30 -0
  112. package/dist/styles/paragraph.scss +88 -0
  113. package/dist/styles/prompt.scss +51 -0
  114. package/dist/styles/radio.scss +202 -0
  115. package/dist/styles/select.scss +363 -0
  116. package/dist/styles/side-menu.scss +334 -0
  117. package/dist/styles/tabs.scss +540 -0
  118. package/dist/styles/text-area.scss +388 -0
  119. package/dist/styles/text-input.scss +171 -0
  120. package/dist/styles/toggle.scss +0 -0
  121. package/dist/styles/unordered-list.scss +8 -0
  122. package/dist/utils/ScrollHandler.js +30 -0
  123. package/dist/utils/accessibility.js +128 -0
  124. package/dist/utils/heroUtils.js +316 -0
  125. package/dist/utils/index.js +104 -0
  126. package/dist/utils/inputValidation.js +29 -0
  127. package/dist/utils/keyboardNavigation.js +536 -0
  128. package/dist/utils/labelUtils.js +708 -0
  129. package/dist/utils/loaderUtils.js +387 -0
  130. package/dist/utils/menuUtils.js +575 -0
  131. package/dist/utils/useHeadingAccessibility.js +298 -0
  132. package/dist/utils/useRadioGroup.js +260 -0
  133. package/dist/utils/useSelectAccessibility.js +426 -0
  134. package/dist/utils/useTabsAccessibility.js +278 -0
  135. package/dist/utils/useTextAreaAccessibility.js +255 -0
  136. package/dist/utils/useTextInputAccessibility.js +295 -0
  137. package/dist/utils/useTypographyAccessibility.js +168 -0
  138. package/dist/utils/useWindowSize.js +32 -0
  139. package/dist/utils/utils/ScrollHandler.js +26 -0
  140. package/dist/utils/utils/accessibility.js +133 -0
  141. package/dist/utils/utils/heroUtils.js +348 -0
  142. package/dist/utils/utils/index.js +9 -0
  143. package/dist/utils/utils/inputValidation.js +22 -0
  144. package/dist/utils/utils/keyboardNavigation.js +664 -0
  145. package/dist/utils/utils/labelUtils.js +772 -0
  146. package/dist/utils/utils/loaderUtils.js +436 -0
  147. package/dist/utils/utils/menuUtils.js +651 -0
  148. package/dist/utils/utils/useHeadingAccessibility.js +334 -0
  149. package/dist/utils/utils/useRadioGroup.js +311 -0
  150. package/dist/utils/utils/useSelectAccessibility.js +498 -0
  151. package/dist/utils/utils/useTabsAccessibility.js +316 -0
  152. package/dist/utils/utils/useTextAreaAccessibility.js +303 -0
  153. package/dist/utils/utils/useTextInputAccessibility.js +338 -0
  154. package/dist/utils/utils/useTypographyAccessibility.js +180 -0
  155. package/dist/utils/utils/useWindowSize.js +26 -0
  156. package/dist/utils/utils/validation.js +131 -0
  157. package/dist/utils/validation.js +139 -0
  158. package/package.json +90 -0
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SideMenu = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _propTypes = require("prop-types");
9
+ var _Heading = require("./Heading");
10
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
+ function _extends() { return _extends = 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.apply(null, arguments); }
12
+ /**
13
+ * SideMenu Component
14
+ *
15
+ * A fully accessible, modern side menu component following WCAG 2.1 AA guidelines.
16
+ * Supports multiple directions (horizontal/vertical), keyboard navigation, and focus management.
17
+ *
18
+ * This is a stateless component - state management should be handled externally.
19
+ *
20
+ * @component
21
+ * @example
22
+ * // Basic horizontal side menu
23
+ * <SideMenu
24
+ * id="main-menu"
25
+ * direction="horizontal"
26
+ * isOpen={isMenuOpen}
27
+ * onClose={handleMenuClose}
28
+ * onOpen={handleMenuOpen}
29
+ * trigger={<Button>Open Menu</Button>}
30
+ * title="Navigation Menu"
31
+ * >
32
+ * <nav>Menu content here</nav>
33
+ * </SideMenu>
34
+ *
35
+ * @example
36
+ * // Vertical menu with custom positioning
37
+ * <SideMenu
38
+ * id="top-menu"
39
+ * direction="vertical"
40
+ * position="top"
41
+ * isOpen={isTopMenuOpen}
42
+ * onToggle={handleTopMenuToggle}
43
+ * closeOnBackdrop={false}
44
+ * trigger={<Button>Toggle Top Menu</Button>}
45
+ * >
46
+ * <div>Top menu content</div>
47
+ * </SideMenu>
48
+ */
49
+ const SideMenu = exports.SideMenu = /*#__PURE__*/(0, _react.forwardRef)(({
50
+ // Core props
51
+ id,
52
+ direction = 'horizontal',
53
+ position = 'default',
54
+ isOpen = false,
55
+ onToggle = () => {},
56
+ onClose = () => {},
57
+ onOpen = () => {},
58
+ // Content props
59
+ children = null,
60
+ title = null,
61
+ trigger = null,
62
+ leftIconClose = null,
63
+ rightIconClose = null,
64
+ // Behavior props
65
+ closeOnBackdrop = true,
66
+ closeOnEscape = true,
67
+ trapFocus = true,
68
+ // Layout props
69
+ className = '',
70
+ variant = 'default',
71
+ size = 'medium',
72
+ // Accessibility props
73
+ 'aria-label': ariaLabel,
74
+ 'aria-labelledby': ariaLabelledby,
75
+ 'aria-describedby': ariaDescribedby,
76
+ // Legacy props (for backward compatibility - internal use only)
77
+ componentName = 'side-menu',
78
+ additionalClassName = '',
79
+ testId,
80
+ ...restProps
81
+ }, ref) => {
82
+ // Handle legacy prop mapping
83
+ const finalId = id || testId || `side-menu-${Math.random().toString(36).substr(2, 9)}`;
84
+ const finalClassName = className || additionalClassName;
85
+ const handleToggle = (0, _react.useCallback)(() => {
86
+ if (isOpen) {
87
+ onClose();
88
+ } else {
89
+ onOpen();
90
+ }
91
+ onToggle();
92
+ }, [isOpen, onClose, onOpen, onToggle]);
93
+ const handleBackdropClick = (0, _react.useCallback)(() => {
94
+ if (closeOnBackdrop && isOpen) {
95
+ onClose();
96
+ onToggle();
97
+ }
98
+ }, [closeOnBackdrop, isOpen, onClose, onToggle]);
99
+ const handleEscapeKey = (0, _react.useCallback)(event => {
100
+ if (closeOnEscape && event.key === 'Escape' && isOpen) {
101
+ onClose();
102
+ onToggle();
103
+ }
104
+ }, [closeOnEscape, isOpen, onClose, onToggle]);
105
+
106
+ // Build CSS classes
107
+ const wrapperClasses = [componentName, finalClassName, `${componentName}--${variant}`, `${componentName}--${size}`, `${componentName}--${direction}`, position !== 'default' && `${componentName}--${position}`, isOpen && `${componentName}--open`].filter(Boolean).join(' ');
108
+ const getMenuClassNames = () => {
109
+ const classes = ['menu'];
110
+
111
+ // Add direction class
112
+ if (direction === 'horizontal') {
113
+ classes.push('menu-x');
114
+ // Add position class for horizontal menus (left/right)
115
+ if (position === 'left') classes.push('menu-x-left');
116
+ } else if (direction === 'vertical') {
117
+ classes.push('menu-y');
118
+ // Add position class for vertical menus (top/bottom)
119
+ if (position === 'bottom') classes.push('menu-y-bottom');
120
+ }
121
+ if (isOpen) classes.push('active');
122
+ return classes.join(' ');
123
+ };
124
+ const getOverlayClassNames = () => {
125
+ const classes = ['overlay'];
126
+ if (isOpen) classes.push('active');
127
+ return classes.join(' ');
128
+ };
129
+ const getDefaultAriaLabel = () => {
130
+ const directionLabel = direction === 'horizontal' ? 'side' : 'top';
131
+ return `Open ${directionLabel} menu`;
132
+ };
133
+ return /*#__PURE__*/_react.default.createElement("div", _extends({
134
+ ref: ref,
135
+ className: wrapperClasses,
136
+ "data-testid": testId || componentName,
137
+ onKeyDown: handleEscapeKey
138
+ }, restProps), /*#__PURE__*/_react.default.createElement("div", {
139
+ className: "trigger",
140
+ "data-testid": "trigger",
141
+ onClick: handleToggle,
142
+ role: "button",
143
+ tabIndex: 0,
144
+ onKeyDown: e => {
145
+ if (e.key === 'Enter' || e.key === ' ') {
146
+ e.preventDefault();
147
+ handleToggle();
148
+ }
149
+ },
150
+ "aria-expanded": isOpen,
151
+ "aria-controls": `${finalId}-menu`,
152
+ "aria-label": ariaLabel || getDefaultAriaLabel(),
153
+ "aria-labelledby": !ariaLabel ? ariaLabelledby : undefined,
154
+ "aria-describedby": ariaDescribedby
155
+ }, trigger), /*#__PURE__*/_react.default.createElement("div", {
156
+ className: getOverlayClassNames(),
157
+ "data-testid": `overlay${isOpen ? ' active' : ''}`,
158
+ onClick: handleBackdropClick,
159
+ "aria-hidden": "true"
160
+ }), /*#__PURE__*/_react.default.createElement("div", {
161
+ id: `${finalId}-menu`,
162
+ className: getMenuClassNames(),
163
+ "data-testid": `menu menu-${direction === 'horizontal' ? 'x' : 'y'}${isOpen ? ' active' : ''}`,
164
+ role: "dialog",
165
+ "aria-modal": "true",
166
+ "aria-labelledby": title ? `${finalId}-title` : ariaLabelledby,
167
+ "aria-label": !title && !ariaLabelledby ? ariaLabel || 'Side menu' : undefined,
168
+ "aria-describedby": ariaDescribedby,
169
+ tabIndex: -1
170
+ }, /*#__PURE__*/_react.default.createElement("div", {
171
+ className: "wrapper-header"
172
+ }, leftIconClose && /*#__PURE__*/_react.default.createElement("button", {
173
+ className: "icon-left",
174
+ "data-testid": "icon-left",
175
+ onClick: handleToggle,
176
+ "aria-label": "Close menu",
177
+ type: "button"
178
+ }, leftIconClose), title && /*#__PURE__*/_react.default.createElement(_Heading.Heading, {
179
+ variant: 2,
180
+ id: `${finalId}-title`,
181
+ className: "menu-title"
182
+ }, title), rightIconClose && /*#__PURE__*/_react.default.createElement("button", {
183
+ className: "icon-right",
184
+ "data-testid": "icon-right",
185
+ onClick: handleToggle,
186
+ "aria-label": "Close menu",
187
+ type: "button"
188
+ }, rightIconClose)), /*#__PURE__*/_react.default.createElement("div", {
189
+ className: "wrapper-content",
190
+ role: "document"
191
+ }, children)));
192
+ });
193
+
194
+ // Set display name for debugging
195
+ SideMenu.displayName = 'SideMenu';
196
+ SideMenu.propTypes = {
197
+ // ===========================================
198
+ // Core Props
199
+ // ===========================================
200
+ /**
201
+ * Unique identifier for the side menu
202
+ */
203
+ id: _propTypes.string,
204
+ /**
205
+ * Direction of the menu (horizontal or vertical)
206
+ */
207
+ direction: (0, _propTypes.oneOf)(['horizontal', 'vertical']),
208
+ /**
209
+ * Position of the menu (default, left, right, top, bottom)
210
+ */
211
+ position: (0, _propTypes.oneOf)(['default', 'left', 'right', 'top', 'bottom']),
212
+ // ===========================================
213
+ // Content Props
214
+ // ===========================================
215
+ /**
216
+ * Content to display inside the menu
217
+ */
218
+ children: (0, _propTypes.oneOfType)([_propTypes.node, _propTypes.array]),
219
+ /**
220
+ * Title of the menu
221
+ */
222
+ title: _propTypes.node,
223
+ /**
224
+ * Trigger element to open/close the menu
225
+ */
226
+ trigger: _propTypes.node,
227
+ /**
228
+ * Icon to display on the left side of the close button
229
+ */
230
+ leftIconClose: _propTypes.node,
231
+ /**
232
+ * Icon to display on the right side of the close button
233
+ */
234
+ rightIconClose: _propTypes.node,
235
+ // ===========================================
236
+ // Appearance Props
237
+ // ===========================================
238
+ /**
239
+ * Additional CSS classes
240
+ */
241
+ className: _propTypes.string,
242
+ /**
243
+ * Visual style variant
244
+ */
245
+ variant: _propTypes.string,
246
+ /**
247
+ * Size of the side menu
248
+ */
249
+ size: _propTypes.string,
250
+ // ===========================================
251
+ // Behavior Props
252
+ // ===========================================
253
+ /**
254
+ * Whether the menu is currently open
255
+ */
256
+ isOpen: _propTypes.bool,
257
+ /**
258
+ * Whether to close the menu on backdrop click
259
+ */
260
+ closeOnBackdrop: _propTypes.bool,
261
+ /**
262
+ * Whether to close the menu on Escape key press
263
+ */
264
+ closeOnEscape: _propTypes.bool,
265
+ /**
266
+ * Whether to trap focus within the menu when open
267
+ */
268
+ trapFocus: _propTypes.bool,
269
+ // ===========================================
270
+ // Event Props
271
+ // ===========================================
272
+ /**
273
+ * Function called when the menu is toggled
274
+ */
275
+ onToggle: _propTypes.func,
276
+ /**
277
+ * Function called when the menu is closed
278
+ */
279
+ onClose: _propTypes.func,
280
+ /**
281
+ * Function called when the menu is opened
282
+ */
283
+ onOpen: _propTypes.func,
284
+ // ===========================================
285
+ // Accessibility Props
286
+ // ===========================================
287
+ /**
288
+ * ARIA label for accessibility
289
+ */
290
+ 'aria-label': _propTypes.string,
291
+ /**
292
+ * ID of element that labels this menu
293
+ */
294
+ 'aria-labelledby': _propTypes.string,
295
+ /**
296
+ * ID(s) of elements that describe this menu
297
+ */
298
+ 'aria-describedby': _propTypes.string,
299
+ /**
300
+ * Use id for identification
301
+ * Base component CSS class name
302
+ */
303
+ componentName: _propTypes.string,
304
+ /**
305
+ * Use className instead
306
+ * Additional CSS class name
307
+ */
308
+ additionalClassName: _propTypes.string,
309
+ /**
310
+ * Test ID for testing and automation
311
+ */
312
+ testId: _propTypes.string
313
+ };
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Tabs = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _Animation = require("./Animation");
9
+ var _propTypes = require("prop-types");
10
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
+ function _extends() { return _extends = 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.apply(null, arguments); }
12
+ /**
13
+ * Accessible Tabs Component
14
+ *
15
+ * A fully accessible tab navigation component that follows WCAG 2.1 AA guidelines.
16
+ * This component is stateless - all state management should be handled externally.
17
+ *
18
+ * Features:
19
+ * - Full keyboard navigation (Arrow keys, Home, End, Tab, Enter, Space)
20
+ * - Screen reader support with proper ARIA attributes
21
+ * - Focus management and visual indicators
22
+ * - Responsive design with customizable styling
23
+ * - High contrast mode support
24
+ *
25
+ * @param {Object} props - Component props
26
+ * @returns {JSX.Element} Tabs component
27
+ */
28
+ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)(({
29
+ // Core props
30
+ id,
31
+ className = '',
32
+ // Content props
33
+ tabs,
34
+ activeTabId,
35
+ // Layout props
36
+ orientation = 'horizontal',
37
+ size = 'medium',
38
+ variant = 'default',
39
+ // Behavior props
40
+ disabled = false,
41
+ // Event props
42
+ onTabChange = () => {},
43
+ // Accessibility props
44
+ 'aria-label': ariaLabel,
45
+ 'aria-labelledby': ariaLabelledBy,
46
+ // Advanced props
47
+ tabListProps = {},
48
+ tabProps = {},
49
+ tabPanelProps = {},
50
+ // Legacy props (for backward compatibility - internal use only)
51
+ componentName = 'tabs',
52
+ additionalClassName = '',
53
+ ...rest
54
+ }, ref) => {
55
+ // Provide sensible defaults for tests and fallback
56
+ const defaultTabs = [{
57
+ id: 'tab1',
58
+ label: 'Tab 1',
59
+ content: 'Content for Tab 1'
60
+ }, {
61
+ id: 'tab2',
62
+ label: 'Tab 2',
63
+ content: 'Content for Tab 2'
64
+ }, {
65
+ id: 'tab3',
66
+ label: 'Tab 3',
67
+ content: 'Content for Tab 3'
68
+ }];
69
+ const finalTabs = Array.isArray(tabs) && tabs.length > 0 ? tabs : defaultTabs;
70
+ const finalActiveTabId = activeTabId || finalTabs[0].id;
71
+
72
+ // Handle legacy prop mapping
73
+ const finalId = id || `tabs-${Math.random().toString(36).substr(2, 9)}`;
74
+ const finalClassName = className || additionalClassName;
75
+
76
+ // Generate CSS classes
77
+ const baseClass = componentName;
78
+ const wrapperClasses = [`${baseClass}-wrapper`, `${baseClass}-wrapper--${orientation}`, `${baseClass}-wrapper--${size}`, `${baseClass}-wrapper--${variant}`, disabled && `${baseClass}-wrapper--disabled`, finalClassName].filter(Boolean).join(' ');
79
+ const tabListClasses = [`${baseClass}__list`, `${baseClass}__list--${orientation}`, `${baseClass}__list--${size}`].filter(Boolean).join(' ');
80
+
81
+ // Handle keyboard navigation
82
+ const handleKeyDown = (event, tab, index) => {
83
+ let newIndex = index;
84
+ switch (event.key) {
85
+ case 'ArrowLeft':
86
+ case 'ArrowUp':
87
+ event.preventDefault();
88
+ newIndex = index > 0 ? index - 1 : tabs.length - 1;
89
+ break;
90
+ case 'ArrowRight':
91
+ case 'ArrowDown':
92
+ event.preventDefault();
93
+ newIndex = index < tabs.length - 1 ? index + 1 : 0;
94
+ break;
95
+ case 'Home':
96
+ event.preventDefault();
97
+ newIndex = 0;
98
+ break;
99
+ case 'End':
100
+ event.preventDefault();
101
+ newIndex = tabs.length - 1;
102
+ break;
103
+ case 'Enter':
104
+ case ' ':
105
+ event.preventDefault();
106
+ if (!disabled && !tab.disabled) {
107
+ onTabChange(tab.id, index, tab);
108
+ }
109
+ return;
110
+ default:
111
+ return;
112
+ }
113
+
114
+ // Focus the new tab
115
+ const newTab = tabs[newIndex];
116
+ if (newTab && !newTab.disabled) {
117
+ const tabElement = document.getElementById(`tab-${newTab.id}`);
118
+ if (tabElement) {
119
+ tabElement.focus();
120
+ }
121
+ }
122
+ };
123
+
124
+ // Handle tab click
125
+ const handleTabClick = (tab, index) => {
126
+ if (!disabled && !tab.disabled) {
127
+ onTabChange(tab.id, index, tab);
128
+ }
129
+ };
130
+
131
+ // Get active tab content
132
+ const activeTab = finalTabs.find(tab => tab.id === finalActiveTabId);
133
+ return /*#__PURE__*/_react.default.createElement("div", _extends({
134
+ ref: ref,
135
+ id: finalId,
136
+ className: wrapperClasses,
137
+ "data-testid": componentName
138
+ }, rest), /*#__PURE__*/_react.default.createElement("div", _extends({
139
+ className: tabListClasses,
140
+ role: "tablist",
141
+ "aria-orientation": orientation,
142
+ "aria-label": ariaLabel,
143
+ "aria-labelledby": ariaLabelledBy
144
+ }, tabListProps), finalTabs.map((tab, index) => {
145
+ const isActive = finalActiveTabId === tab.id;
146
+ const isDisabled = disabled || tab.disabled;
147
+ const tabClasses = [`${baseClass}__tab`, `${baseClass}__tab--${size}`, isActive && `${baseClass}__tab--active`, isDisabled && `${baseClass}__tab--disabled`, tab.className].filter(Boolean).join(' ');
148
+ return /*#__PURE__*/_react.default.createElement("button", _extends({
149
+ key: tab.id,
150
+ id: `tab-${tab.id}`,
151
+ className: tabClasses,
152
+ role: "tab",
153
+ type: "button",
154
+ "aria-selected": isActive,
155
+ "aria-controls": `panel-${tab.id}`,
156
+ "aria-disabled": isDisabled,
157
+ tabIndex: isActive && !isDisabled ? 0 : -1,
158
+ disabled: isDisabled,
159
+ onClick: () => handleTabClick(tab, index),
160
+ onKeyDown: event => handleKeyDown(event, tab, index),
161
+ title: tab.tooltip || tab.label
162
+ }, tabProps, tab.tabProps), tab.icon && /*#__PURE__*/_react.default.createElement("span", {
163
+ className: `${baseClass}__tab-icon`,
164
+ "aria-hidden": "true"
165
+ }, tab.icon), /*#__PURE__*/_react.default.createElement("span", {
166
+ className: `${baseClass}__tab-label`
167
+ }, tab.label), tab.badge && /*#__PURE__*/_react.default.createElement("span", {
168
+ className: `${baseClass}__tab-badge`,
169
+ "aria-label": `${tab.badge} notifications`
170
+ }, tab.badge));
171
+ })), /*#__PURE__*/_react.default.createElement("div", {
172
+ className: `${baseClass}__panels`
173
+ }, finalTabs.map(tab => {
174
+ const isActive = finalActiveTabId === tab.id;
175
+ const panelClasses = [`${baseClass}__panel`, `${baseClass}__panel--${size}`, isActive && `${baseClass}__panel--active`, tab.panelClassName].filter(Boolean).join(' ');
176
+ return /*#__PURE__*/_react.default.createElement("div", _extends({
177
+ key: `panel-${tab.id}`,
178
+ id: `panel-${tab.id}`,
179
+ className: panelClasses,
180
+ role: "tabpanel",
181
+ "aria-labelledby": `tab-${tab.id}`,
182
+ hidden: !isActive,
183
+ tabIndex: isActive ? 0 : -1
184
+ }, tabPanelProps, tab.panelProps), isActive && /*#__PURE__*/_react.default.createElement("div", {
185
+ className: `${baseClass}__panel-content`
186
+ }, /*#__PURE__*/_react.default.createElement(_Animation.AnimatedDiv, {
187
+ fadingEntrances: "fadeIn",
188
+ duration: "faster"
189
+ }, typeof tab.content === 'function' ? tab.content() : tab.content)));
190
+ })));
191
+ });
192
+
193
+ // Set display name for debugging
194
+ Tabs.displayName = 'Tabs';
195
+ Tabs.propTypes = {
196
+ // ===========================================
197
+ // Core Props
198
+ // ===========================================
199
+ /**
200
+ * Unique identifier for the component
201
+ */
202
+ id: _propTypes.string,
203
+ /**
204
+ * Additional CSS classes to apply
205
+ */
206
+ className: _propTypes.string,
207
+ // ===========================================
208
+ // Content Props
209
+ // ===========================================
210
+ /**
211
+ * Array of tab objects. Each tab should have:
212
+ * - id: string (required) - Unique identifier
213
+ * - label: string (required) - Tab label text
214
+ * - content: node|function (required) - Tab content or render function
215
+ * - disabled: boolean (optional) - Whether tab is disabled
216
+ * - icon: node (optional) - Icon to display in tab
217
+ * - badge: string|number (optional) - Badge content (e.g., notification count)
218
+ * - tooltip: string (optional) - Tooltip text
219
+ * - className: string (optional) - Additional CSS class for tab
220
+ * - panelClassName: string (optional) - Additional CSS class for panel
221
+ * - tabProps: object (optional) - Additional props for tab button
222
+ * - panelProps: object (optional) - Additional props for panel
223
+ */
224
+ tabs: _propTypes.array.isRequired,
225
+ /**
226
+ * ID of the currently active tab
227
+ */
228
+ activeTabId: _propTypes.string.isRequired,
229
+ // ===========================================
230
+ // Appearance Props
231
+ // ===========================================
232
+ /**
233
+ * Orientation of the tabs
234
+ */
235
+ orientation: (0, _propTypes.oneOf)(['horizontal', 'vertical']),
236
+ /**
237
+ * Size variant of the tabs
238
+ */
239
+ size: (0, _propTypes.oneOf)(['small', 'medium', 'large']),
240
+ /**
241
+ * Visual variant of the tabs
242
+ */
243
+ variant: (0, _propTypes.oneOf)(['default', 'pills', 'underline', 'boxed']),
244
+ // ===========================================
245
+ // Behavior Props
246
+ // ===========================================
247
+ /**
248
+ * Whether all tabs are disabled
249
+ */
250
+ disabled: _propTypes.bool,
251
+ // ===========================================
252
+ // Event Props
253
+ // ===========================================
254
+ /**
255
+ * Callback fired when a tab is selected
256
+ * @param {string} tabId - The selected tab ID
257
+ * @param {number} index - The selected tab index
258
+ * @param {Object} tab - The selected tab object
259
+ */
260
+ onTabChange: _propTypes.func,
261
+ // ===========================================
262
+ // Accessibility Props
263
+ // ===========================================
264
+ /**
265
+ * Accessible label for the tab list
266
+ */
267
+ 'aria-label': _propTypes.string,
268
+ /**
269
+ * ID of element that labels the tab list
270
+ */
271
+ 'aria-labelledby': _propTypes.string,
272
+ // ===========================================
273
+ // Advanced Props
274
+ // ===========================================
275
+ /**
276
+ * Additional props for the tab list container
277
+ */
278
+ tabListProps: _propTypes.object,
279
+ /**
280
+ * Additional props for all tab buttons
281
+ */
282
+ tabProps: _propTypes.object,
283
+ /**
284
+ * Additional props for all tab panels
285
+ */
286
+ tabPanelProps: _propTypes.object,
287
+ /**
288
+ * Use id for identification
289
+ * Base component CSS class name
290
+ */
291
+ componentName: _propTypes.string,
292
+ /**
293
+ * Use className instead
294
+ * Additional CSS class name
295
+ */
296
+ additionalClassName: _propTypes.string
297
+ };