@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,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DragAndDrop = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _propTypes = require("prop-types");
|
|
9
|
+
var _accessibility = require("../utils/accessibility.js");
|
|
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
|
+
/**
|
|
12
|
+
* DragAndDrop Component - A completely stateless, accessible drag-and-drop list component
|
|
13
|
+
*
|
|
14
|
+
* This component provides drag-and-drop functionality with:
|
|
15
|
+
* - Full accessibility support (ARIA, keyboard navigation, screen reader announcements)
|
|
16
|
+
* - Customizable item rendering and styling
|
|
17
|
+
* - Proper event handling for all drag operations
|
|
18
|
+
* - Focus management and keyboard interactions
|
|
19
|
+
* - High contrast and reduced motion support
|
|
20
|
+
* - Screen reader announcements for all drag operations
|
|
21
|
+
*
|
|
22
|
+
* All state management and business logic should be handled by parent components.
|
|
23
|
+
* This ensures maximum flexibility and reusability across different use cases.
|
|
24
|
+
*
|
|
25
|
+
* ## Accessibility Features
|
|
26
|
+
* - Full keyboard navigation with Tab, Arrow keys, Space, Enter
|
|
27
|
+
* - Screen reader announcements for drag operations and state changes
|
|
28
|
+
* - Proper ARIA labeling and live regions
|
|
29
|
+
* - Focus indicators and management
|
|
30
|
+
* - High contrast mode support
|
|
31
|
+
* - Reduced motion preferences respected
|
|
32
|
+
* - Semantic HTML structure
|
|
33
|
+
*
|
|
34
|
+
* ## Technical Implementation
|
|
35
|
+
* - Uses HTML5 drag and drop API with proper event handling
|
|
36
|
+
* - Provides callbacks for all drag events (start, over, enter, leave, drop)
|
|
37
|
+
* - Supports custom drag state management from parent
|
|
38
|
+
* - Handles keyboard interactions for accessibility
|
|
39
|
+
*
|
|
40
|
+
* @component
|
|
41
|
+
* @example
|
|
42
|
+
* // Basic usage (stateless)
|
|
43
|
+
* <DragAndDrop
|
|
44
|
+
* items={items}
|
|
45
|
+
* onDragStart={handleDragStart}
|
|
46
|
+
* onDragOver={handleDragOver}
|
|
47
|
+
* onDrop={handleDrop}
|
|
48
|
+
* dragState={dragState}
|
|
49
|
+
* ariaLabel="Reorderable task list"
|
|
50
|
+
* />
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} props - Component props
|
|
53
|
+
* @param {string} [props.componentName='drag-and-drop'] - Base CSS class name
|
|
54
|
+
* @param {string} [props.additionalClassName=''] - Additional CSS classes
|
|
55
|
+
* @param {Array} props.items - Array of items to display and make draggable (required)
|
|
56
|
+
* @param {Function} props.onDragStart - Callback for drag start events (required)
|
|
57
|
+
* @param {Function} props.onDragOver - Callback for drag over events (required)
|
|
58
|
+
* @param {Function} props.onDragEnter - Callback for drag enter events
|
|
59
|
+
* @param {Function} props.onDragLeave - Callback for drag leave events (required)
|
|
60
|
+
* @param {Function} props.onDrop - Callback for drop events (required)
|
|
61
|
+
* @param {Function} [props.onKeyDown] - Callback for keyboard events
|
|
62
|
+
* @param {Object} [props.dragState] - Current drag and drop state
|
|
63
|
+
* @param {boolean} [props.dragState.isDragging=false] - Whether an item is being dragged
|
|
64
|
+
* @param {number|string} [props.dragState.draggedFrom] - Index of item being dragged
|
|
65
|
+
* @param {number|string} [props.dragState.draggedTo] - Index of drop target
|
|
66
|
+
* @param {boolean} [props.enableKeyboardNavigation=true] - Enable keyboard navigation
|
|
67
|
+
* @param {string} [props.ariaLabel='Draggable list'] - Accessible label for the component
|
|
68
|
+
* @param {string} [props.ariaDescribedBy] - ID of element describing the component
|
|
69
|
+
* @param {Function} [props.renderItem] - Custom item renderer function
|
|
70
|
+
* @param {string} [props.itemIdKey='id'] - Key to use for item IDs
|
|
71
|
+
* @param {string} [props.itemContentKey='title'] - Key to use for item content
|
|
72
|
+
* @param {string} [props.itemNumberKey='number'] - Key to use for item numbers
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
const DragAndDrop = exports.DragAndDrop = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
76
|
+
componentName = 'drag-and-drop',
|
|
77
|
+
additionalClassName = '',
|
|
78
|
+
items = [],
|
|
79
|
+
onDragStart,
|
|
80
|
+
onDragOver,
|
|
81
|
+
onDragEnter = null,
|
|
82
|
+
onDragLeave,
|
|
83
|
+
onDrop,
|
|
84
|
+
onKeyDown = null,
|
|
85
|
+
dragState = {
|
|
86
|
+
isDragging: false,
|
|
87
|
+
draggedFrom: null,
|
|
88
|
+
draggedTo: null,
|
|
89
|
+
originalOrder: [],
|
|
90
|
+
updatedOrder: []
|
|
91
|
+
},
|
|
92
|
+
enableKeyboardNavigation = true,
|
|
93
|
+
ariaLabel = 'Draggable list',
|
|
94
|
+
ariaDescribedBy = null,
|
|
95
|
+
renderItem = null,
|
|
96
|
+
itemIdKey = 'id',
|
|
97
|
+
itemContentKey = 'title',
|
|
98
|
+
itemNumberKey = 'number'
|
|
99
|
+
}, ref) => {
|
|
100
|
+
const {
|
|
101
|
+
isDragging = false,
|
|
102
|
+
draggedFrom = null,
|
|
103
|
+
draggedTo = null
|
|
104
|
+
} = dragState;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Handles drag start events with accessibility announcements
|
|
108
|
+
*/
|
|
109
|
+
const handleDragStart = (event, item, index) => {
|
|
110
|
+
// Call parent callback
|
|
111
|
+
onDragStart?.(event, item, index);
|
|
112
|
+
|
|
113
|
+
// Set data for Firefox compatibility
|
|
114
|
+
event.dataTransfer.setData('text/html', '');
|
|
115
|
+
|
|
116
|
+
// Focus management for accessibility
|
|
117
|
+
(0, _accessibility.safeFocus)(event.currentTarget);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Handles drag over events
|
|
122
|
+
*/
|
|
123
|
+
const handleDragOver = (event, item, index) => {
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
onDragOver?.(event, item, index);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Handles drag enter events
|
|
130
|
+
*/
|
|
131
|
+
const handleDragEnter = (event, item, index) => {
|
|
132
|
+
onDragEnter?.(event, item, index);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Handles drag leave events
|
|
137
|
+
*/
|
|
138
|
+
const handleDragLeave = (event, item, index) => {
|
|
139
|
+
onDragLeave?.(event, item, index);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Handles drop events
|
|
144
|
+
*/
|
|
145
|
+
const handleDrop = (event, item, index) => {
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
onDrop?.(event, item, index);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Handles keyboard navigation for accessibility
|
|
152
|
+
*/
|
|
153
|
+
const handleKeyDown = (event, item, index) => {
|
|
154
|
+
if (!enableKeyboardNavigation) return;
|
|
155
|
+
|
|
156
|
+
// Let parent handle any custom keyboard logic
|
|
157
|
+
onKeyDown?.(event, item, index);
|
|
158
|
+
|
|
159
|
+
// Basic keyboard navigation
|
|
160
|
+
switch (event.key) {
|
|
161
|
+
case 'ArrowUp':
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
if (index > 0) {
|
|
164
|
+
const prevItem = event.currentTarget.parentElement?.previousElementSibling?.querySelector('[draggable]');
|
|
165
|
+
(0, _accessibility.safeFocus)(prevItem);
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
case 'ArrowDown':
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
if (index < items.length - 1) {
|
|
171
|
+
const nextItem = event.currentTarget.parentElement?.nextElementSibling?.querySelector('[draggable]');
|
|
172
|
+
(0, _accessibility.safeFocus)(nextItem);
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
case 'Space':
|
|
176
|
+
case 'Enter':
|
|
177
|
+
// Parent can handle drag simulation for keyboard users
|
|
178
|
+
break;
|
|
179
|
+
default:
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Default item renderer
|
|
186
|
+
*/
|
|
187
|
+
const defaultRenderItem = (item, index) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", {
|
|
188
|
+
"aria-hidden": "true"
|
|
189
|
+
}, item[itemNumberKey] || index + 1), /*#__PURE__*/_react.default.createElement("p", null, item[itemContentKey] || item.title || `Item ${index + 1}`));
|
|
190
|
+
const itemRenderer = renderItem || defaultRenderItem;
|
|
191
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
192
|
+
ref: ref,
|
|
193
|
+
className: `${componentName} ${additionalClassName}`.trim(),
|
|
194
|
+
"data-testid": componentName,
|
|
195
|
+
role: "application",
|
|
196
|
+
"aria-label": ariaLabel,
|
|
197
|
+
"aria-describedby": ariaDescribedBy
|
|
198
|
+
}, /*#__PURE__*/_react.default.createElement("ul", {
|
|
199
|
+
className: "unordered-list",
|
|
200
|
+
role: "list",
|
|
201
|
+
"aria-label": "Reorderable items"
|
|
202
|
+
}, items.map((item, index) => {
|
|
203
|
+
const isDropTarget = draggedTo === index;
|
|
204
|
+
const isBeingDragged = draggedFrom === index && isDragging;
|
|
205
|
+
const itemId = item[itemIdKey] || item.id || `item-${index}`;
|
|
206
|
+
return /*#__PURE__*/_react.default.createElement("li", {
|
|
207
|
+
key: itemId,
|
|
208
|
+
"data-position": index,
|
|
209
|
+
draggable: true,
|
|
210
|
+
onDragStart: e => handleDragStart(e, item, index),
|
|
211
|
+
onDragOver: e => handleDragOver(e, item, index),
|
|
212
|
+
onDragEnter: e => handleDragEnter(e, item, index),
|
|
213
|
+
onDrop: e => handleDrop(e, item, index),
|
|
214
|
+
onDragLeave: e => handleDragLeave(e, item, index),
|
|
215
|
+
onKeyDown: e => handleKeyDown(e, item, index),
|
|
216
|
+
className: `list-item ${isDropTarget ? 'dropArea' : ''} ${isBeingDragged ? 'being-dragged' : ''}`,
|
|
217
|
+
role: "listitem",
|
|
218
|
+
tabIndex: 0,
|
|
219
|
+
"aria-label": `${item[itemContentKey] || item.title}, position ${index + 1} of ${items.length}. Press space to start dragging.`,
|
|
220
|
+
"aria-describedby": `drag-instructions-${index}`,
|
|
221
|
+
"aria-grabbed": isBeingDragged,
|
|
222
|
+
"aria-dropeffect": isDropTarget ? 'move' : 'none'
|
|
223
|
+
}, itemRenderer(item, index), /*#__PURE__*/_react.default.createElement("div", {
|
|
224
|
+
id: `drag-instructions-${index}`,
|
|
225
|
+
className: "sr-only",
|
|
226
|
+
"aria-live": "polite"
|
|
227
|
+
}, isBeingDragged && 'Currently dragging this item', isDropTarget && 'Drop target area', !isBeingDragged && !isDropTarget && 'Use arrow keys to navigate, space to drag'));
|
|
228
|
+
})), /*#__PURE__*/_react.default.createElement("div", {
|
|
229
|
+
"aria-live": "assertive",
|
|
230
|
+
"aria-atomic": "true",
|
|
231
|
+
className: "sr-only",
|
|
232
|
+
role: "status"
|
|
233
|
+
}, isDragging && draggedFrom !== null && `Item ${draggedFrom + 1} is being dragged`));
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Set display name for better debugging
|
|
237
|
+
DragAndDrop.displayName = 'DragAndDrop';
|
|
238
|
+
|
|
239
|
+
// PropTypes definition with proper required fields
|
|
240
|
+
DragAndDrop.propTypes = {
|
|
241
|
+
/**
|
|
242
|
+
* Base CSS class name for the component
|
|
243
|
+
*/
|
|
244
|
+
componentName: _propTypes.string,
|
|
245
|
+
/**
|
|
246
|
+
* Additional CSS classes to apply
|
|
247
|
+
*/
|
|
248
|
+
additionalClassName: _propTypes.string,
|
|
249
|
+
/**
|
|
250
|
+
* Array of items to display and make draggable (REQUIRED)
|
|
251
|
+
* Each item should have at least a title/content property
|
|
252
|
+
*/
|
|
253
|
+
items: _propTypes.array.isRequired,
|
|
254
|
+
/**
|
|
255
|
+
* Callback function for drag start events (REQUIRED)
|
|
256
|
+
* @param {Event} event - The drag event
|
|
257
|
+
* @param {Object} item - The item being dragged
|
|
258
|
+
* @param {number} index - The index of the item
|
|
259
|
+
*/
|
|
260
|
+
onDragStart: _propTypes.func.isRequired,
|
|
261
|
+
/**
|
|
262
|
+
* Callback function for drag over events (REQUIRED)
|
|
263
|
+
* @param {Event} event - The drag event
|
|
264
|
+
* @param {Object} item - The item being dragged over
|
|
265
|
+
* @param {number} index - The index of the drop target
|
|
266
|
+
*/
|
|
267
|
+
onDragOver: _propTypes.func.isRequired,
|
|
268
|
+
/**
|
|
269
|
+
* Callback function for drag enter events
|
|
270
|
+
* @param {Event} event - The drag event
|
|
271
|
+
* @param {Object} item - The item being entered
|
|
272
|
+
* @param {number} index - The index of the item
|
|
273
|
+
*/
|
|
274
|
+
onDragEnter: _propTypes.func,
|
|
275
|
+
/**
|
|
276
|
+
* Callback function for drag leave events (REQUIRED)
|
|
277
|
+
* @param {Event} event - The drag event
|
|
278
|
+
* @param {Object} item - The item being left
|
|
279
|
+
* @param {number} index - The index of the item
|
|
280
|
+
*/
|
|
281
|
+
onDragLeave: _propTypes.func.isRequired,
|
|
282
|
+
/**
|
|
283
|
+
* Callback function for drop events (REQUIRED)
|
|
284
|
+
* @param {Event} event - The drop event
|
|
285
|
+
* @param {Object} item - The item being dropped on
|
|
286
|
+
* @param {number} index - The index of the drop target
|
|
287
|
+
*/
|
|
288
|
+
onDrop: _propTypes.func.isRequired,
|
|
289
|
+
/**
|
|
290
|
+
* Callback function for keyboard events
|
|
291
|
+
* @param {Event} event - The keyboard event
|
|
292
|
+
* @param {Object} item - The focused item
|
|
293
|
+
* @param {number} index - The index of the focused item
|
|
294
|
+
*/
|
|
295
|
+
onKeyDown: _propTypes.func,
|
|
296
|
+
/**
|
|
297
|
+
* Current drag and drop state object
|
|
298
|
+
*/
|
|
299
|
+
dragState: (0, _propTypes.shape)({
|
|
300
|
+
isDragging: _propTypes.bool,
|
|
301
|
+
draggedFrom: (0, _propTypes.oneOfType)([_propTypes.number, _propTypes.string]),
|
|
302
|
+
draggedTo: (0, _propTypes.oneOfType)([_propTypes.number, _propTypes.string]),
|
|
303
|
+
originalOrder: _propTypes.array,
|
|
304
|
+
updatedOrder: _propTypes.array
|
|
305
|
+
}),
|
|
306
|
+
/**
|
|
307
|
+
* Enable/disable keyboard navigation
|
|
308
|
+
*/
|
|
309
|
+
enableKeyboardNavigation: _propTypes.bool,
|
|
310
|
+
/**
|
|
311
|
+
* Accessible label for the entire component
|
|
312
|
+
*/
|
|
313
|
+
ariaLabel: _propTypes.string,
|
|
314
|
+
/**
|
|
315
|
+
* ID of element that describes the component
|
|
316
|
+
*/
|
|
317
|
+
ariaDescribedBy: _propTypes.string,
|
|
318
|
+
/**
|
|
319
|
+
* Custom item renderer function
|
|
320
|
+
* @param {Object} item - The item to render
|
|
321
|
+
* @param {number} index - The index of the item
|
|
322
|
+
* @returns {React.ReactNode} The rendered item content
|
|
323
|
+
*/
|
|
324
|
+
renderItem: _propTypes.func,
|
|
325
|
+
/**
|
|
326
|
+
* Key to use for item IDs (for React keys)
|
|
327
|
+
*/
|
|
328
|
+
itemIdKey: _propTypes.string,
|
|
329
|
+
/**
|
|
330
|
+
* Key to use for item content/title
|
|
331
|
+
*/
|
|
332
|
+
itemContentKey: _propTypes.string,
|
|
333
|
+
/**
|
|
334
|
+
* Key to use for item numbers/positions
|
|
335
|
+
*/
|
|
336
|
+
itemNumberKey: _propTypes.string
|
|
337
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Error = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _propTypes = require("prop-types");
|
|
9
|
+
require("../styles/error.scss");
|
|
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
|
+
* Error Component
|
|
14
|
+
*
|
|
15
|
+
* Displays error messages with proper accessibility support and multiple severity levels.
|
|
16
|
+
* Features ARIA live regions, keyboard navigation, and customizable styling.
|
|
17
|
+
*
|
|
18
|
+
* @component
|
|
19
|
+
* @example
|
|
20
|
+
* ```jsx
|
|
21
|
+
* <Error
|
|
22
|
+
* severity="error"
|
|
23
|
+
* size="medium"
|
|
24
|
+
* dismissible={true}
|
|
25
|
+
* onDismiss={() => setShowError(false)}
|
|
26
|
+
* aria-label="Form validation error"
|
|
27
|
+
* >
|
|
28
|
+
* Please fill in all required fields
|
|
29
|
+
* </Error>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @param {object} props - Component props
|
|
33
|
+
* @param {React.ReactNode} props.children - The error message content to display
|
|
34
|
+
* @param {string} [props.className=''] - Additional CSS classes to apply
|
|
35
|
+
* @param {string} [props.id] - Unique identifier for the component
|
|
36
|
+
* @param {string} [props.severity='error'] - Error severity level
|
|
37
|
+
* @param {string} [props.size='medium'] - Size variant
|
|
38
|
+
* @param {boolean} [props.dismissible=false] - Whether the error can be dismissed
|
|
39
|
+
* @param {function} [props.onDismiss] - Callback when error is dismissed
|
|
40
|
+
* @returns {JSX.Element} The Error component
|
|
41
|
+
*/
|
|
42
|
+
const Error = exports.Error = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
43
|
+
// Core props
|
|
44
|
+
id,
|
|
45
|
+
className = '',
|
|
46
|
+
// Content props
|
|
47
|
+
children = '',
|
|
48
|
+
// Appearance props
|
|
49
|
+
severity = 'error',
|
|
50
|
+
size = 'medium',
|
|
51
|
+
// Behavior props
|
|
52
|
+
dismissible = false,
|
|
53
|
+
isLive = true,
|
|
54
|
+
// Event props
|
|
55
|
+
onDismiss = null,
|
|
56
|
+
// Accessibility props
|
|
57
|
+
'aria-label': ariaLabel = '',
|
|
58
|
+
role = '',
|
|
59
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
60
|
+
componentName = 'error',
|
|
61
|
+
additionalClassName = '',
|
|
62
|
+
...restProps
|
|
63
|
+
}, ref) => {
|
|
64
|
+
// Handle legacy prop mapping
|
|
65
|
+
const finalId = id || `error-${Math.random().toString(36).substr(2, 9)}`;
|
|
66
|
+
const finalClassName = className || additionalClassName;
|
|
67
|
+
|
|
68
|
+
// Determine the appropriate ARIA role based on severity
|
|
69
|
+
const getAriaRole = () => {
|
|
70
|
+
if (role) return role;
|
|
71
|
+
switch (severity) {
|
|
72
|
+
case 'error':
|
|
73
|
+
return 'alert';
|
|
74
|
+
case 'warning':
|
|
75
|
+
return 'alert';
|
|
76
|
+
case 'info':
|
|
77
|
+
return 'status';
|
|
78
|
+
case 'success':
|
|
79
|
+
return 'status';
|
|
80
|
+
default:
|
|
81
|
+
return 'alert';
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Determine aria-live setting based on severity and isLive prop
|
|
86
|
+
const getAriaLive = () => {
|
|
87
|
+
if (!isLive) return 'off';
|
|
88
|
+
switch (severity) {
|
|
89
|
+
case 'error':
|
|
90
|
+
return 'assertive';
|
|
91
|
+
case 'warning':
|
|
92
|
+
return 'assertive';
|
|
93
|
+
case 'info':
|
|
94
|
+
return 'polite';
|
|
95
|
+
case 'success':
|
|
96
|
+
return 'polite';
|
|
97
|
+
default:
|
|
98
|
+
return 'assertive';
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Generate comprehensive class names
|
|
103
|
+
const classNames = [componentName, `${componentName}--${severity}`, `${componentName}--${size}`, finalClassName].filter(Boolean).join(' ');
|
|
104
|
+
return /*#__PURE__*/_react.default.createElement("div", _extends({
|
|
105
|
+
ref: ref,
|
|
106
|
+
className: classNames,
|
|
107
|
+
"data-testid": componentName,
|
|
108
|
+
id: finalId,
|
|
109
|
+
role: getAriaRole(),
|
|
110
|
+
"aria-live": getAriaLive(),
|
|
111
|
+
"aria-atomic": "true",
|
|
112
|
+
"aria-label": ariaLabel || undefined
|
|
113
|
+
}, restProps), /*#__PURE__*/_react.default.createElement("span", {
|
|
114
|
+
className: `${componentName}__icon`,
|
|
115
|
+
"aria-hidden": "true"
|
|
116
|
+
}, severity === 'error' && '⚠️', severity === 'warning' && '⚠️', severity === 'info' && 'ℹ️', severity === 'success' && '✅'), /*#__PURE__*/_react.default.createElement("span", {
|
|
117
|
+
className: `${componentName}__text`
|
|
118
|
+
}, children), dismissible && /*#__PURE__*/_react.default.createElement("button", {
|
|
119
|
+
type: "button",
|
|
120
|
+
className: `${componentName}__dismiss`,
|
|
121
|
+
onClick: onDismiss,
|
|
122
|
+
"aria-label": "Dismiss message"
|
|
123
|
+
}, "\xD7"));
|
|
124
|
+
});
|
|
125
|
+
Error.propTypes = {
|
|
126
|
+
// ====================
|
|
127
|
+
// Core Props
|
|
128
|
+
// ====================
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Unique identifier for the component
|
|
132
|
+
*/
|
|
133
|
+
id: _propTypes.string,
|
|
134
|
+
/**
|
|
135
|
+
* Additional CSS classes to apply to the component
|
|
136
|
+
*/
|
|
137
|
+
className: _propTypes.string,
|
|
138
|
+
// ====================
|
|
139
|
+
// Content Props
|
|
140
|
+
// ====================
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The error message content to display
|
|
144
|
+
* @required for meaningful error messages
|
|
145
|
+
*/
|
|
146
|
+
children: _propTypes.node.isRequired,
|
|
147
|
+
// ====================
|
|
148
|
+
// Appearance Props
|
|
149
|
+
// ====================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Error severity level affecting styling and ARIA behavior
|
|
153
|
+
* @default 'error'
|
|
154
|
+
*/
|
|
155
|
+
severity: (0, _propTypes.oneOf)(['error', 'warning', 'info', 'success']),
|
|
156
|
+
/**
|
|
157
|
+
* Size variant for responsive display
|
|
158
|
+
* @default 'medium'
|
|
159
|
+
*/
|
|
160
|
+
size: (0, _propTypes.oneOf)(['small', 'medium', 'large']),
|
|
161
|
+
// ====================
|
|
162
|
+
// Behavior Props
|
|
163
|
+
// ====================
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Whether the error can be dismissed by the user
|
|
167
|
+
* @default false
|
|
168
|
+
*/
|
|
169
|
+
dismissible: _propTypes.bool,
|
|
170
|
+
/**
|
|
171
|
+
* Whether to use aria-live for real-time announcements
|
|
172
|
+
* @default true
|
|
173
|
+
*/
|
|
174
|
+
isLive: _propTypes.bool,
|
|
175
|
+
// ====================
|
|
176
|
+
// Event Props
|
|
177
|
+
// ====================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Callback function when error is dismissed
|
|
181
|
+
*/
|
|
182
|
+
onDismiss: _propTypes.func,
|
|
183
|
+
// ====================
|
|
184
|
+
// Accessibility Props
|
|
185
|
+
// ====================
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* ARIA label for screen readers (optional, uses children if not provided)
|
|
189
|
+
*/
|
|
190
|
+
'aria-label': _propTypes.string,
|
|
191
|
+
/**
|
|
192
|
+
* ARIA role override (auto-determined from severity if not provided)
|
|
193
|
+
*/
|
|
194
|
+
role: _propTypes.string,
|
|
195
|
+
/**
|
|
196
|
+
* Use `className` instead
|
|
197
|
+
* Additional CSS class names to apply
|
|
198
|
+
*/
|
|
199
|
+
additionalClassName: _propTypes.string,
|
|
200
|
+
/**
|
|
201
|
+
* Internal prop for CSS class generation
|
|
202
|
+
* The base component name for CSS classes
|
|
203
|
+
*/
|
|
204
|
+
componentName: _propTypes.string
|
|
205
|
+
};
|
|
206
|
+
var _default = exports.default = Error;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Footer = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _propTypes = require("prop-types");
|
|
9
|
+
var _useWindowSize = require("../utils/useWindowSize");
|
|
10
|
+
var _Animation = require("./Animation");
|
|
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
|
+
* Footer Component
|
|
15
|
+
*
|
|
16
|
+
* A responsive footer component that adapts between desktop and mobile layouts.
|
|
17
|
+
* Features navigation display and optional authentication links.
|
|
18
|
+
*/
|
|
19
|
+
const Footer = exports.Footer = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
20
|
+
id,
|
|
21
|
+
className = '',
|
|
22
|
+
navigation = null,
|
|
23
|
+
signInLink = null,
|
|
24
|
+
signUpLink = null,
|
|
25
|
+
avatarSrc = null,
|
|
26
|
+
ellipsesList = [],
|
|
27
|
+
hasAuth = false,
|
|
28
|
+
'aria-label': ariaLabel = 'Footer',
|
|
29
|
+
componentName = 'footer',
|
|
30
|
+
additionalClassName = '',
|
|
31
|
+
...restProps
|
|
32
|
+
}, ref) => {
|
|
33
|
+
const size = (0, _useWindowSize.useWindowSize)();
|
|
34
|
+
const finalId = id || `footer-${Math.random().toString(36).substr(2, 9)}`;
|
|
35
|
+
const finalClassName = className || additionalClassName;
|
|
36
|
+
return /*#__PURE__*/_react.default.createElement("footer", _extends({
|
|
37
|
+
ref: ref,
|
|
38
|
+
id: finalId,
|
|
39
|
+
className: `${componentName} ${finalClassName}`,
|
|
40
|
+
"data-testid": componentName,
|
|
41
|
+
role: "contentinfo",
|
|
42
|
+
"aria-label": ariaLabel
|
|
43
|
+
}, restProps), /*#__PURE__*/_react.default.createElement(_Animation.AnimatedDiv, {
|
|
44
|
+
fadingEntrances: "fadeIn",
|
|
45
|
+
duration: "faster"
|
|
46
|
+
}, size.width < 768 ?
|
|
47
|
+
/*#__PURE__*/
|
|
48
|
+
// mobile
|
|
49
|
+
_react.default.createElement("div", {
|
|
50
|
+
className: "navigation",
|
|
51
|
+
role: "navigation",
|
|
52
|
+
"aria-label": "Mobile footer navigation"
|
|
53
|
+
}, !hasAuth ? /*#__PURE__*/_react.default.createElement("ul", {
|
|
54
|
+
className: "unordered-list",
|
|
55
|
+
role: "list"
|
|
56
|
+
}, navigation) : /*#__PURE__*/_react.default.createElement("ul", {
|
|
57
|
+
className: "unordered-list auth-menu",
|
|
58
|
+
role: "list",
|
|
59
|
+
"aria-label": "User navigation"
|
|
60
|
+
}, /*#__PURE__*/_react.default.createElement("li", {
|
|
61
|
+
className: "list-item",
|
|
62
|
+
role: "listitem"
|
|
63
|
+
}, "Home"), /*#__PURE__*/_react.default.createElement("li", {
|
|
64
|
+
className: "list-item",
|
|
65
|
+
role: "listitem"
|
|
66
|
+
}, "Search"), /*#__PURE__*/_react.default.createElement("li", {
|
|
67
|
+
className: "list-item",
|
|
68
|
+
role: "listitem"
|
|
69
|
+
}, "Add"), /*#__PURE__*/_react.default.createElement("li", {
|
|
70
|
+
className: "list-item",
|
|
71
|
+
role: "listitem"
|
|
72
|
+
}, "Profile"))) :
|
|
73
|
+
/*#__PURE__*/
|
|
74
|
+
// desktop
|
|
75
|
+
_react.default.createElement("div", {
|
|
76
|
+
className: "navigation",
|
|
77
|
+
"data-testid": "navigation",
|
|
78
|
+
role: "navigation",
|
|
79
|
+
"aria-label": "Desktop footer navigation"
|
|
80
|
+
}, /*#__PURE__*/_react.default.createElement("ul", {
|
|
81
|
+
className: "unordered-list",
|
|
82
|
+
role: "list"
|
|
83
|
+
}, navigation))));
|
|
84
|
+
});
|
|
85
|
+
Footer.displayName = 'Footer';
|
|
86
|
+
Footer.propTypes = {
|
|
87
|
+
id: _propTypes.string,
|
|
88
|
+
className: _propTypes.string,
|
|
89
|
+
navigation: _propTypes.node,
|
|
90
|
+
signInLink: _propTypes.node,
|
|
91
|
+
signUpLink: _propTypes.node,
|
|
92
|
+
avatarSrc: _propTypes.string,
|
|
93
|
+
ellipsesList: (0, _propTypes.arrayOf)(_propTypes.node),
|
|
94
|
+
hasAuth: _propTypes.bool,
|
|
95
|
+
'aria-label': _propTypes.string,
|
|
96
|
+
componentName: _propTypes.string,
|
|
97
|
+
additionalClassName: _propTypes.string
|
|
98
|
+
};
|
|
99
|
+
var _default = exports.default = Footer;
|