@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.
- package/LICENSE +21 -0
- package/README.md +708 -0
- package/dist/__tests__/Anchor.test.js +145 -0
- package/dist/__tests__/ArrowRight.test.js +91 -0
- package/dist/__tests__/Avatar.test.js +123 -0
- package/dist/__tests__/Button.test.js +82 -0
- package/dist/__tests__/Card.test.js +198 -0
- package/dist/__tests__/CheckCircle.test.js +98 -0
- package/dist/__tests__/Checkbox.test.js +161 -0
- package/dist/__tests__/ChevronDown.test.js +73 -0
- package/dist/__tests__/Close.test.js +98 -0
- package/dist/__tests__/EditSquare.test.js +99 -0
- package/dist/__tests__/Error.test.js +74 -0
- package/dist/__tests__/Footer.test.js +66 -0
- package/dist/__tests__/Heading.test.js +227 -0
- package/dist/__tests__/Hero.test.js +74 -0
- package/dist/__tests__/Label.test.js +123 -0
- package/dist/__tests__/Loader.test.js +115 -0
- package/dist/__tests__/MenuHover.test.js +137 -0
- package/dist/__tests__/Paragraph.test.js +93 -0
- package/dist/__tests__/PlusCircle.test.js +99 -0
- package/dist/__tests__/Radio.test.js +153 -0
- package/dist/__tests__/Select.test.js +187 -0
- package/dist/__tests__/Tabs.test.js +162 -0
- package/dist/__tests__/TextArea.test.js +127 -0
- package/dist/__tests__/TextInput.test.js +181 -0
- package/dist/__tests__/Toggle.test.js +120 -0
- package/dist/__tests__/TrashX.test.js +99 -0
- package/dist/__tests__/useHeadingAccessibility.test.js +144 -0
- package/dist/components/Anchor.js +131 -0
- package/dist/components/Animation.js +129 -0
- package/dist/components/AnimationGroup.js +207 -0
- package/dist/components/AnimationToggle.js +216 -0
- package/dist/components/Avatar.js +153 -0
- package/dist/components/Button.js +218 -0
- package/dist/components/Card.js +222 -0
- package/dist/components/Checkbox.js +305 -0
- package/dist/components/Crud.js +564 -0
- package/dist/components/DragAndDrop.js +337 -0
- package/dist/components/Error.js +206 -0
- package/dist/components/Footer.js +99 -0
- package/dist/components/Form.js +412 -0
- package/dist/components/Header.js +372 -0
- package/dist/components/Heading.js +134 -0
- package/dist/components/Hero.js +181 -0
- package/dist/components/Label.js +256 -0
- package/dist/components/Loader.js +302 -0
- package/dist/components/MenuHover.js +114 -0
- package/dist/components/Paragraph.js +128 -0
- package/dist/components/Prompt.js +61 -0
- package/dist/components/Radio.js +254 -0
- package/dist/components/Select.js +422 -0
- package/dist/components/SideMenu.js +313 -0
- package/dist/components/Tabs.js +297 -0
- package/dist/components/TextArea.js +370 -0
- package/dist/components/TextInput.js +286 -0
- package/dist/components/Toggle.js +186 -0
- package/dist/components/crudFiles/CrudEditBase.js +150 -0
- package/dist/components/crudFiles/CrudViewBase.js +39 -0
- package/dist/components/crudFiles/crudDevelopment.js +118 -0
- package/dist/components/crudFiles/crudEditHandlers.js +50 -0
- package/dist/constants/animation.js +30 -0
- package/dist/icons/ArrowIcon.js +32 -0
- package/dist/icons/ArrowRight.js +33 -0
- package/dist/icons/CheckCircle.js +33 -0
- package/dist/icons/ChevronDown.js +28 -0
- package/dist/icons/Close.js +33 -0
- package/dist/icons/EditSquare.js +33 -0
- package/dist/icons/Ellipses.js +34 -0
- package/dist/icons/Hamburger.js +39 -0
- package/dist/icons/LoadingSpinner.js +42 -0
- package/dist/icons/PlusCircle.js +33 -0
- package/dist/icons/SaveIcon.js +32 -0
- package/dist/icons/TrashX.js +33 -0
- package/dist/icons/__tests__/CheckCircle.test.js +9 -0
- package/dist/icons/__tests__/ChevronDown.test.js +9 -0
- package/dist/icons/__tests__/Close.test.js +9 -0
- package/dist/icons/__tests__/EditSquare.test.js +9 -0
- package/dist/icons/__tests__/PlusCircle.test.js +9 -0
- package/dist/icons/__tests__/TrashX.test.js +9 -0
- package/dist/icons/index.js +89 -0
- package/dist/index.js +332 -0
- package/dist/setupTests.js +3 -0
- package/dist/styles/_variables.scss +286 -0
- package/dist/styles/anchor.scss +40 -0
- package/dist/styles/animation-accessibility.scss +96 -0
- package/dist/styles/animation-toggle.scss +233 -0
- package/dist/styles/animation.scss +3781 -0
- package/dist/styles/avatar.scss +285 -0
- package/dist/styles/button.scss +430 -0
- package/dist/styles/card.scss +210 -0
- package/dist/styles/checkbox.scss +160 -0
- package/dist/styles/crud.scss +474 -0
- package/dist/styles/dragAndDrop.scss +312 -0
- package/dist/styles/error.scss +232 -0
- package/dist/styles/footer.scss +58 -0
- package/dist/styles/form.scss +420 -0
- package/dist/styles/grid.scss +29 -0
- package/dist/styles/header.scss +276 -0
- package/dist/styles/heading.scss +118 -0
- package/dist/styles/hero.scss +185 -0
- package/dist/styles/htmlElements.scss +20 -0
- package/dist/styles/image.scss +9 -0
- package/dist/styles/label.scss +340 -0
- package/dist/styles/list-item.scss +5 -0
- package/dist/styles/loader.scss +354 -0
- package/dist/styles/logo.scss +19 -0
- package/dist/styles/main.css +9056 -0
- package/dist/styles/main.css.map +1 -0
- package/dist/styles/main.scss +0 -0
- package/dist/styles/menu-hover.scss +30 -0
- package/dist/styles/paragraph.scss +88 -0
- package/dist/styles/prompt.scss +51 -0
- package/dist/styles/radio.scss +202 -0
- package/dist/styles/select.scss +363 -0
- package/dist/styles/side-menu.scss +334 -0
- package/dist/styles/tabs.scss +540 -0
- package/dist/styles/text-area.scss +388 -0
- package/dist/styles/text-input.scss +171 -0
- package/dist/styles/toggle.scss +0 -0
- package/dist/styles/unordered-list.scss +8 -0
- package/dist/utils/ScrollHandler.js +30 -0
- package/dist/utils/accessibility.js +128 -0
- package/dist/utils/heroUtils.js +316 -0
- package/dist/utils/index.js +104 -0
- package/dist/utils/inputValidation.js +29 -0
- package/dist/utils/keyboardNavigation.js +536 -0
- package/dist/utils/labelUtils.js +708 -0
- package/dist/utils/loaderUtils.js +387 -0
- package/dist/utils/menuUtils.js +575 -0
- package/dist/utils/useHeadingAccessibility.js +298 -0
- package/dist/utils/useRadioGroup.js +260 -0
- package/dist/utils/useSelectAccessibility.js +426 -0
- package/dist/utils/useTabsAccessibility.js +278 -0
- package/dist/utils/useTextAreaAccessibility.js +255 -0
- package/dist/utils/useTextInputAccessibility.js +295 -0
- package/dist/utils/useTypographyAccessibility.js +168 -0
- package/dist/utils/useWindowSize.js +32 -0
- package/dist/utils/utils/ScrollHandler.js +26 -0
- package/dist/utils/utils/accessibility.js +133 -0
- package/dist/utils/utils/heroUtils.js +348 -0
- package/dist/utils/utils/index.js +9 -0
- package/dist/utils/utils/inputValidation.js +22 -0
- package/dist/utils/utils/keyboardNavigation.js +664 -0
- package/dist/utils/utils/labelUtils.js +772 -0
- package/dist/utils/utils/loaderUtils.js +436 -0
- package/dist/utils/utils/menuUtils.js +651 -0
- package/dist/utils/utils/useHeadingAccessibility.js +334 -0
- package/dist/utils/utils/useRadioGroup.js +311 -0
- package/dist/utils/utils/useSelectAccessibility.js +498 -0
- package/dist/utils/utils/useTabsAccessibility.js +316 -0
- package/dist/utils/utils/useTextAreaAccessibility.js +303 -0
- package/dist/utils/utils/useTextInputAccessibility.js +338 -0
- package/dist/utils/utils/useTypographyAccessibility.js +180 -0
- package/dist/utils/utils/useWindowSize.js +26 -0
- package/dist/utils/utils/validation.js +131 -0
- package/dist/utils/validation.js +139 -0
- package/package.json +90 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Button = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _Animation = require("./Animation");
|
|
9
|
+
var _propTypes = require("prop-types");
|
|
10
|
+
var _icons = require("../icons");
|
|
11
|
+
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); }
|
|
12
|
+
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); }
|
|
13
|
+
/**
|
|
14
|
+
* Button - A stateless, accessible button component for user interactions
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Multiple variants (primary, secondary, success, error, warning)
|
|
18
|
+
* - Multiple sizes (xs, sm, md, lg, xl)
|
|
19
|
+
* - Accessible with proper ARIA labels and keyboard support
|
|
20
|
+
* - Loading states with built-in spinner
|
|
21
|
+
* - Icon support (leading and trailing)
|
|
22
|
+
* - Full width and outline variants
|
|
23
|
+
* - Keyboard navigation and focus management
|
|
24
|
+
* - High contrast mode support
|
|
25
|
+
*
|
|
26
|
+
* @component
|
|
27
|
+
* @example
|
|
28
|
+
* <Button
|
|
29
|
+
* variant="primary"
|
|
30
|
+
* size="md"
|
|
31
|
+
* onClick={(event) => console.log('Button clicked')}
|
|
32
|
+
* disabled={false}
|
|
33
|
+
* isLoading={false}
|
|
34
|
+
* ariaLabel="Save changes"
|
|
35
|
+
* >
|
|
36
|
+
* Save Changes
|
|
37
|
+
* </Button>
|
|
38
|
+
*/
|
|
39
|
+
const Button = exports.Button = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
40
|
+
// Core props
|
|
41
|
+
id,
|
|
42
|
+
className = '',
|
|
43
|
+
// Appearance props
|
|
44
|
+
variant = 'primary',
|
|
45
|
+
size = 'md',
|
|
46
|
+
isOutline = false,
|
|
47
|
+
isFullWidth = false,
|
|
48
|
+
// Content props
|
|
49
|
+
type = 'button',
|
|
50
|
+
children = null,
|
|
51
|
+
iconLeading = null,
|
|
52
|
+
iconTrailing = null,
|
|
53
|
+
loadingText = 'Loading...',
|
|
54
|
+
// Behavior props
|
|
55
|
+
disabled = false,
|
|
56
|
+
isLoading = false,
|
|
57
|
+
tabIndex = 0,
|
|
58
|
+
// Event props
|
|
59
|
+
onClick,
|
|
60
|
+
onFocus,
|
|
61
|
+
onBlur,
|
|
62
|
+
onKeyDown,
|
|
63
|
+
onMouseEnter,
|
|
64
|
+
onMouseLeave,
|
|
65
|
+
// Accessibility props
|
|
66
|
+
'aria-label': ariaLabel,
|
|
67
|
+
'aria-describedby': ariaDescribedBy,
|
|
68
|
+
'aria-expanded': ariaExpanded,
|
|
69
|
+
'aria-haspopup': ariaHaspopup,
|
|
70
|
+
'aria-pressed': ariaPressed,
|
|
71
|
+
role = 'button',
|
|
72
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
73
|
+
componentName = 'button',
|
|
74
|
+
additionalClassName = '',
|
|
75
|
+
...restProps
|
|
76
|
+
}, ref) => {
|
|
77
|
+
// Handle legacy prop mapping
|
|
78
|
+
const finalId = id || `button-${Math.random().toString(36).substr(2, 9)}`;
|
|
79
|
+
const finalClassName = className || additionalClassName;
|
|
80
|
+
|
|
81
|
+
// Handle keyboard navigation
|
|
82
|
+
const handleKeyDown = event => {
|
|
83
|
+
// Call custom handler first
|
|
84
|
+
onKeyDown?.(event);
|
|
85
|
+
|
|
86
|
+
// Prevent default for space key to avoid page scroll
|
|
87
|
+
if (event.key === ' ') {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
onClick?.(event);
|
|
90
|
+
}
|
|
91
|
+
// Enter key should trigger click
|
|
92
|
+
if (event.key === 'Enter') {
|
|
93
|
+
onClick?.(event);
|
|
94
|
+
}
|
|
95
|
+
onKeyDown?.(event);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Generate comprehensive class names
|
|
99
|
+
const getClassNames = () => {
|
|
100
|
+
const classes = [componentName, `${componentName}--${variant}`, `${componentName}--${size}`];
|
|
101
|
+
if (isOutline) classes.push(`${componentName}--outline`);
|
|
102
|
+
if (isFullWidth) classes.push(`${componentName}--full-width`);
|
|
103
|
+
if (isLoading) classes.push(`${componentName}--loading`);
|
|
104
|
+
if (disabled) classes.push(`${componentName}--disabled`);
|
|
105
|
+
if (finalClassName) classes.push(finalClassName);
|
|
106
|
+
return classes.join(' ');
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Determine button content
|
|
110
|
+
const buttonContent = isLoading ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_icons.LoadingSpinner, {
|
|
111
|
+
componentName: `${componentName}__loading-spinner`,
|
|
112
|
+
dimensions: 16
|
|
113
|
+
}), /*#__PURE__*/_react.default.createElement("span", {
|
|
114
|
+
className: `${componentName}__loading-text`
|
|
115
|
+
}, loadingText)) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, iconLeading && /*#__PURE__*/_react.default.createElement("span", {
|
|
116
|
+
className: `${componentName}__icon ${componentName}__icon--leading`
|
|
117
|
+
}, iconLeading), children && /*#__PURE__*/_react.default.createElement("span", {
|
|
118
|
+
className: `${componentName}__text`
|
|
119
|
+
}, children), iconTrailing && /*#__PURE__*/_react.default.createElement("span", {
|
|
120
|
+
className: `${componentName}__icon ${componentName}__icon--trailing`
|
|
121
|
+
}, iconTrailing));
|
|
122
|
+
return /*#__PURE__*/_react.default.createElement(_Animation.AnimatedDiv, {
|
|
123
|
+
fadingEntrances: "fadeIn",
|
|
124
|
+
duration: "faster"
|
|
125
|
+
}, /*#__PURE__*/_react.default.createElement("button", _extends({}, restProps, {
|
|
126
|
+
ref: ref,
|
|
127
|
+
id: finalId,
|
|
128
|
+
className: getClassNames(),
|
|
129
|
+
"data-testid": componentName,
|
|
130
|
+
type: type,
|
|
131
|
+
disabled: disabled || isLoading,
|
|
132
|
+
tabIndex: disabled ? -1 : tabIndex,
|
|
133
|
+
onClick: !disabled && !isLoading ? onClick : undefined,
|
|
134
|
+
onFocus: !disabled ? onFocus : undefined,
|
|
135
|
+
onBlur: !disabled ? onBlur : undefined,
|
|
136
|
+
onKeyDown: !disabled && !isLoading ? handleKeyDown : undefined,
|
|
137
|
+
onMouseEnter: !disabled ? onMouseEnter : undefined,
|
|
138
|
+
onMouseLeave: !disabled ? onMouseLeave : undefined,
|
|
139
|
+
"aria-label": ariaLabel,
|
|
140
|
+
"aria-describedby": ariaDescribedBy,
|
|
141
|
+
"aria-expanded": ariaExpanded,
|
|
142
|
+
"aria-haspopup": ariaHaspopup,
|
|
143
|
+
"aria-pressed": ariaPressed,
|
|
144
|
+
"aria-disabled": disabled || isLoading,
|
|
145
|
+
"aria-busy": isLoading,
|
|
146
|
+
role: role
|
|
147
|
+
}), buttonContent, /*#__PURE__*/_react.default.createElement("span", {
|
|
148
|
+
className: "sr-only"
|
|
149
|
+
}, isLoading ? 'Button is loading, please wait' : 'Press Enter or Space to activate this button')));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Configure component PropTypes
|
|
153
|
+
Button.propTypes = {
|
|
154
|
+
// Core props
|
|
155
|
+
/** Unique identifier for the component */
|
|
156
|
+
id: _propTypes.string,
|
|
157
|
+
/** Additional CSS classes to apply */
|
|
158
|
+
className: _propTypes.string,
|
|
159
|
+
// Appearance props
|
|
160
|
+
/** Button color variant */
|
|
161
|
+
variant: (0, _propTypes.oneOf)(['primary', 'secondary', 'success', 'error', 'warning']),
|
|
162
|
+
/** Button size variant */
|
|
163
|
+
size: (0, _propTypes.oneOf)(['xs', 'sm', 'md', 'lg', 'xl']),
|
|
164
|
+
/** Whether to use outline variant styling */
|
|
165
|
+
isOutline: _propTypes.bool,
|
|
166
|
+
/** Whether the button should take full width of its container */
|
|
167
|
+
isFullWidth: _propTypes.bool,
|
|
168
|
+
// Content props
|
|
169
|
+
/** HTML button type attribute */
|
|
170
|
+
type: (0, _propTypes.oneOf)(['button', 'submit', 'reset']),
|
|
171
|
+
/** Button content */
|
|
172
|
+
children: _propTypes.node,
|
|
173
|
+
/** Icon to display before the button text */
|
|
174
|
+
iconLeading: _propTypes.node,
|
|
175
|
+
/** Icon to display after the button text */
|
|
176
|
+
iconTrailing: _propTypes.node,
|
|
177
|
+
/** Text to display when button is loading */
|
|
178
|
+
loadingText: _propTypes.string,
|
|
179
|
+
// Behavior props
|
|
180
|
+
/** Whether the button is disabled */
|
|
181
|
+
disabled: _propTypes.bool,
|
|
182
|
+
/** Whether the button is in a loading state */
|
|
183
|
+
isLoading: _propTypes.bool,
|
|
184
|
+
/** Tab index for keyboard navigation */
|
|
185
|
+
tabIndex: _propTypes.number,
|
|
186
|
+
// Event props
|
|
187
|
+
/** Click event handler */
|
|
188
|
+
onClick: _propTypes.func,
|
|
189
|
+
/** Focus event handler */
|
|
190
|
+
onFocus: _propTypes.func,
|
|
191
|
+
/** Blur event handler */
|
|
192
|
+
onBlur: _propTypes.func,
|
|
193
|
+
/** Key down event handler */
|
|
194
|
+
onKeyDown: _propTypes.func,
|
|
195
|
+
/** Mouse enter event handler */
|
|
196
|
+
onMouseEnter: _propTypes.func,
|
|
197
|
+
/** Mouse leave event handler */
|
|
198
|
+
onMouseLeave: _propTypes.func,
|
|
199
|
+
// Accessibility props
|
|
200
|
+
/** Accessible label for the button */
|
|
201
|
+
'aria-label': _propTypes.string,
|
|
202
|
+
/** ID of element that describes the button */
|
|
203
|
+
'aria-describedby': _propTypes.string,
|
|
204
|
+
/** Whether the button controls an expanded element */
|
|
205
|
+
'aria-expanded': _propTypes.bool,
|
|
206
|
+
/** Whether the button has a popup */
|
|
207
|
+
'aria-haspopup': _propTypes.bool,
|
|
208
|
+
/** Whether the button is pressed (for toggle buttons) */
|
|
209
|
+
'aria-pressed': _propTypes.bool,
|
|
210
|
+
/** ARIA role for the button */
|
|
211
|
+
role: _propTypes.string,
|
|
212
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
213
|
+
/** Base CSS class name for the component */
|
|
214
|
+
componentName: _propTypes.string,
|
|
215
|
+
/** Additional CSS class names to apply to the button */
|
|
216
|
+
additionalClassName: _propTypes.string
|
|
217
|
+
};
|
|
218
|
+
Button.displayName = 'Button';
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Card = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _Animation = require("./Animation");
|
|
9
|
+
var _propTypes = require("prop-types");
|
|
10
|
+
var _Heading = _interopRequireDefault(require("./Heading"));
|
|
11
|
+
var _Avatar = _interopRequireDefault(require("./Avatar"));
|
|
12
|
+
var _Ellipses = require("../icons/Ellipses");
|
|
13
|
+
var _Paragraph = require("./Paragraph");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
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); }
|
|
16
|
+
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); } // components
|
|
17
|
+
// styles
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Card Component
|
|
21
|
+
*
|
|
22
|
+
* A flexible, accessible card container for displaying structured content.
|
|
23
|
+
* Perfect for user profiles, product displays, articles, and content previews.
|
|
24
|
+
*
|
|
25
|
+
* Features:
|
|
26
|
+
* - Stateless design for maximum flexibility
|
|
27
|
+
* - Built-in avatar and header sections
|
|
28
|
+
* - Dropdown menu support with keyboard navigation
|
|
29
|
+
* - Full-width image support
|
|
30
|
+
* - Comprehensive accessibility features
|
|
31
|
+
* - Customizable content areas
|
|
32
|
+
*
|
|
33
|
+
* @component
|
|
34
|
+
* @example
|
|
35
|
+
* // Basic card with title and content
|
|
36
|
+
* <Card
|
|
37
|
+
* title="John Doe"
|
|
38
|
+
* subTitle="Software Engineer"
|
|
39
|
+
* bodyCopy="Passionate about creating accessible web experiences..."
|
|
40
|
+
* avatarSrc="/path/to/avatar.jpg"
|
|
41
|
+
* />
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Interactive card with menu
|
|
45
|
+
* <Card
|
|
46
|
+
* title="Project Alpha"
|
|
47
|
+
* ellipsesList={[<MenuItem key="edit">Edit</MenuItem>]}
|
|
48
|
+
* isEllipsesOpen={menuOpen}
|
|
49
|
+
* onEllipsesToggle={() => setMenuOpen(!menuOpen)}
|
|
50
|
+
* role="article"
|
|
51
|
+
* />
|
|
52
|
+
*/
|
|
53
|
+
const Card = exports.Card = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
54
|
+
// Content props
|
|
55
|
+
bodyCopy = '',
|
|
56
|
+
title = '',
|
|
57
|
+
subTitle = '',
|
|
58
|
+
titleSize = 'h3',
|
|
59
|
+
// Image props
|
|
60
|
+
avatarSrc = '',
|
|
61
|
+
fullImage = '',
|
|
62
|
+
imgComponent = null,
|
|
63
|
+
// Menu props
|
|
64
|
+
ellipsesList = [],
|
|
65
|
+
isEllipsesOpen = false,
|
|
66
|
+
onEllipsesToggle = null,
|
|
67
|
+
onEllipsesKeyDown = null,
|
|
68
|
+
// Layout props
|
|
69
|
+
className = '',
|
|
70
|
+
// Accessibility props
|
|
71
|
+
'aria-label': ariaLabel,
|
|
72
|
+
'aria-describedby': ariaDescribedBy = null,
|
|
73
|
+
role = null,
|
|
74
|
+
tabIndex = null,
|
|
75
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
76
|
+
additionalClassName = '',
|
|
77
|
+
componentName = 'card',
|
|
78
|
+
children = null,
|
|
79
|
+
...restProps
|
|
80
|
+
}, ref) => {
|
|
81
|
+
// Handle legacy prop mapping
|
|
82
|
+
const finalClassName = className || additionalClassName;
|
|
83
|
+
return /*#__PURE__*/_react.default.createElement("div", _extends({
|
|
84
|
+
ref: ref,
|
|
85
|
+
className: `${componentName} ${isEllipsesOpen ? 'open' : ''} ${finalClassName}`.trim(),
|
|
86
|
+
"data-testid": componentName,
|
|
87
|
+
role: role,
|
|
88
|
+
"aria-label": ariaLabel,
|
|
89
|
+
"aria-describedby": ariaDescribedBy,
|
|
90
|
+
tabIndex: tabIndex
|
|
91
|
+
}, restProps), avatarSrc && /*#__PURE__*/_react.default.createElement("div", {
|
|
92
|
+
className: "head"
|
|
93
|
+
}, /*#__PURE__*/_react.default.createElement(_Avatar.default, {
|
|
94
|
+
dimensions: "50px",
|
|
95
|
+
source: avatarSrc,
|
|
96
|
+
alt: title ? `${title} avatar` : 'User avatar'
|
|
97
|
+
}), ellipsesList.length > 0 && /*#__PURE__*/_react.default.createElement("div", {
|
|
98
|
+
className: "ellipses"
|
|
99
|
+
}, /*#__PURE__*/_react.default.createElement("button", {
|
|
100
|
+
type: "button",
|
|
101
|
+
className: "ellipses-trigger",
|
|
102
|
+
onClick: onEllipsesToggle,
|
|
103
|
+
onKeyDown: onEllipsesKeyDown,
|
|
104
|
+
"aria-expanded": isEllipsesOpen,
|
|
105
|
+
"aria-haspopup": "menu",
|
|
106
|
+
"aria-label": "Card options menu"
|
|
107
|
+
}, /*#__PURE__*/_react.default.createElement(_Ellipses.Ellipses, null)), isEllipsesOpen && /*#__PURE__*/_react.default.createElement("div", {
|
|
108
|
+
className: "ellipses-content",
|
|
109
|
+
role: "menu",
|
|
110
|
+
"aria-label": "Card options"
|
|
111
|
+
}, /*#__PURE__*/_react.default.createElement("ul", {
|
|
112
|
+
className: "unordered-list",
|
|
113
|
+
role: "none"
|
|
114
|
+
}, ellipsesList)))), fullImage && imgComponent ? imgComponent : fullImage && /*#__PURE__*/_react.default.createElement("img", {
|
|
115
|
+
src: fullImage,
|
|
116
|
+
alt: title ? `${title} image` : 'Card image',
|
|
117
|
+
className: "card-image"
|
|
118
|
+
}), title && /*#__PURE__*/_react.default.createElement(_Heading.default, {
|
|
119
|
+
variant: titleSize,
|
|
120
|
+
style: {
|
|
121
|
+
marginTop: avatarSrc && '0'
|
|
122
|
+
},
|
|
123
|
+
componentName: "card-title"
|
|
124
|
+
}, title), subTitle && /*#__PURE__*/_react.default.createElement(_Paragraph.Paragraph, {
|
|
125
|
+
componentName: "card-subtitle"
|
|
126
|
+
}, subTitle), bodyCopy && /*#__PURE__*/_react.default.createElement(_Animation.AnimatedDiv, {
|
|
127
|
+
fadingEntrances: "fadeIn",
|
|
128
|
+
duration: "faster"
|
|
129
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
130
|
+
className: `${componentName}__body`
|
|
131
|
+
}, /*#__PURE__*/_react.default.createElement(_Paragraph.Paragraph, {
|
|
132
|
+
className: `${componentName}__body-copy`
|
|
133
|
+
}, bodyCopy), children)));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Set display name for debugging
|
|
137
|
+
Card.displayName = 'Card';
|
|
138
|
+
Card.propTypes = {
|
|
139
|
+
// ===========================================
|
|
140
|
+
// Core Props
|
|
141
|
+
// ===========================================
|
|
142
|
+
/**
|
|
143
|
+
* Card title text for the header
|
|
144
|
+
*/
|
|
145
|
+
title: _propTypes.string,
|
|
146
|
+
/**
|
|
147
|
+
* Card subtitle text displayed below title
|
|
148
|
+
*/
|
|
149
|
+
subTitle: _propTypes.string,
|
|
150
|
+
// ===========================================
|
|
151
|
+
// Content Props
|
|
152
|
+
// ===========================================
|
|
153
|
+
/**
|
|
154
|
+
* Main content text for the card body
|
|
155
|
+
*/
|
|
156
|
+
bodyCopy: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.node]),
|
|
157
|
+
/**
|
|
158
|
+
* Heading size variant for the title
|
|
159
|
+
*/
|
|
160
|
+
titleSize: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.node]),
|
|
161
|
+
// ===========================================
|
|
162
|
+
// Image Props
|
|
163
|
+
// ===========================================
|
|
164
|
+
/**
|
|
165
|
+
* URL for avatar image in card header
|
|
166
|
+
*/
|
|
167
|
+
avatarSrc: _propTypes.string,
|
|
168
|
+
/**
|
|
169
|
+
* URL for full-width card image
|
|
170
|
+
*/
|
|
171
|
+
fullImage: _propTypes.string,
|
|
172
|
+
/**
|
|
173
|
+
* Custom image component to render instead of default img tag
|
|
174
|
+
*/
|
|
175
|
+
imgComponent: _propTypes.node,
|
|
176
|
+
// ===========================================
|
|
177
|
+
// Menu & Interaction Props
|
|
178
|
+
// ===========================================
|
|
179
|
+
/**
|
|
180
|
+
* Array of menu items for ellipses dropdown
|
|
181
|
+
*/
|
|
182
|
+
ellipsesList: (0, _propTypes.arrayOf)(_propTypes.node),
|
|
183
|
+
/**
|
|
184
|
+
* Whether the ellipses dropdown menu is open (controlled by parent)
|
|
185
|
+
*/
|
|
186
|
+
isEllipsesOpen: _propTypes.bool,
|
|
187
|
+
/**
|
|
188
|
+
* Callback function for ellipses dropdown toggle
|
|
189
|
+
*/
|
|
190
|
+
onEllipsesToggle: _propTypes.func,
|
|
191
|
+
/**
|
|
192
|
+
* Callback function for ellipses keyboard navigation
|
|
193
|
+
*/
|
|
194
|
+
onEllipsesKeyDown: _propTypes.func,
|
|
195
|
+
// ===========================================
|
|
196
|
+
// Appearance Props
|
|
197
|
+
// ===========================================
|
|
198
|
+
/**
|
|
199
|
+
* Additional CSS classes to apply to the card
|
|
200
|
+
*/
|
|
201
|
+
className: _propTypes.string,
|
|
202
|
+
// ===========================================
|
|
203
|
+
// Accessibility Props
|
|
204
|
+
// ===========================================
|
|
205
|
+
/**
|
|
206
|
+
* Accessible label for the entire card
|
|
207
|
+
*/
|
|
208
|
+
'aria-label': _propTypes.string,
|
|
209
|
+
/**
|
|
210
|
+
* ID of element that describes the card content
|
|
211
|
+
*/
|
|
212
|
+
'aria-describedby': _propTypes.string,
|
|
213
|
+
/**
|
|
214
|
+
* ARIA role for the card (e.g., 'article', 'region')
|
|
215
|
+
*/
|
|
216
|
+
role: _propTypes.string,
|
|
217
|
+
/**
|
|
218
|
+
* Tab index for keyboard navigation (-1 to remove from tab order)
|
|
219
|
+
*/
|
|
220
|
+
tabIndex: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.node])
|
|
221
|
+
};
|
|
222
|
+
var _default = exports.default = Card;
|