@mezzanine-ui/react 1.0.0-beta.6 → 1.0.0-beta.7
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/Accordion/Accordion.d.ts +23 -1
- package/Accordion/Accordion.js +59 -11
- package/Accordion/AccordionActions.d.ts +13 -0
- package/Accordion/AccordionActions.js +24 -0
- package/Accordion/AccordionContent.d.ts +9 -0
- package/Accordion/{AccordionDetails.js → AccordionContent.js} +4 -6
- package/Accordion/AccordionControlContext.d.ts +2 -2
- package/Accordion/AccordionGroup.d.ts +10 -0
- package/Accordion/AccordionGroup.js +26 -0
- package/Accordion/AccordionTitle.d.ts +14 -0
- package/Accordion/AccordionTitle.js +56 -0
- package/Accordion/index.d.ts +8 -4
- package/Accordion/index.js +4 -2
- package/AutoComplete/AutoComplete.d.ts +20 -6
- package/AutoComplete/AutoComplete.js +118 -30
- package/Backdrop/Backdrop.js +15 -19
- package/Calendar/CalendarDays.js +1 -1
- package/Card/BaseCard.d.ts +11 -0
- package/Card/BaseCard.js +48 -0
- package/Card/BaseCardSkeleton.d.ts +14 -0
- package/Card/BaseCardSkeleton.js +18 -0
- package/Card/CardGroup.d.ts +47 -0
- package/Card/CardGroup.js +147 -0
- package/Card/FourThumbnailCard.d.ts +14 -0
- package/Card/FourThumbnailCard.js +73 -0
- package/Card/FourThumbnailCardSkeleton.d.ts +14 -0
- package/Card/FourThumbnailCardSkeleton.js +20 -0
- package/Card/QuickActionCard.d.ts +12 -0
- package/Card/QuickActionCard.js +23 -0
- package/Card/QuickActionCardSkeleton.d.ts +14 -0
- package/Card/QuickActionCardSkeleton.js +18 -0
- package/Card/SingleThumbnailCard.d.ts +13 -0
- package/Card/SingleThumbnailCard.js +44 -0
- package/Card/SingleThumbnailCardSkeleton.d.ts +19 -0
- package/Card/SingleThumbnailCardSkeleton.js +18 -0
- package/Card/Thumbnail.d.ts +12 -0
- package/Card/Thumbnail.js +18 -0
- package/Card/ThumbnailCardInfo.d.ts +34 -0
- package/Card/ThumbnailCardInfo.js +43 -0
- package/Card/index.d.ts +43 -4
- package/Card/index.js +19 -2
- package/Card/typings.d.ts +442 -0
- package/Checkbox/Checkbox.d.ts +8 -0
- package/Checkbox/Checkbox.js +3 -2
- package/Checkbox/CheckboxGroup.js +1 -1
- package/ContentHeader/ContentHeader.d.ts +22 -70
- package/ContentHeader/ContentHeader.js +1 -1
- package/ContentHeader/ContentHeaderResponsive.d.ts +9 -0
- package/ContentHeader/ContentHeaderResponsive.js +7 -0
- package/ContentHeader/utils.d.ts +3 -3
- package/ContentHeader/utils.js +66 -20
- package/Cropper/Cropper.d.ts +66 -0
- package/Cropper/Cropper.js +115 -0
- package/Cropper/CropperElement.d.ts +10 -0
- package/Cropper/CropperElement.js +892 -0
- package/Cropper/index.d.ts +18 -0
- package/Cropper/index.js +8 -0
- package/Cropper/tools.d.ts +90 -0
- package/Cropper/tools.js +143 -0
- package/Cropper/typings.d.ts +69 -0
- package/Cropper/utils/cropper-calculations.d.ts +39 -0
- package/Cropper/utils/cropper-calculations.js +95 -0
- package/DateTimePicker/DateTimePicker.d.ts +1 -1
- package/DateTimePicker/DateTimePicker.js +14 -1
- package/Dropdown/Dropdown.d.ts +7 -1
- package/Dropdown/Dropdown.js +31 -14
- package/Dropdown/DropdownItem.d.ts +7 -1
- package/Dropdown/DropdownItem.js +36 -6
- package/Dropdown/DropdownItemCard.js +2 -1
- package/FloatingButton/FloatingButton.d.ts +21 -0
- package/FloatingButton/FloatingButton.js +18 -0
- package/FloatingButton/index.d.ts +2 -0
- package/FloatingButton/index.js +1 -0
- package/Form/FormField.d.ts +21 -10
- package/Form/FormField.js +12 -4
- package/Input/Input.js +9 -2
- package/Message/Message.js +1 -1
- package/MultipleDatePicker/MultipleDatePicker.js +2 -2
- package/Navigation/NavigationHeader.js +1 -1
- package/Picker/FormattedInput.d.ts +1 -1
- package/Picker/FormattedInput.js +2 -1
- package/Picker/PickerTriggerWithSeparator.d.ts +10 -0
- package/Picker/PickerTriggerWithSeparator.js +2 -2
- package/Picker/useDateInputFormatter.d.ts +6 -0
- package/Picker/useDateInputFormatter.js +4 -1
- package/Select/Select.d.ts +2 -8
- package/Select/Select.js +12 -33
- package/Select/SelectTrigger.js +21 -7
- package/Select/index.d.ts +0 -4
- package/Select/index.js +0 -2
- package/Select/typings.d.ts +0 -4
- package/Select/useSelectTriggerTags.d.ts +1 -1
- package/Select/useSelectTriggerTags.js +9 -6
- package/Separator/Separator.d.ts +14 -0
- package/Separator/Separator.js +17 -0
- package/Separator/index.d.ts +2 -0
- package/Separator/index.js +1 -0
- package/Table/utils/useTableRowSelection.js +6 -0
- package/Tag/TagGroup.d.ts +4 -2
- package/Tag/TagGroup.js +7 -4
- package/TextField/TextField.d.ts +1 -1
- package/TextField/TextField.js +63 -9
- package/TimePanel/TimePanelColumn.js +19 -12
- package/index.d.ts +27 -28
- package/index.js +23 -25
- package/package.json +4 -4
- package/Accordion/AccordionDetails.d.ts +0 -9
- package/Accordion/AccordionSummary.d.ts +0 -18
- package/Accordion/AccordionSummary.js +0 -51
- package/Alert/Alert.d.ts +0 -20
- package/Alert/Alert.js +0 -18
- package/Alert/index.d.ts +0 -3
- package/Alert/index.js +0 -1
- package/Card/Card.d.ts +0 -51
- package/Card/Card.js +0 -20
- package/Card/CardActions.d.ts +0 -34
- package/Card/CardActions.js +0 -15
- package/ConfirmActions/ConfirmActions.d.ts +0 -46
- package/ConfirmActions/ConfirmActions.js +0 -15
- package/ConfirmActions/index.d.ts +0 -2
- package/ConfirmActions/index.js +0 -1
- package/Select/Option.d.ts +0 -18
- package/Select/Option.js +0 -45
- package/Select/TreeSelect.d.ts +0 -72
- package/Select/TreeSelect.js +0 -205
- package/Tree/Tree.d.ts +0 -70
- package/Tree/Tree.js +0 -139
- package/Tree/TreeNode.d.ts +0 -40
- package/Tree/TreeNode.js +0 -50
- package/Tree/TreeNodeList.d.ts +0 -24
- package/Tree/TreeNodeList.js +0 -28
- package/Tree/getTreeNodeEntities.d.ts +0 -11
- package/Tree/getTreeNodeEntities.js +0 -92
- package/Tree/index.d.ts +0 -13
- package/Tree/index.js +0 -7
- package/Tree/toggleValue.d.ts +0 -4
- package/Tree/toggleValue.js +0 -19
- package/Tree/traverseTree.d.ts +0 -2
- package/Tree/traverseTree.js +0 -11
- package/Tree/typings.d.ts +0 -16
- package/Tree/useTreeExpandedValue.d.ts +0 -14
- package/Tree/useTreeExpandedValue.js +0 -33
|
@@ -15,6 +15,7 @@ import Dropdown from '../Dropdown/Dropdown.js';
|
|
|
15
15
|
import cx from 'clsx';
|
|
16
16
|
|
|
17
17
|
const MENU_ID_PREFIX = 'mzn-select-autocomplete-menu-id';
|
|
18
|
+
const BLUR_RESET_OPTIONS_DELAY = 120;
|
|
18
19
|
/**
|
|
19
20
|
* Type guard to check if value is array (multiple mode)
|
|
20
21
|
*/
|
|
@@ -46,7 +47,8 @@ function isOptionSelected(option, value, isMultiple) {
|
|
|
46
47
|
*/
|
|
47
48
|
const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
48
49
|
const { disabled: disabledFromFormControl, fullWidth: fullWidthFromFormControl, required: requiredFromFormControl, severity, } = useContext(FormControlContext) || {};
|
|
49
|
-
const { addable = false, asyncData = false, className, createSeparators = [',', '+', '\n'], defaultValue, disabled = disabledFromFormControl || false, disabledOptionsFilter = false, emptyText, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, id,
|
|
50
|
+
const { addable = false, asyncData = false, className, clearSearchText = true, createSeparators = [',', '+', '\n'], defaultValue, disabled = disabledFromFormControl || false, disabledOptionsFilter = false, emptyText, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, id, inputPosition = 'outside', inputProps, inputRef, loading = false, loadingText, loadingPosition = 'bottom', menuMaxHeight, mode = 'single', name, onClear: onClearProp, onChange: onChangeProp, onInsert, onSearch, onSearchTextChange, onVisibilityChange, open: openProp, options: optionsProp, overflowStrategy, placeholder = '', prefix, required = requiredFromFormControl || false, searchDebounceTime = 300, searchTextControlRef, size, trimOnCreate = true, value: valueProp, createActionText, createActionTextTemplate = '建立 "{text}"', dropdownZIndex, globalPortal = true, onReachBottom, onLeaveBottom, } = props;
|
|
51
|
+
const shouldClearSearchTextOnBlur = clearSearchText;
|
|
50
52
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
|
|
51
53
|
const isMultiple = mode === 'multiple';
|
|
52
54
|
const isSingle = !isMultiple;
|
|
@@ -142,20 +144,51 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
142
144
|
return result;
|
|
143
145
|
}, [clearNewlyCreated, isMultiple, isSingle, markUnselected, onChange, value]);
|
|
144
146
|
const nodeRef = useRef(null);
|
|
147
|
+
const inputElementRef = useRef(null);
|
|
145
148
|
const controlRef = useRef(null);
|
|
149
|
+
const resetOptionsTimeoutRef = useRef(null);
|
|
150
|
+
const skipNextMultipleCloseResetRef = useRef(false);
|
|
146
151
|
const composedRef = useComposeRefs([ref, controlRef]);
|
|
152
|
+
const composedInputRef = useComposeRefs([inputRef, inputElementRef]);
|
|
153
|
+
const clearPendingOptionsReset = useCallback(() => {
|
|
154
|
+
if (resetOptionsTimeoutRef.current !== null) {
|
|
155
|
+
window.clearTimeout(resetOptionsTimeoutRef.current);
|
|
156
|
+
resetOptionsTimeoutRef.current = null;
|
|
157
|
+
}
|
|
158
|
+
}, []);
|
|
159
|
+
const clearSearchInputDisplay = useCallback(() => {
|
|
160
|
+
if (inputElementRef.current) {
|
|
161
|
+
inputElementRef.current.value = '';
|
|
162
|
+
}
|
|
163
|
+
}, []);
|
|
164
|
+
const resetSearchInputs = useCallback(() => {
|
|
165
|
+
resetCreationInputs();
|
|
166
|
+
clearSearchInputDisplay();
|
|
167
|
+
}, [clearSearchInputDisplay, resetCreationInputs]);
|
|
168
|
+
const resetSearchInputsAndOptions = useCallback(() => {
|
|
169
|
+
resetSearchInputs();
|
|
170
|
+
clearPendingOptionsReset();
|
|
171
|
+
cancelSearch();
|
|
172
|
+
resetOptionsTimeoutRef.current = window.setTimeout(() => {
|
|
173
|
+
runSearch('', { immediate: true });
|
|
174
|
+
resetOptionsTimeoutRef.current = null;
|
|
175
|
+
}, BLUR_RESET_OPTIONS_DELAY);
|
|
176
|
+
}, [cancelSearch, clearPendingOptionsReset, resetSearchInputs, runSearch]);
|
|
177
|
+
useEffect(() => () => {
|
|
178
|
+
clearPendingOptionsReset();
|
|
179
|
+
}, [clearPendingOptionsReset]);
|
|
147
180
|
// In single mode, show searchText when focused, otherwise show selected value
|
|
148
181
|
// In multiple mode, always return empty string to avoid displaying "0"
|
|
149
182
|
const renderValue = useMemo(() => {
|
|
150
183
|
if (isSingle
|
|
151
|
-
&& (focused || (
|
|
184
|
+
&& (focused || (!shouldClearSearchTextOnBlur && !value && searchText))) {
|
|
152
185
|
return () => searchText;
|
|
153
186
|
}
|
|
154
187
|
if (isMultiple) {
|
|
155
188
|
return () => '';
|
|
156
189
|
}
|
|
157
190
|
return undefined;
|
|
158
|
-
}, [focused, isMultiple, isSingle,
|
|
191
|
+
}, [focused, isMultiple, isSingle, searchText, shouldClearSearchTextOnBlur, value]);
|
|
159
192
|
function getPlaceholder() {
|
|
160
193
|
if (isSingle && focused && isSingleValue(value)) {
|
|
161
194
|
return value.name;
|
|
@@ -164,6 +197,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
164
197
|
}
|
|
165
198
|
/** Trigger input props */
|
|
166
199
|
const onSearchInputChange = (e) => {
|
|
200
|
+
clearPendingOptionsReset();
|
|
167
201
|
const nextSearch = e.target.value;
|
|
168
202
|
/** should sync both search input and value */
|
|
169
203
|
setSearchText(nextSearch);
|
|
@@ -180,6 +214,8 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
180
214
|
};
|
|
181
215
|
const onSearchInputFocus = (e) => {
|
|
182
216
|
var _a;
|
|
217
|
+
skipNextMultipleCloseResetRef.current = false;
|
|
218
|
+
clearPendingOptionsReset();
|
|
183
219
|
// When inputPosition is inside, let Dropdown handle the focus event
|
|
184
220
|
// Otherwise, stop propagation to prevent conflicts
|
|
185
221
|
if (inputPosition !== 'inside') {
|
|
@@ -195,6 +231,9 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
195
231
|
};
|
|
196
232
|
const onSearchInputBlur = (e) => {
|
|
197
233
|
var _a, _b, _c;
|
|
234
|
+
// In multiple mode while dropdown is open, defer clearing to visibility change.
|
|
235
|
+
// This prevents clearing on intermediate blur triggered by dropdown interactions.
|
|
236
|
+
const shouldDeferMultipleBlurReset = isMultiple && open;
|
|
198
237
|
// When inputPosition is inside, we need special handling
|
|
199
238
|
if (inputPosition === 'inside') {
|
|
200
239
|
// When open is controlled, prevent default blur behavior to avoid conflicts
|
|
@@ -203,33 +242,33 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
203
242
|
// Don't let Dropdown's onBlur close the dropdown when controlled
|
|
204
243
|
// Only call onFocus(false) to update internal state
|
|
205
244
|
onFocus(false);
|
|
206
|
-
// Clear search text and insert text when blur
|
|
207
|
-
if (!
|
|
208
|
-
|
|
245
|
+
// Clear search text and insert text when blur clear behavior is enabled
|
|
246
|
+
if (shouldClearSearchTextOnBlur && !shouldDeferMultipleBlurReset) {
|
|
247
|
+
resetSearchInputsAndOptions();
|
|
209
248
|
}
|
|
210
249
|
(_a = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onBlur) === null || _a === void 0 ? void 0 : _a.call(inputProps, e);
|
|
211
250
|
return;
|
|
212
251
|
}
|
|
213
252
|
// For uncontrolled mode, let Dropdown handle it normally
|
|
214
253
|
// Dropdown's inlineTriggerElement will handle the blur and close logic
|
|
215
|
-
// Clear search text and insert text when blur
|
|
216
|
-
if (!
|
|
217
|
-
|
|
254
|
+
// Clear search text and insert text when blur clear behavior is enabled
|
|
255
|
+
if (shouldClearSearchTextOnBlur && !shouldDeferMultipleBlurReset) {
|
|
256
|
+
resetSearchInputsAndOptions();
|
|
218
257
|
}
|
|
219
258
|
(_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onBlur) === null || _b === void 0 ? void 0 : _b.call(inputProps, e);
|
|
220
259
|
return;
|
|
221
260
|
}
|
|
222
261
|
onFocus(false);
|
|
223
|
-
// Clear search text and insert text when blur
|
|
224
|
-
if (!
|
|
225
|
-
|
|
262
|
+
// Clear search text and insert text when blur clear behavior is enabled
|
|
263
|
+
if (shouldClearSearchTextOnBlur && !shouldDeferMultipleBlurReset) {
|
|
264
|
+
resetSearchInputsAndOptions();
|
|
226
265
|
}
|
|
227
266
|
(_c = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onBlur) === null || _c === void 0 ? void 0 : _c.call(inputProps, e);
|
|
228
267
|
};
|
|
229
268
|
const handleClear = useCallback((e) => {
|
|
230
269
|
onClear(e);
|
|
231
|
-
|
|
232
|
-
}, [onClear,
|
|
270
|
+
resetSearchInputs();
|
|
271
|
+
}, [onClear, resetSearchInputs]);
|
|
233
272
|
const onClickSuffixActionIcon = () => {
|
|
234
273
|
toggleOpen((prev) => !prev);
|
|
235
274
|
};
|
|
@@ -270,15 +309,9 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
270
309
|
}, [mode, value]);
|
|
271
310
|
// Disable input when loading
|
|
272
311
|
const isInputDisabled = disabled || isLoading;
|
|
273
|
-
// For rendering: when loading, force options to empty to show loading status in Dropdown
|
|
274
|
-
const dropdownOptionsForRender = useMemo(() => {
|
|
275
|
-
if (isLoading)
|
|
276
|
-
return [];
|
|
277
|
-
return dropdownOptions;
|
|
278
|
-
}, [isLoading, dropdownOptions]);
|
|
279
312
|
const dropdownStatus = isLoading
|
|
280
313
|
? 'loading'
|
|
281
|
-
:
|
|
314
|
+
: dropdownOptions.length === 0
|
|
282
315
|
? 'empty'
|
|
283
316
|
: undefined;
|
|
284
317
|
// Handle dropdown option selection
|
|
@@ -296,13 +329,24 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
296
329
|
onFocus(false);
|
|
297
330
|
}
|
|
298
331
|
else {
|
|
332
|
+
skipNextMultipleCloseResetRef.current = true;
|
|
299
333
|
wrappedOnChange(selectedValue);
|
|
334
|
+
// In multiple mode, keep current filter text after selecting an item.
|
|
335
|
+
// Search text is cleared only when blur/close actually leaves the scope.
|
|
300
336
|
}
|
|
301
337
|
}
|
|
302
|
-
}, [
|
|
338
|
+
}, [
|
|
339
|
+
mode,
|
|
340
|
+
onFocus,
|
|
341
|
+
options,
|
|
342
|
+
setSearchText,
|
|
343
|
+
setInsertText,
|
|
344
|
+
toggleOpen,
|
|
345
|
+
wrappedOnChange,
|
|
346
|
+
]);
|
|
303
347
|
// Active index for dropdown keyboard navigation
|
|
304
348
|
const [activeIndex, setActiveIndex] = useState(null);
|
|
305
|
-
const setListboxHasVisualFocus = useCallback(() => { }, []);
|
|
349
|
+
const setListboxHasVisualFocus = useCallback((_focus) => { }, []);
|
|
306
350
|
// Reset activeIndex when options change
|
|
307
351
|
useEffect(() => {
|
|
308
352
|
if (!dropdownOptions.length) {
|
|
@@ -339,7 +383,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
339
383
|
handleBulkCreate,
|
|
340
384
|
handleDropdownSelect,
|
|
341
385
|
inputPropsOnKeyDown: inputProps === null || inputProps === void 0 ? void 0 : inputProps.onKeyDown,
|
|
342
|
-
inputRef,
|
|
386
|
+
inputRef: inputElementRef,
|
|
343
387
|
mode,
|
|
344
388
|
onFocus,
|
|
345
389
|
open,
|
|
@@ -354,13 +398,59 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
354
398
|
value,
|
|
355
399
|
wrappedOnChange,
|
|
356
400
|
});
|
|
401
|
+
const onSearchInputKeyDown = useCallback((e) => {
|
|
402
|
+
var _a, _b;
|
|
403
|
+
if (e.key === 'Escape') {
|
|
404
|
+
e.preventDefault();
|
|
405
|
+
e.stopPropagation();
|
|
406
|
+
toggleOpen(false);
|
|
407
|
+
setActiveIndex(null);
|
|
408
|
+
setListboxHasVisualFocus(false);
|
|
409
|
+
onFocus(false);
|
|
410
|
+
(_a = inputElementRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
411
|
+
(_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onKeyDown) === null || _b === void 0 ? void 0 : _b.call(inputProps, e);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
handleInputKeyDown(e);
|
|
415
|
+
}, [
|
|
416
|
+
handleInputKeyDown,
|
|
417
|
+
inputProps,
|
|
418
|
+
onFocus,
|
|
419
|
+
setActiveIndex,
|
|
420
|
+
setListboxHasVisualFocus,
|
|
421
|
+
toggleOpen,
|
|
422
|
+
]);
|
|
357
423
|
// Handle visibility change from Dropdown to prevent flickering
|
|
358
424
|
const handleVisibilityChange = useCallback((newOpen) => {
|
|
425
|
+
var _a;
|
|
359
426
|
// Only update if state actually changed to prevent flickering
|
|
360
427
|
if (newOpen !== open) {
|
|
361
428
|
toggleOpen(newOpen);
|
|
362
429
|
}
|
|
363
|
-
|
|
430
|
+
// In multiple mode, blur/close can be driven by Dropdown visibility updates.
|
|
431
|
+
// Keep input text cleanup consistent even when native input blur is not triggered.
|
|
432
|
+
if (!newOpen && isMultiple && shouldClearSearchTextOnBlur) {
|
|
433
|
+
if (skipNextMultipleCloseResetRef.current) {
|
|
434
|
+
const menuElement = document.getElementById(menuId);
|
|
435
|
+
const activeElement = document.activeElement;
|
|
436
|
+
const activeWithinHost = !!(activeElement
|
|
437
|
+
&& (((_a = nodeRef.current) === null || _a === void 0 ? void 0 : _a.contains(activeElement))
|
|
438
|
+
|| (menuElement && menuElement.contains(activeElement))));
|
|
439
|
+
skipNextMultipleCloseResetRef.current = false;
|
|
440
|
+
if (activeWithinHost) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
resetSearchInputsAndOptions();
|
|
445
|
+
}
|
|
446
|
+
}, [
|
|
447
|
+
isMultiple,
|
|
448
|
+
menuId,
|
|
449
|
+
open,
|
|
450
|
+
resetSearchInputsAndOptions,
|
|
451
|
+
shouldClearSearchTextOnBlur,
|
|
452
|
+
toggleOpen,
|
|
453
|
+
]);
|
|
364
454
|
const handlePasteWithFallback = useCallback((e) => {
|
|
365
455
|
var _a;
|
|
366
456
|
handlePaste(e);
|
|
@@ -388,7 +478,6 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
388
478
|
const alreadySelected = isOptionSelected(matchingOption, value, isMultiple);
|
|
389
479
|
if (!alreadySelected) {
|
|
390
480
|
wrappedOnChange(matchingOption);
|
|
391
|
-
resetCreationInputs();
|
|
392
481
|
return true;
|
|
393
482
|
}
|
|
394
483
|
return false;
|
|
@@ -398,7 +487,6 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
398
487
|
isSingle,
|
|
399
488
|
onFocus,
|
|
400
489
|
options,
|
|
401
|
-
resetCreationInputs,
|
|
402
490
|
setSearchText,
|
|
403
491
|
setInsertText,
|
|
404
492
|
toggleOpen,
|
|
@@ -416,7 +504,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
416
504
|
onBlur: onSearchInputBlur,
|
|
417
505
|
onChange: onSearchInputChange,
|
|
418
506
|
onFocus: onSearchInputFocus,
|
|
419
|
-
onKeyDown:
|
|
507
|
+
onKeyDown: onSearchInputKeyDown,
|
|
420
508
|
onPaste: handlePasteWithFallback,
|
|
421
509
|
readOnly: false,
|
|
422
510
|
role: 'combobox',
|
|
@@ -429,9 +517,9 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
|
|
|
429
517
|
? (createActionText
|
|
430
518
|
? createActionText(insertText)
|
|
431
519
|
: createActionTextTemplate.replace('{text}', insertText))
|
|
432
|
-
: undefined, activeIndex: activeIndex, disabled: isInputDisabled, emptyText: emptyText, followText: searchText, inputPosition: inputPosition, isMatchInputValue: true, listboxId: menuId, loadingText: loadingText, maxHeight: menuMaxHeight, mode: mode, onActionCustom: shouldShowCreateAction
|
|
520
|
+
: undefined, activeIndex: activeIndex, disabled: isInputDisabled, emptyText: emptyText, followText: searchText, inputPosition: inputPosition, isMatchInputValue: true, listboxId: menuId, loadingText: loadingText, loadingPosition: loadingPosition, maxHeight: menuMaxHeight, mode: mode, onActionCustom: shouldShowCreateAction
|
|
433
521
|
? handleActionCustom
|
|
434
|
-
: undefined, onItemHover: setActiveIndex, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: open, options:
|
|
522
|
+
: undefined, onItemHover: setActiveIndex, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: open, options: dropdownOptions, placement: "bottom", sameWidth: true, showDropdownActions: shouldShowCreateAction, showActionShowTopBar: shouldShowCreateAction, status: dropdownStatus, type: "default", value: dropdownValue, zIndex: dropdownZIndex, globalPortal: globalPortal, onReachBottom: onReachBottom, onLeaveBottom: onLeaveBottom, children: jsx(SelectTrigger, { ref: composedRef, active: open, className: className, clearable: true, disabled: isInputDisabled, fullWidth: fullWidth, inputRef: composedInputRef, mode: mode, onTagClose: wrappedOnChange, onClear: handleClear, overflowStrategy: overflowStrategy, placeholder: getPlaceholder(), prefix: prefix, readOnly: false, required: required, type: error ? 'error' : 'default', inputProps: {
|
|
435
523
|
...resolvedInputProps,
|
|
436
524
|
onClick: (e) => {
|
|
437
525
|
var _a;
|
package/Backdrop/Backdrop.js
CHANGED
|
@@ -15,26 +15,22 @@ const Backdrop = forwardRef(function Backdrop(props, ref) {
|
|
|
15
15
|
const { children, className, container, disableCloseOnBackdropClick = false, disablePortal, disableScrollLock = false, onBackdropClick, onClose, open = false, variant = 'dark', ...rest } = props;
|
|
16
16
|
// Lock body scroll when backdrop is open
|
|
17
17
|
useScrollLock({ enabled: open && !disableScrollLock });
|
|
18
|
-
|
|
19
|
-
// When using default Portal (to #mzn-portal-container), overlay uses relative positioning
|
|
20
|
-
const applyAbsolutePosition = Boolean(disablePortal || container);
|
|
21
|
-
return (jsx(Portal, { container: container, disablePortal: disablePortal, layer: "default", children: jsxs("div", { ...rest, ref: ref, "aria-hidden": !open, className: cx(backdropClasses.host, {
|
|
22
|
-
[backdropClasses.hostAbsolute]: applyAbsolutePosition,
|
|
18
|
+
return (jsx(Portal, { container: container, disablePortal: disablePortal, layer: "default", children: jsx("div", { ...rest, ref: ref, "aria-hidden": !open, className: cx(backdropClasses.host, backdropClasses.hostAbsolute, {
|
|
23
19
|
[backdropClasses.hostOpen]: open,
|
|
24
|
-
}, className), role: "presentation", children: [jsx(Fade, { in: open, duration: {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
}, className), role: "presentation", children: jsxs("div", { className: backdropClasses.main, children: [jsx(Fade, { in: open, duration: {
|
|
21
|
+
enter: MOTION_DURATION.fast,
|
|
22
|
+
exit: MOTION_DURATION.fast,
|
|
23
|
+
}, easing: {
|
|
24
|
+
enter: MOTION_EASING.standard,
|
|
25
|
+
exit: MOTION_EASING.standard,
|
|
26
|
+
}, children: jsx("div", { "aria-hidden": "true", className: cx(backdropClasses.backdrop, backdropClasses.backdropVariant(variant)), onClick: (event) => {
|
|
27
|
+
if (!disableCloseOnBackdropClick && onClose) {
|
|
28
|
+
onClose();
|
|
29
|
+
}
|
|
30
|
+
if (onBackdropClick) {
|
|
31
|
+
onBackdropClick(event);
|
|
32
|
+
}
|
|
33
|
+
} }) }), jsx("div", { className: backdropClasses.content, children: children })] }) }) }));
|
|
38
34
|
});
|
|
39
35
|
|
|
40
36
|
export { Backdrop as default };
|
package/Calendar/CalendarDays.js
CHANGED
|
@@ -74,7 +74,7 @@ function CalendarDays(props) {
|
|
|
74
74
|
? (() => {
|
|
75
75
|
var _a, _b;
|
|
76
76
|
const annotation = renderAnnotations(date);
|
|
77
|
-
return (jsx(Typography, { variant: "annotation", color: (_a = annotation === null || annotation === void 0 ? void 0 : annotation.color) !== null && _a !== void 0 ? _a : 'text-neutral', children: (_b = annotation === null || annotation === void 0 ? void 0 : annotation.value) !== null && _b !== void 0 ? _b : '' }));
|
|
77
|
+
return (jsx(Typography, { variant: "annotation", color: (_a = annotation === null || annotation === void 0 ? void 0 : annotation.color) !== null && _a !== void 0 ? _a : 'text-neutral', children: (_b = annotation === null || annotation === void 0 ? void 0 : annotation.value) !== null && _b !== void 0 ? _b : '--' }));
|
|
78
78
|
})()
|
|
79
79
|
: null] }) }, `${getMonth(date)}/${getDate(date)}`));
|
|
80
80
|
}) }, `CALENDAR_DAYS/WEEK_OF/${index}`)))] }) }));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ComponentOverridableForwardRefComponentPropsFactory } from '../utils/jsx-types';
|
|
2
|
+
import { BaseCardComponent, BaseCardProps } from './typings';
|
|
3
|
+
export type BaseCardComponentProps<C extends BaseCardComponent = 'div'> = ComponentOverridableForwardRefComponentPropsFactory<BaseCardComponent, C, BaseCardProps>;
|
|
4
|
+
/**
|
|
5
|
+
* BaseCard is a versatile card component that can be used as a div, link, or custom component.
|
|
6
|
+
* It supports four types: default, action, overflow, and toggle, each with different header actions.
|
|
7
|
+
*/
|
|
8
|
+
declare const BaseCard: import("react").ForwardRefExoticComponent<Omit<Omit<import("../utils/jsx-types").ComponentPropsWithoutKeyAndRef<"div">, "children" | "title" | "defaultChecked" | "className" | "disabled" | "type" | "checked" | "readOnly" | "options" | "description" | "actionName" | "actionVariant" | "onActionClick" | "onOptionSelect" | "onToggleChange" | "toggleSize" | "toggleLabel" | "toggleSupportingText"> & BaseCardProps, "component"> & {
|
|
9
|
+
component?: BaseCardComponent | undefined;
|
|
10
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
11
|
+
export default BaseCard;
|
package/Card/BaseCard.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { cardClasses } from '@mezzanine-ui/core/card';
|
|
5
|
+
import { DotHorizontalIcon } from '@mezzanine-ui/icons';
|
|
6
|
+
import Button from '../Button/Button.js';
|
|
7
|
+
import Dropdown from '../Dropdown/Dropdown.js';
|
|
8
|
+
import Toggle from '../Toggle/Toggle.js';
|
|
9
|
+
import cx from 'clsx';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* BaseCard is a versatile card component that can be used as a div, link, or custom component.
|
|
13
|
+
* It supports four types: default, action, overflow, and toggle, each with different header actions.
|
|
14
|
+
*/
|
|
15
|
+
const BaseCard = forwardRef(function BaseCard(props, ref) {
|
|
16
|
+
const { children, className, component: Component = 'div', disabled = false, readOnly = false, title, description, type = 'default', ...rest } = props;
|
|
17
|
+
const hasHeaderContent = Boolean(title || description);
|
|
18
|
+
const renderHeaderAction = () => {
|
|
19
|
+
if (type === 'default') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (type === 'action') {
|
|
23
|
+
const { actionName, actionVariant = 'base-text-link', onActionClick, } = props;
|
|
24
|
+
return (jsx("div", { className: cardClasses.baseHeaderAction, children: jsx(Button, { disabled: disabled, onClick: (event) => {
|
|
25
|
+
event.stopPropagation();
|
|
26
|
+
onActionClick === null || onActionClick === void 0 ? void 0 : onActionClick(event);
|
|
27
|
+
}, size: "sub", variant: actionVariant, type: "button", children: actionName }) }));
|
|
28
|
+
}
|
|
29
|
+
if (type === 'overflow') {
|
|
30
|
+
const { options, onOptionSelect } = props;
|
|
31
|
+
return (jsx("div", { className: cardClasses.baseHeaderAction, children: jsx(Dropdown, { disabled: disabled, mode: "single", onSelect: onOptionSelect, options: options, globalPortal: false, children: jsx(Button, { disabled: disabled, icon: DotHorizontalIcon, iconType: "icon-only", size: "sub", variant: "base-text-link", type: "button" }) }) }));
|
|
32
|
+
}
|
|
33
|
+
if (type === 'toggle') {
|
|
34
|
+
const { checked, defaultChecked, onToggleChange, toggleSize, toggleLabel, toggleSupportingText, } = props;
|
|
35
|
+
return (jsx("div", { className: cardClasses.baseHeaderAction, children: jsx(Toggle, { checked: checked, defaultChecked: defaultChecked, disabled: disabled, label: toggleLabel, onChange: onToggleChange, size: toggleSize, supportingText: toggleSupportingText }) }));
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
};
|
|
39
|
+
// Filter out type-specific props before spreading to component
|
|
40
|
+
const { actionName: _actionName, actionVariant: _actionVariant, onActionClick: _onActionClick, options: _options, onOptionSelect: _onOptionSelect, checked: _checked, defaultChecked: _defaultChecked, onToggleChange: _onToggleChange, toggleSize: _toggleSize, toggleLabel: _toggleLabel, toggleSupportingText: _toggleSupportingText, type: _type, ...componentProps } = rest;
|
|
41
|
+
return (jsxs(Component, { ...componentProps, ref: ref, "aria-disabled": disabled || undefined, "aria-readonly": readOnly || undefined, className: cx(cardClasses.base, {
|
|
42
|
+
[cardClasses.baseDisabled]: disabled,
|
|
43
|
+
[cardClasses.baseReadOnly]: readOnly,
|
|
44
|
+
}, className), children: [hasHeaderContent && (jsxs("div", { className: cardClasses.baseHeader, children: [jsxs("div", { className: cardClasses.baseHeaderContentWrapper, children: [title && (jsx("span", { className: cardClasses.baseHeaderTitle, children: title })), description && (jsx("span", { className: cardClasses.baseHeaderDescription, children: description }))] }), renderHeaderAction()] })), jsx("div", { className: cardClasses.baseContent, children: children })] }));
|
|
45
|
+
});
|
|
46
|
+
BaseCard.displayName = 'BaseCard';
|
|
47
|
+
|
|
48
|
+
export { BaseCard as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
2
|
+
export interface BaseCardSkeletonProps extends Omit<NativeElementPropsWithoutKeyAndRef<'div'>, 'children'> {
|
|
3
|
+
/**
|
|
4
|
+
* Whether to show content skeleton
|
|
5
|
+
* @default true
|
|
6
|
+
*/
|
|
7
|
+
showContent?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Skeleton placeholder for BaseCard component.
|
|
11
|
+
* Renders a skeleton that mimics the BaseCard layout.
|
|
12
|
+
*/
|
|
13
|
+
declare const BaseCardSkeleton: import("react").ForwardRefExoticComponent<BaseCardSkeletonProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
14
|
+
export default BaseCardSkeleton;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { cardClasses } from '@mezzanine-ui/core/card';
|
|
5
|
+
import Skeleton from '../Skeleton/Skeleton.js';
|
|
6
|
+
import cx from 'clsx';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Skeleton placeholder for BaseCard component.
|
|
10
|
+
* Renders a skeleton that mimics the BaseCard layout.
|
|
11
|
+
*/
|
|
12
|
+
const BaseCardSkeleton = forwardRef(function BaseCardSkeleton(props, ref) {
|
|
13
|
+
const { className, showContent = true, ...rest } = props;
|
|
14
|
+
return (jsxs("div", { ...rest, ref: ref, className: cx(cardClasses.base, cardClasses.baseReadOnly, className), children: [jsx("div", { className: cardClasses.baseHeader, children: jsxs("div", { className: cardClasses.baseHeaderContentWrapper, children: [jsx(Skeleton, { height: 20, width: "60%" }), jsx(Skeleton, { height: 16, width: "40%" })] }) }), showContent && (jsxs("div", { className: cardClasses.baseContent, children: [jsx(Skeleton, { height: 16, width: "100%" }), jsx(Skeleton, { height: 16, style: { marginTop: 8 }, width: "80%" })] }))] }));
|
|
15
|
+
});
|
|
16
|
+
BaseCardSkeleton.displayName = 'BaseCardSkeleton';
|
|
17
|
+
|
|
18
|
+
export { BaseCardSkeleton as default };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Card type for loading skeleton
|
|
4
|
+
*/
|
|
5
|
+
export type CardGroupLoadingType = 'base' | 'four-thumbnail' | 'quick-action' | 'single-thumbnail';
|
|
6
|
+
export interface CardGroupProps {
|
|
7
|
+
/**
|
|
8
|
+
* Custom class name
|
|
9
|
+
*/
|
|
10
|
+
className?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Card components to render in the group.
|
|
13
|
+
* Only accepts BaseCard, QuickActionCard, SingleThumbnailCard, and FourThumbnailCard as children.
|
|
14
|
+
*/
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Whether to show loading skeletons
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
loading?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Number of skeleton items to render when loading
|
|
23
|
+
* @default 3
|
|
24
|
+
*/
|
|
25
|
+
loadingCount?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Type of card skeleton to render when loading.
|
|
28
|
+
* Required when loading is true.
|
|
29
|
+
*/
|
|
30
|
+
loadingType?: CardGroupLoadingType;
|
|
31
|
+
/**
|
|
32
|
+
* Width of each thumbnail skeleton when loadingType is 'single-thumbnail' or 'four-thumbnail'.
|
|
33
|
+
* @default 360
|
|
34
|
+
*/
|
|
35
|
+
loadingThumbnailWidth?: number | string;
|
|
36
|
+
/**
|
|
37
|
+
* Aspect ratio of thumbnail skeletons when loadingType is 'single-thumbnail' or 'four-thumbnail'.
|
|
38
|
+
* For single-thumbnail, defaults to '16/9'. For four-thumbnail, defaults to '4/3'.
|
|
39
|
+
*/
|
|
40
|
+
loadingThumbnailAspectRatio?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* CardGroup is a container for card components.
|
|
44
|
+
* It uses CSS Grid to layout cards in a horizontal row with consistent spacing.
|
|
45
|
+
*/
|
|
46
|
+
declare const CardGroup: import("react").ForwardRefExoticComponent<CardGroupProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
47
|
+
export default CardGroup;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { cardClasses } from '@mezzanine-ui/core/card';
|
|
4
|
+
import { forwardRef, Children, isValidElement } from 'react';
|
|
5
|
+
import BaseCard from './BaseCard.js';
|
|
6
|
+
import BaseCardSkeleton from './BaseCardSkeleton.js';
|
|
7
|
+
import FourThumbnailCard from './FourThumbnailCard.js';
|
|
8
|
+
import FourThumbnailCardSkeleton from './FourThumbnailCardSkeleton.js';
|
|
9
|
+
import QuickActionCard from './QuickActionCard.js';
|
|
10
|
+
import QuickActionCardSkeleton from './QuickActionCardSkeleton.js';
|
|
11
|
+
import SingleThumbnailCard from './SingleThumbnailCard.js';
|
|
12
|
+
import SingleThumbnailCardSkeleton from './SingleThumbnailCardSkeleton.js';
|
|
13
|
+
import cx from 'clsx';
|
|
14
|
+
|
|
15
|
+
// List of allowed child component types
|
|
16
|
+
const ALLOWED_CARD_TYPES = [
|
|
17
|
+
BaseCard,
|
|
18
|
+
FourThumbnailCard,
|
|
19
|
+
QuickActionCard,
|
|
20
|
+
SingleThumbnailCard,
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Get display name of a component for error messages
|
|
24
|
+
*/
|
|
25
|
+
function getComponentDisplayName(child) {
|
|
26
|
+
const { type } = child;
|
|
27
|
+
if (typeof type === 'string') {
|
|
28
|
+
return type;
|
|
29
|
+
}
|
|
30
|
+
return (type.displayName ||
|
|
31
|
+
type.name ||
|
|
32
|
+
'Unknown');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Detect the first valid card type from children
|
|
36
|
+
*/
|
|
37
|
+
function getFirstCardType(children) {
|
|
38
|
+
let firstType = null;
|
|
39
|
+
Children.forEach(children, (child) => {
|
|
40
|
+
if (firstType !== null)
|
|
41
|
+
return;
|
|
42
|
+
if (!isValidElement(child))
|
|
43
|
+
return;
|
|
44
|
+
if (child.type === QuickActionCard) {
|
|
45
|
+
firstType = QuickActionCard;
|
|
46
|
+
}
|
|
47
|
+
else if (child.type === BaseCard) {
|
|
48
|
+
firstType = BaseCard;
|
|
49
|
+
}
|
|
50
|
+
else if (child.type === SingleThumbnailCard) {
|
|
51
|
+
firstType = SingleThumbnailCard;
|
|
52
|
+
}
|
|
53
|
+
else if (child.type === FourThumbnailCard) {
|
|
54
|
+
firstType = FourThumbnailCard;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return firstType;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the skeleton component based on loading type
|
|
61
|
+
*/
|
|
62
|
+
function getSkeletonComponent(loadingType) {
|
|
63
|
+
switch (loadingType) {
|
|
64
|
+
case 'base':
|
|
65
|
+
return BaseCardSkeleton;
|
|
66
|
+
case 'four-thumbnail':
|
|
67
|
+
return FourThumbnailCardSkeleton;
|
|
68
|
+
case 'quick-action':
|
|
69
|
+
return QuickActionCardSkeleton;
|
|
70
|
+
case 'single-thumbnail':
|
|
71
|
+
return SingleThumbnailCardSkeleton;
|
|
72
|
+
default:
|
|
73
|
+
return BaseCardSkeleton;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the group class modifier based on loading type
|
|
78
|
+
*/
|
|
79
|
+
function getGroupClassFromLoadingType(loadingType) {
|
|
80
|
+
switch (loadingType) {
|
|
81
|
+
case 'four-thumbnail':
|
|
82
|
+
return cardClasses.groupFourThumbnail;
|
|
83
|
+
case 'quick-action':
|
|
84
|
+
return cardClasses.groupQuickAction;
|
|
85
|
+
case 'single-thumbnail':
|
|
86
|
+
return cardClasses.groupSingleThumbnail;
|
|
87
|
+
default:
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* CardGroup is a container for card components.
|
|
93
|
+
* It uses CSS Grid to layout cards in a horizontal row with consistent spacing.
|
|
94
|
+
*/
|
|
95
|
+
const CardGroup = forwardRef(function CardGroup(props, ref) {
|
|
96
|
+
const { className, children, loading = false, loadingCount = 3, loadingThumbnailAspectRatio, loadingThumbnailWidth, loadingType, } = props;
|
|
97
|
+
// Detect first card type to determine min-width class
|
|
98
|
+
const firstCardType = getFirstCardType(children);
|
|
99
|
+
// Validate children at runtime
|
|
100
|
+
const validChildren = Children.map(children, (child) => {
|
|
101
|
+
if (!isValidElement(child)) {
|
|
102
|
+
return child;
|
|
103
|
+
}
|
|
104
|
+
const isAllowedType = ALLOWED_CARD_TYPES.some((allowedType) => child.type === allowedType);
|
|
105
|
+
if (!isAllowedType) {
|
|
106
|
+
const displayName = getComponentDisplayName(child);
|
|
107
|
+
console.warn(`[CardGroup] Invalid child type: <${displayName}>. ` +
|
|
108
|
+
'CardGroup only accepts Card components (BaseCard, FourThumbnailCard, QuickActionCard, SingleThumbnailCard) as children.');
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return child;
|
|
112
|
+
});
|
|
113
|
+
// Render loading skeletons
|
|
114
|
+
const renderSkeletons = () => {
|
|
115
|
+
if (!loading || !loadingType) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const SkeletonComponent = getSkeletonComponent(loadingType);
|
|
119
|
+
// Build props for thumbnail skeletons
|
|
120
|
+
const thumbnailSkeletonProps = loadingType === 'single-thumbnail' || loadingType === 'four-thumbnail'
|
|
121
|
+
? {
|
|
122
|
+
...(loadingThumbnailAspectRatio && {
|
|
123
|
+
thumbnailAspectRatio: loadingThumbnailAspectRatio,
|
|
124
|
+
}),
|
|
125
|
+
...(loadingThumbnailWidth && {
|
|
126
|
+
thumbnailWidth: loadingThumbnailWidth,
|
|
127
|
+
}),
|
|
128
|
+
}
|
|
129
|
+
: undefined;
|
|
130
|
+
return Array.from({ length: loadingCount }, (_, index) => (jsx(SkeletonComponent, { ...thumbnailSkeletonProps }, `skeleton-${index}`)));
|
|
131
|
+
};
|
|
132
|
+
// Determine group class modifier
|
|
133
|
+
const groupClassModifier = loadingType && loading
|
|
134
|
+
? getGroupClassFromLoadingType(loadingType)
|
|
135
|
+
: undefined;
|
|
136
|
+
return (jsxs("div", { ref: ref, className: cx(cardClasses.group, {
|
|
137
|
+
[cardClasses.groupFourThumbnail]: firstCardType === FourThumbnailCard ||
|
|
138
|
+
groupClassModifier === cardClasses.groupFourThumbnail,
|
|
139
|
+
[cardClasses.groupQuickAction]: firstCardType === QuickActionCard ||
|
|
140
|
+
groupClassModifier === cardClasses.groupQuickAction,
|
|
141
|
+
[cardClasses.groupSingleThumbnail]: firstCardType === SingleThumbnailCard ||
|
|
142
|
+
groupClassModifier === cardClasses.groupSingleThumbnail,
|
|
143
|
+
}, className), children: [validChildren, renderSkeletons()] }));
|
|
144
|
+
});
|
|
145
|
+
CardGroup.displayName = 'CardGroup';
|
|
146
|
+
|
|
147
|
+
export { CardGroup as default };
|