@mezzanine-ui/react 1.0.0-rc.6 → 1.0.0-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/AutoComplete/AutoComplete.d.ts +25 -9
  2. package/AutoComplete/AutoComplete.js +84 -17
  3. package/AutoComplete/AutoCompleteInside.d.ts +54 -0
  4. package/AutoComplete/AutoCompleteInside.js +17 -0
  5. package/AutoComplete/useAutoCompleteKeyboard.d.ts +2 -1
  6. package/AutoComplete/useAutoCompleteKeyboard.js +4 -1
  7. package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
  8. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
  9. package/Breadcrumb/typings.d.ts +1 -1
  10. package/COMPONENTS.md +2 -2
  11. package/Checkbox/Checkbox.js +24 -3
  12. package/Cropper/Cropper.d.ts +1 -1
  13. package/Description/Description.d.ts +1 -1
  14. package/Description/Description.js +1 -1
  15. package/Description/DescriptionTitle.d.ts +6 -1
  16. package/Description/DescriptionTitle.js +2 -2
  17. package/Drawer/Drawer.d.ts +39 -34
  18. package/Drawer/Drawer.js +33 -35
  19. package/Dropdown/Dropdown.d.ts +16 -1
  20. package/Dropdown/Dropdown.js +156 -9
  21. package/Dropdown/DropdownItem.d.ts +26 -2
  22. package/Dropdown/DropdownItem.js +91 -43
  23. package/Dropdown/DropdownItemCard.d.ts +3 -2
  24. package/Dropdown/DropdownItemCard.js +8 -5
  25. package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
  26. package/Dropdown/dropdownKeydownHandler.js +14 -7
  27. package/FilterArea/Filter.d.ts +25 -2
  28. package/FilterArea/Filter.js +23 -0
  29. package/FilterArea/FilterArea.d.ts +43 -4
  30. package/FilterArea/FilterArea.js +35 -2
  31. package/FilterArea/FilterLine.d.ts +19 -0
  32. package/FilterArea/FilterLine.js +19 -0
  33. package/Input/SpinnerButton/SpinnerButton.js +1 -1
  34. package/Modal/Modal.d.ts +22 -86
  35. package/Modal/Modal.js +4 -2
  36. package/Modal/ModalBodyForVerification.js +3 -1
  37. package/NotificationCenter/NotificationCenter.d.ts +21 -9
  38. package/NotificationCenter/NotificationCenter.js +22 -10
  39. package/NotificationCenter/NotificationCenterDrawer.d.ts +52 -1
  40. package/NotificationCenter/NotificationCenterDrawer.js +2 -2
  41. package/OverflowTooltip/OverflowTooltip.js +46 -5
  42. package/PageFooter/PageFooter.js +6 -14
  43. package/Pagination/PaginationPageSize.js +1 -1
  44. package/README.md +34 -4
  45. package/Radio/Radio.js +16 -2
  46. package/Table/Table.js +1 -1
  47. package/TimePicker/TimePicker.js +1 -1
  48. package/TimeRangePicker/TimeRangePicker.js +1 -1
  49. package/Toggle/Toggle.d.ts +1 -1
  50. package/Toggle/Toggle.js +1 -1
  51. package/Upload/Upload.d.ts +13 -7
  52. package/Upload/Upload.js +55 -20
  53. package/Upload/UploadItem.js +4 -1
  54. package/Upload/UploadPictureCard.d.ts +5 -0
  55. package/Upload/UploadPictureCard.js +8 -5
  56. package/Upload/Uploader.d.ts +32 -31
  57. package/Upload/Uploader.js +10 -9
  58. package/index.d.ts +3 -3
  59. package/index.js +1 -1
  60. package/llms.txt +128 -9
  61. package/package.json +5 -4
@@ -21,6 +21,8 @@ export interface AutoCompleteBaseProps extends Omit<SelectTriggerProps, 'active'
21
21
  asyncData?: boolean;
22
22
  /**
23
23
  * Whether to clear search text when leaving the textfield/dropdown scope.
24
+ * When `false`, typed text persists after blur. In `single` mode, a clearable
25
+ * icon will appear if the user has typed text without selecting an option.
24
26
  * @default true
25
27
  */
26
28
  clearSearchText?: boolean;
@@ -46,7 +48,10 @@ export interface AutoCompleteBaseProps extends Omit<SelectTriggerProps, 'active'
46
48
  */
47
49
  id?: string;
48
50
  /**
49
- * The position of the input.
51
+ * The position of the search input relative to the dropdown.
52
+ * - `'outside'`: input is always visible above the dropdown (default trigger layout).
53
+ * - `'inside'`: input is rendered inside the dropdown panel; the trigger shows only
54
+ * the selected value(s) and opens the dropdown on click.
50
55
  * @default 'outside'
51
56
  */
52
57
  inputPosition?: DropdownInputPosition;
@@ -86,10 +91,10 @@ export interface AutoCompleteBaseProps extends Omit<SelectTriggerProps, 'active'
86
91
  */
87
92
  name?: string;
88
93
  /**
89
- * insert callback whenever insert icon is clicked
90
- * receives the text to insert and current options, returns the updated options array
91
- * should remove previously created but unselected options
92
- * The returned options will be used to update the component's options prop
94
+ * Callback fired when the user confirms a new item creation.
95
+ * Receives the typed text and the current options array; must return the updated options array.
96
+ * Use this to append the new item to your options state.
97
+ * Required when `addable` is true; omitting it will disable the creation feature.
93
98
  */
94
99
  onInsert?(text: string, currentOptions: SelectValue[]): SelectValue[];
95
100
  /**
@@ -178,9 +183,10 @@ export interface AutoCompleteBaseProps extends Omit<SelectTriggerProps, 'active'
178
183
  */
179
184
  onLeaveBottom?: () => void;
180
185
  /**
181
- * Called on blur when addable mode has unselected created items.
182
- * Receives the cleaned options (unselected created items already removed).
183
- * Use this to update your options state.
186
+ * Called when the dropdown closes (on blur or Escape) and `addable` mode has
187
+ * items that were created but never selected.
188
+ * Receives the cleaned options array with unselected created items already removed.
189
+ * Use this to sync your options state and strip the dangling created entries.
184
190
  * Only called when `addable` is true and there are unselected created items.
185
191
  */
186
192
  onRemoveCreated?(cleanedOptions: SelectValue[]): void;
@@ -245,7 +251,8 @@ export type AutoCompleteProps = AutoCompleteMultipleProps | AutoCompleteSinglePr
245
251
  /**
246
252
  * 自動完成輸入元件,在使用者輸入時即時顯示符合的下拉選項。
247
253
  *
248
- * 支援 `single`(單選)與 `multiple`(多選標籤)兩種模式;設定 `addable` 與 `onInsert`
254
+ * 支援 `single`(單選)與 `multiple`(多選標籤)兩種模式;`inputPosition` 控制搜尋輸入框
255
+ * 位於下拉選單外(`'outside'`,預設)或內(`'inside'`)。設定 `addable` 與 `onInsert`
249
256
  * 可讓使用者動態建立不在選項清單中的項目。`asyncData` 搭配 `onSearch` 可實現非同步搜尋,
250
257
  * 輸入時觸發 debounce 查詢並顯示 loading 狀態。若僅需從固定選項中搜尋,請改用 `Select` 元件。
251
258
  *
@@ -270,6 +277,15 @@ export type AutoCompleteProps = AutoCompleteMultipleProps | AutoCompleteSinglePr
270
277
  * onChange={setSelectedList}
271
278
  * />
272
279
  *
280
+ * // 搜尋框置於下拉選單內(inside 模式)
281
+ * <AutoComplete
282
+ * mode="multiple"
283
+ * inputPosition="inside"
284
+ * options={options}
285
+ * value={selectedList}
286
+ * onChange={setSelectedList}
287
+ * />
288
+ *
273
289
  * // 非同步搜尋
274
290
  * <AutoComplete
275
291
  * mode="single"
@@ -11,6 +11,7 @@ import { useAutoCompleteCreation, getFullParsedList } from './useAutoCompleteCre
11
11
  import { useAutoCompleteKeyboard } from './useAutoCompleteKeyboard.js';
12
12
  import { useAutoCompleteSearch } from './useAutoCompleteSearch.js';
13
13
  import { useCreationTracker } from './useCreationTracker.js';
14
+ import AutoCompleteInsideTrigger from './AutoCompleteInside.js';
14
15
  import { FormControlContext } from '../Form/FormControlContext.js';
15
16
  import Dropdown from '../Dropdown/Dropdown.js';
16
17
  import cx from 'clsx';
@@ -32,7 +33,8 @@ function isSingleValue(value) {
32
33
  /**
33
34
  * 自動完成輸入元件,在使用者輸入時即時顯示符合的下拉選項。
34
35
  *
35
- * 支援 `single`(單選)與 `multiple`(多選標籤)兩種模式;設定 `addable` 與 `onInsert`
36
+ * 支援 `single`(單選)與 `multiple`(多選標籤)兩種模式;`inputPosition` 控制搜尋輸入框
37
+ * 位於下拉選單外(`'outside'`,預設)或內(`'inside'`)。設定 `addable` 與 `onInsert`
36
38
  * 可讓使用者動態建立不在選項清單中的項目。`asyncData` 搭配 `onSearch` 可實現非同步搜尋,
37
39
  * 輸入時觸發 debounce 查詢並顯示 loading 狀態。若僅需從固定選項中搜尋,請改用 `Select` 元件。
38
40
  *
@@ -57,6 +59,15 @@ function isSingleValue(value) {
57
59
  * onChange={setSelectedList}
58
60
  * />
59
61
  *
62
+ * // 搜尋框置於下拉選單內(inside 模式)
63
+ * <AutoComplete
64
+ * mode="multiple"
65
+ * inputPosition="inside"
66
+ * options={options}
67
+ * value={selectedList}
68
+ * onChange={setSelectedList}
69
+ * />
70
+ *
60
71
  * // 非同步搜尋
61
72
  * <AutoComplete
62
73
  * mode="single"
@@ -268,7 +279,10 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
268
279
  }, [clearPendingOptionsReset]);
269
280
  useEffect(() => {
270
281
  var _a;
271
- if (!isMultiple)
282
+ // Multiple mode bridge only applies to SelectTrigger (tags) rendering.
283
+ // In `inputPosition="inside"` we render a plain Input trigger, so the hidden
284
+ // SelectTrigger input bridge is not needed.
285
+ if (!isMultiple || inputPosition === 'inside')
272
286
  return;
273
287
  const hiddenTriggerInput = (_a = nodeRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${selectClasses.triggerInput}`);
274
288
  if (!hiddenTriggerInput)
@@ -279,7 +293,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
279
293
  return;
280
294
  hiddenTriggerInput.value = bridgeValue;
281
295
  hiddenTriggerInput.dispatchEvent(new Event('change', { bubbles: true }));
282
- }, [isMultiple, searchText, value]);
296
+ }, [inputPosition, isMultiple, searchText, value]);
283
297
  // In single mode, show searchText when focused, otherwise show selected value
284
298
  // In multiple mode, always return empty string to avoid displaying "0"
285
299
  const renderValue = useMemo(() => {
@@ -299,6 +313,27 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
299
313
  shouldClearSearchTextOnBlur,
300
314
  value,
301
315
  ]);
316
+ const insideInputValue = useMemo(() => {
317
+ // Inside trigger is a plain Input, so we must decide what to display.
318
+ // - multiple: always show current search text
319
+ // - single: show search text when focused (or when clear-on-blur is disabled)
320
+ if (isMultiple)
321
+ return searchText;
322
+ if (isSingle &&
323
+ (focused || (!shouldClearSearchTextOnBlur && !value && searchText))) {
324
+ return searchText;
325
+ }
326
+ if (isSingleValue(value))
327
+ return value.name;
328
+ return '';
329
+ }, [
330
+ focused,
331
+ isMultiple,
332
+ isSingle,
333
+ searchText,
334
+ shouldClearSearchTextOnBlur,
335
+ value,
336
+ ]);
302
337
  function getPlaceholder() {
303
338
  if (isSingle && focused && isSingleValue(value)) {
304
339
  return value.name;
@@ -330,8 +365,8 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
330
365
  e.stopPropagation();
331
366
  }
332
367
  // Only open if not already open to avoid flickering
333
- // When inputPosition is inside, Dropdown will handle opening via inlineTriggerElement
334
- if (inputPosition !== 'inside' && !open) {
368
+ // Ensure inside/uncontrolled can open from the native input focus.
369
+ if (!open) {
335
370
  toggleOpen(true);
336
371
  }
337
372
  onFocus(true);
@@ -412,11 +447,13 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
412
447
  id: option.id,
413
448
  name: option.name,
414
449
  };
415
- // Set checkSite based on mode
416
- // Multiple mode: show checkbox at prepend
417
- // Single mode: show checked icon at append when selected
450
+ // Set checkSite based on mode and input position.
451
+ // - inside + multiple: keep multiple behavior, but render single-like checked icon
452
+ // at suffix to match product visual expectation.
453
+ // - outside + multiple: render checkbox at prefix.
454
+ // - single: render checked icon at suffix.
418
455
  if (mode === 'multiple') {
419
- result.checkSite = 'prefix';
456
+ result.checkSite = inputPosition === 'inside' ? 'suffix' : 'prefix';
420
457
  }
421
458
  else {
422
459
  result.checkSite = 'suffix';
@@ -427,7 +464,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
427
464
  }
428
465
  return result;
429
466
  });
430
- }, [isCreated, mode, options]);
467
+ }, [inputPosition, isCreated, mode, options]);
431
468
  // Get selected value for dropdown
432
469
  const dropdownValue = useMemo(() => {
433
470
  if (mode === 'multiple') {
@@ -445,7 +482,8 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
445
482
  const shouldForceClearable = isMultiple
446
483
  ? (isMultipleValue(value) && value.length > 0) ||
447
484
  searchText.trim().length > 0
448
- : isSingleValue(value);
485
+ : isSingleValue(value) ||
486
+ (!shouldClearSearchTextOnBlur && searchText.trim().length > 0);
449
487
  // Handle dropdown option selection
450
488
  const handleDropdownSelect = useCallback((option) => {
451
489
  const selectedValue = options.find((opt) => opt.id === option.id);
@@ -478,11 +516,15 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
478
516
  ]);
479
517
  // Active index for dropdown keyboard navigation
480
518
  const [activeIndex, setActiveIndex] = useState(null);
519
+ // Keyboard-only active index: only set by arrow key navigation, not mouse hover.
520
+ // Drives the focus ring (--keyboard-active CSS class) separately from hover highlight.
521
+ const [keyboardActiveIndex, setKeyboardActiveIndex] = useState(null);
481
522
  const setListboxHasVisualFocus = useCallback((_focus) => { }, []);
482
- // Reset activeIndex when options change
523
+ // Reset activeIndex and keyboardActiveIndex when options change
483
524
  useEffect(() => {
484
525
  if (!dropdownOptions.length) {
485
526
  setActiveIndex(null);
527
+ setKeyboardActiveIndex(null);
486
528
  return;
487
529
  }
488
530
  setActiveIndex((prev) => {
@@ -490,6 +532,11 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
490
532
  return null;
491
533
  return Math.min(prev, dropdownOptions.length - 1);
492
534
  });
535
+ setKeyboardActiveIndex((prev) => {
536
+ if (prev === null)
537
+ return null;
538
+ return Math.min(prev, dropdownOptions.length - 1);
539
+ });
493
540
  }, [dropdownOptions.length]);
494
541
  // Scroll to active option when activeIndex changes
495
542
  useEffect(() => {
@@ -524,6 +571,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
524
571
  searchText,
525
572
  searchTextExistWithoutOption,
526
573
  setActiveIndex,
574
+ setKeyboardActiveIndex,
527
575
  setInsertText,
528
576
  setListboxHasVisualFocus,
529
577
  setSearchText,
@@ -539,19 +587,29 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
539
587
  e.stopPropagation();
540
588
  toggleOpen(false);
541
589
  setActiveIndex(null);
590
+ setKeyboardActiveIndex(null);
542
591
  setListboxHasVisualFocus(false);
543
592
  onFocus(false);
593
+ if (isMultiple && shouldClearSearchTextOnBlur) {
594
+ resetSearchInputsAndOptions();
595
+ cleanupUnselectedCreated();
596
+ }
544
597
  (_a = inputElementRef.current) === null || _a === void 0 ? void 0 : _a.blur();
545
598
  (_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onKeyDown) === null || _b === void 0 ? void 0 : _b.call(inputProps, e);
546
599
  return;
547
600
  }
548
601
  handleInputKeyDown(e);
549
602
  }, [
603
+ cleanupUnselectedCreated,
550
604
  handleInputKeyDown,
551
605
  inputProps,
606
+ isMultiple,
552
607
  onFocus,
608
+ resetSearchInputsAndOptions,
553
609
  setActiveIndex,
610
+ setKeyboardActiveIndex,
554
611
  setListboxHasVisualFocus,
612
+ shouldClearSearchTextOnBlur,
555
613
  toggleOpen,
556
614
  ]);
557
615
  // Handle visibility change from Dropdown to prevent flickering
@@ -568,6 +626,9 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
568
626
  }
569
627
  // In multiple mode, blur/close can be driven by Dropdown visibility updates.
570
628
  // Keep input text cleanup consistent even when native input blur is not triggered.
629
+ if (!newOpen) {
630
+ setKeyboardActiveIndex(null);
631
+ }
571
632
  if (!newOpen && isMultiple && shouldClearSearchTextOnBlur) {
572
633
  if (skipNextMultipleCloseResetRef.current) {
573
634
  const menuElement = document.getElementById(menuId);
@@ -589,6 +650,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
589
650
  menuId,
590
651
  open,
591
652
  resetSearchInputsAndOptions,
653
+ setKeyboardActiveIndex,
592
654
  shouldClearSearchTextOnBlur,
593
655
  toggleOpen,
594
656
  ]);
@@ -621,22 +683,27 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
621
683
  ? createActionText
622
684
  ? createActionText(createActionDisplayText)
623
685
  : createActionTextTemplate.replace('{text}', createActionDisplayText)
624
- : 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 ? handleActionCustom : undefined, onItemHover: setActiveIndex, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: open, options: asyncData && isLoading ? [] : 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, isForceClearable: shouldForceClearable, inputRef: composedInputRef, mode: mode, onTagClose: wrappedOnChange, onClear: handleClear, overflowStrategy: isMultiple ? (overflowStrategy !== null && overflowStrategy !== void 0 ? overflowStrategy : 'wrap') : overflowStrategy, placeholder: getPlaceholder(), prefix: prefix, readOnly: false, required: required, type: error ? 'error' : 'default', inputProps: {
686
+ : undefined, activeIndex: activeIndex, keyboardActiveIndex: keyboardActiveIndex, disabled: isInputDisabled, emptyText: emptyText, followText: searchText, inputPosition: inputPosition, isMatchInputValue: true, listboxId: menuId, loadingText: loadingText, loadingPosition: loadingPosition, maxHeight: menuMaxHeight, mode: mode, onActionCustom: shouldShowCreateAction ? handleActionCustom : undefined, onItemHover: setActiveIndex, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: open, options: asyncData && isLoading ? [] : dropdownOptions, placement: "bottom", sameWidth: true, showDropdownActions: shouldShowCreateAction, showActionShowTopBar: shouldShowCreateAction, status: dropdownStatus, toggleCheckedOnClick: inputPosition === 'inside' && mode === 'multiple' ? false : undefined, type: "default", value: dropdownValue, zIndex: dropdownZIndex, globalPortal: globalPortal, onReachBottom: onReachBottom, onLeaveBottom: onLeaveBottom, children: inputPosition === 'inside' ? (jsx(AutoCompleteInsideTrigger, { active: open, className: className, clearable: shouldForceClearable, disabled: isInputDisabled, error: error, fullWidth: fullWidth, inputRef: composedInputRef, onClear: handleClear, placeholder: getPlaceholder(), resolvedInputProps: {
687
+ ...resolvedInputProps,
688
+ onClick: (e) => {
689
+ var _a;
690
+ (_a = resolvedInputProps.onClick) === null || _a === void 0 ? void 0 : _a.call(resolvedInputProps, e);
691
+ },
692
+ }, size: size, value: insideInputValue })) : (jsx(SelectTrigger, { ref: composedRef, active: open, className: className, clearable: true, disabled: isInputDisabled, fullWidth: fullWidth, isForceClearable: shouldForceClearable, inputRef: composedInputRef, mode: mode, onTagClose: wrappedOnChange, onClear: handleClear, overflowStrategy: isMultiple ? (overflowStrategy !== null && overflowStrategy !== void 0 ? overflowStrategy : 'wrap') : overflowStrategy, placeholder: getPlaceholder(), prefix: prefix, readOnly: false, required: required, type: error ? 'error' : 'default', inputProps: {
625
693
  ...resolvedInputProps,
626
694
  onClick: (e) => {
627
695
  var _a;
628
696
  // When inputPosition is inside, let Dropdown handle the click event
629
697
  // Otherwise, stop propagation to prevent conflicts
630
- if (inputPosition !== 'inside') {
631
- e.stopPropagation();
632
- }
698
+ // This branch is only rendered when `inputPosition !== 'inside'`.
699
+ e.stopPropagation();
633
700
  (_a = resolvedInputProps.onClick) === null || _a === void 0 ? void 0 : _a.call(resolvedInputProps, e);
634
701
  },
635
702
  }, searchText: searchText, size: size, showTextInputAfterTags: true, suffixAction: onClickSuffixActionIcon, value: mode === 'multiple' &&
636
703
  isMultipleValue(value) &&
637
704
  value.length === 0
638
705
  ? undefined
639
- : (value !== null && value !== void 0 ? value : undefined), ...(mode === 'single' && renderValue ? { renderValue } : {}) }) }) }) }));
706
+ : (value !== null && value !== void 0 ? value : undefined), ...(mode === 'single' && renderValue ? { renderValue } : {}) })) }) }) }));
640
707
  });
641
708
 
642
709
  export { AutoComplete as default };
@@ -0,0 +1,54 @@
1
+ import { type MouseEventHandler, type Ref } from 'react';
2
+ import type { InputProps } from '../Input';
3
+ import type { SelectTriggerInputProps } from '../Select/typings';
4
+ export interface AutoCompleteInsideTriggerProps {
5
+ /**
6
+ * Disabled state for the input.
7
+ */
8
+ disabled: boolean;
9
+ /**
10
+ * Error state for the input.
11
+ */
12
+ error: boolean;
13
+ /**
14
+ * Whether the trigger should render as active (focused/open).
15
+ */
16
+ active: boolean;
17
+ /**
18
+ * Input display value (usually the current search text).
19
+ */
20
+ value: string;
21
+ /**
22
+ * Input placeholder text.
23
+ */
24
+ placeholder?: string;
25
+ /**
26
+ * Input variant sizing.
27
+ */
28
+ size?: InputProps['size'];
29
+ /**
30
+ * Whether the input should occupy full width.
31
+ */
32
+ fullWidth?: boolean;
33
+ /**
34
+ * Additional class name for the trigger.
35
+ */
36
+ className?: string;
37
+ /**
38
+ * Input ref (points to the underlying <input/> element).
39
+ */
40
+ inputRef?: Ref<HTMLInputElement>;
41
+ /**
42
+ * Props forwarded to the underlying input element.
43
+ */
44
+ resolvedInputProps: SelectTriggerInputProps;
45
+ /**
46
+ * Whether to show the clear icon.
47
+ */
48
+ clearable: boolean;
49
+ /**
50
+ * Input clear handler.
51
+ */
52
+ onClear?: MouseEventHandler;
53
+ }
54
+ export default function AutoCompleteInsideTrigger(props: AutoCompleteInsideTriggerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import Input from '../Input/Input.js';
4
+
5
+ function extractIdAndName(props) {
6
+ const { id, name, readOnly, onChange, ...rest } = props;
7
+ return { id, name, readOnly, onChange, rest };
8
+ }
9
+ function AutoCompleteInsideTrigger(props) {
10
+ const { active, className, clearable, disabled, error, fullWidth, inputRef, onClear, placeholder, resolvedInputProps, size, value, } = props;
11
+ const { id, name, readOnly, onChange, rest } = extractIdAndName(resolvedInputProps);
12
+ return (jsx(Input, { active: active, className: className, ...(disabled ? { disabled: true } : {}), error: error, fullWidth: fullWidth, id: id, name: name, placeholder: placeholder, readonly: readOnly || undefined, onChange: onChange, size: size, value: value, clearable: clearable, onClear: onClear,
13
+ // keep clear icon visibility behavior consistent with SelectTrigger
14
+ forceShowClearable: clearable, inputRef: inputRef, inputProps: rest }));
15
+ }
16
+
17
+ export { AutoCompleteInsideTrigger as default };
@@ -19,6 +19,7 @@ type UseAutoCompleteKeyboardParams = {
19
19
  searchText: string;
20
20
  searchTextExistWithoutOption: boolean;
21
21
  setActiveIndex: Dispatch<SetStateAction<number | null>>;
22
+ setKeyboardActiveIndex?: Dispatch<SetStateAction<number | null>>;
22
23
  setListboxHasVisualFocus: (focus: boolean) => void;
23
24
  setInsertText: (value: string) => void;
24
25
  setSearchText: (value: string) => void;
@@ -27,7 +28,7 @@ type UseAutoCompleteKeyboardParams = {
27
28
  value: SelectValue[] | SelectValue | null | undefined;
28
29
  wrappedOnChange: (chooseOption: SelectValue | null) => SelectValue[] | SelectValue | null;
29
30
  };
30
- export declare function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropdownOptions, handleActionCustom, handleBulkCreate, handleDropdownSelect, inputPropsOnKeyDown, inputRef, mode, onFocus, open, processBulkCreate, searchText, searchTextExistWithoutOption, setActiveIndex, setInsertText, setListboxHasVisualFocus, setSearchText, stepByStepBulkCreate, toggleOpen, value, wrappedOnChange, }: UseAutoCompleteKeyboardParams): {
31
+ export declare function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropdownOptions, handleActionCustom, handleBulkCreate, handleDropdownSelect, inputPropsOnKeyDown, inputRef, mode, onFocus, open, processBulkCreate, searchText, searchTextExistWithoutOption, setActiveIndex, setKeyboardActiveIndex, setInsertText, setListboxHasVisualFocus, setSearchText, stepByStepBulkCreate, toggleOpen, value, wrappedOnChange, }: UseAutoCompleteKeyboardParams): {
31
32
  handleInputKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
32
33
  };
33
34
  export {};
@@ -4,7 +4,7 @@ import { createDropdownKeydownHandler } from '../Dropdown/dropdownKeydownHandler
4
4
  function isMultipleValue(value) {
5
5
  return Array.isArray(value);
6
6
  }
7
- function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropdownOptions, handleActionCustom, handleBulkCreate, handleDropdownSelect, inputPropsOnKeyDown, inputRef, mode, onFocus, open, processBulkCreate, searchText, searchTextExistWithoutOption, setActiveIndex, setInsertText, setListboxHasVisualFocus, setSearchText, stepByStepBulkCreate = false, toggleOpen, value, wrappedOnChange, }) {
7
+ function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropdownOptions, handleActionCustom, handleBulkCreate, handleDropdownSelect, inputPropsOnKeyDown, inputRef, mode, onFocus, open, processBulkCreate, searchText, searchTextExistWithoutOption, setActiveIndex, setKeyboardActiveIndex, setInsertText, setListboxHasVisualFocus, setSearchText, stepByStepBulkCreate = false, toggleOpen, value, wrappedOnChange, }) {
8
8
  const handleKeyDown = useCallback((e) => createDropdownKeydownHandler({
9
9
  activeIndex,
10
10
  onEnterSelect: (option) => {
@@ -18,6 +18,7 @@ function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropd
18
18
  var _a;
19
19
  toggleOpen(false);
20
20
  setActiveIndex(null);
21
+ setKeyboardActiveIndex === null || setKeyboardActiveIndex === void 0 ? void 0 : setKeyboardActiveIndex(null);
21
22
  setListboxHasVisualFocus(false);
22
23
  if (inputRef && typeof inputRef !== 'function') {
23
24
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
@@ -26,6 +27,7 @@ function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropd
26
27
  open,
27
28
  options: dropdownOptions,
28
29
  setActiveIndex,
30
+ setKeyboardActiveIndex,
29
31
  setListboxHasVisualFocus,
30
32
  setOpen: (newOpen) => {
31
33
  if (newOpen && !open) {
@@ -44,6 +46,7 @@ function useAutoCompleteKeyboard({ activeIndex, addable, createSeparators, dropd
44
46
  onFocus,
45
47
  open,
46
48
  setActiveIndex,
49
+ setKeyboardActiveIndex,
47
50
  setListboxHasVisualFocus,
48
51
  toggleOpen,
49
52
  ]);
@@ -1,4 +1,4 @@
1
- declare const BreadcrumbDropdown: import("react").ForwardRefExoticComponent<Omit<import("../Dropdown").DropdownProps, "children"> & {
1
+ declare const BreadcrumbDropdown: import("react").ForwardRefExoticComponent<Omit<import("../Dropdown").DropdownProps, "children" | "onScroll"> & {
2
2
  className?: string;
3
3
  current?: boolean;
4
4
  href?: never;
@@ -1,4 +1,4 @@
1
- declare const BreadcrumbOverflowMenuDropdown: import("react").ForwardRefExoticComponent<Omit<import("../Dropdown").DropdownProps, "children"> & {
1
+ declare const BreadcrumbOverflowMenuDropdown: import("react").ForwardRefExoticComponent<Omit<import("../Dropdown").DropdownProps, "children" | "onScroll"> & {
2
2
  className?: string;
3
3
  current?: boolean;
4
4
  href?: never;
@@ -45,7 +45,7 @@ export type BreadcrumbLinkItemProps = Omit<NativeElementPropsWithoutKeyAndRef<'a
45
45
  */
46
46
  target?: '_blank' | '_parent' | '_self' | '_top' | string;
47
47
  };
48
- export type BreadcrumbDropdownProps = Omit<DropdownProps, 'children'> & {
48
+ export type BreadcrumbDropdownProps = Omit<DropdownProps, 'children' | 'onScroll'> & {
49
49
  className?: string;
50
50
  current?: boolean;
51
51
  href?: never;
package/COMPONENTS.md CHANGED
@@ -110,7 +110,7 @@
110
110
  | SelectTriggerTags | `SelectTriggerTags` | 多選 Select 的標籤顯示區域 | — |
111
111
  | SelectionCard | `SelectionCard` | 可選取的卡片元件,作為 Checkbox/Radio 的卡片式替代 | — |
112
112
  | Slider | `Slider` | 滑桿元件,支援單點與範圍兩種模式 | `useSlider` |
113
- | Switch | `Switch` | 開關切換元件(對應 Toggle) | `useSwitchControlValue` |
113
+ | Toggle | `Toggle` | 切換開關元件,用於表示開/關二元狀態 | `useSwitchControlValue` |
114
114
  | Textarea | `Textarea` | 多行文字輸入框,支援自動調整高度與字數限制 | `useInputControlValue` |
115
115
  | TextField | `TextField` | 文字欄位基底元件,提供通用的邊框/尺寸/狀態樣式 | — |
116
116
  | TimePicker | `TimePicker` | 時間選擇器,點擊觸發 TimePanel 面板選取時間 | `usePickerValue` |
@@ -220,7 +220,7 @@
220
220
  | `useSelectValueControl` | Select | 受控單選 / 多選值管理 |
221
221
  | `useCheckboxControlValue` | Checkbox、CheckboxGroup | 受控勾選狀態管理 |
222
222
  | `useRadioControlValue` | Radio、RadioGroup | 受控 radio 選取管理 |
223
- | `useSwitchControlValue` | Switch(Toggle| 受控開關狀態管理 |
223
+ | `useSwitchControlValue` | Toggle | 受控開關狀態管理 |
224
224
  | `useAutoCompleteValueControl` | AutoComplete | 受控自動完成值管理 |
225
225
  | `useCustomControlValue` | 任意元件 | 泛用受控值 hook |
226
226
  | `useControlValueState` | 任意元件 | 基礎受控 / 非受控狀態管理 |
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { forwardRef, useContext, useId, useEffect, useMemo, useRef } from 'react';
3
+ import { forwardRef, useContext, useId, useEffect, useMemo, useRef, useCallback } from 'react';
4
4
  import { checkboxClasses } from '@mezzanine-ui/core/checkbox';
5
5
  import { CheckedIcon } from '@mezzanine-ui/icons';
6
6
  import { useCheckboxControlValue } from '../Form/useCheckboxControlValue.js';
@@ -110,6 +110,24 @@ const Checkbox = forwardRef(function Checkbox(props, ref) {
110
110
  };
111
111
  }, [withEditInput, editableInput, resolvedName, finalInputId]);
112
112
  const shouldShowEditableInput = withEditInput && defaultEditableInput;
113
+ const prevIsCheckedRef = useRef(isChecked);
114
+ useEffect(() => {
115
+ if (isChecked && !prevIsCheckedRef.current && shouldShowEditableInput && editableInputRef.current) {
116
+ editableInputRef.current.focus();
117
+ }
118
+ prevIsCheckedRef.current = isChecked;
119
+ }, [isChecked, shouldShowEditableInput]);
120
+ const handleEditableInputMouseDown = useCallback((event) => {
121
+ var _a, _b;
122
+ (_b = (_a = defaultEditableInput === null || defaultEditableInput === void 0 ? void 0 : defaultEditableInput.inputProps) === null || _a === void 0 ? void 0 : _a.onMouseDown) === null || _b === void 0 ? void 0 : _b.call(_a, event);
123
+ if (event.defaultPrevented) {
124
+ return;
125
+ }
126
+ if (!isChecked && !disabled && inputElementRef.current) {
127
+ event.preventDefault(); // 阻止原生 focus-on-mousedown,讓 useEffect 統一控制 focus 時機
128
+ inputElementRef.current.click();
129
+ }
130
+ }, [defaultEditableInput, disabled, isChecked]);
113
131
  return (jsxs("div", { className: cx(checkboxClasses.host, className, {
114
132
  [checkboxClasses.checked]: isChecked,
115
133
  [checkboxClasses.indeterminate]: isIndeterminate,
@@ -119,10 +137,13 @@ const Checkbox = forwardRef(function Checkbox(props, ref) {
119
137
  }, checkboxClasses.size(size)), children: [jsxs("label", { ref: ref, ...rest, className: checkboxClasses.labelContainer, children: [jsx("div", { className: checkboxClasses.inputContainer, children: jsxs("div", { className: checkboxClasses.inputContent, children: [jsx("input", { ...restInputProps, "aria-checked": isIndeterminate ? 'mixed' : checked, "aria-disabled": disabled, checked: isChecked, className: checkboxClasses.input, disabled: disabled, id: finalInputId, name: resolvedName, onChange: onChange, ref: composedInputRef, type: "checkbox", value: value }), mode === 'chip' && isChecked && (jsx(Icon, { "aria-hidden": "true", className: cx(checkboxClasses.icon, checkboxClasses.chipIcon), color: "brand", icon: CheckedIcon, size: 16 })), mode !== 'chip' && isChecked && (jsx(Icon, { "aria-hidden": "true", className: checkboxClasses.icon, color: "fixed-light", icon: CheckedIcon, size: 9 })), mode !== 'chip' && isIndeterminate && (jsx("span", { "aria-hidden": "true", className: checkboxClasses.indeterminateLine }))] }) }), (label || description) && (jsxs("span", { className: checkboxClasses.textContainer, children: [label && (jsx(Typography, { className: checkboxClasses.label, color: labelColor, variant: "label-primary", children: label })), description && mode !== 'chip' && !shouldShowEditableInput && (jsx(Typography, { className: checkboxClasses.description, color: "text-neutral", variant: "caption", children: description }))] }))] }), shouldShowEditableInput &&
120
138
  defaultEditableInput &&
121
139
  mode !== 'chip' &&
122
- !indeterminate && (jsx("div", { className: checkboxClasses.editableInputContainer, children: jsx(Input, { ...defaultEditableInput, ...((!isChecked || disabled) &&
140
+ !indeterminate && (jsx("label", { className: checkboxClasses.editableInputContainer, htmlFor: finalInputId, children: jsx(Input, { ...defaultEditableInput, ...(disabled &&
123
141
  defaultEditableInput.disabled !== true
124
142
  ? { disabled: true }
125
- : {}), inputRef: composeRefs([
143
+ : {}), inputProps: {
144
+ ...defaultEditableInput.inputProps,
145
+ onMouseDown: handleEditableInputMouseDown,
146
+ }, inputRef: composeRefs([
126
147
  defaultEditableInput.inputRef,
127
148
  editableInputRef,
128
149
  ]), variant: "base" }) }))] }));
@@ -2,7 +2,7 @@ import type { ReactElement } from 'react';
2
2
  import type { ModalHeaderLayoutProps, ModalProps } from '../Modal';
3
3
  import { ComponentOverridableForwardRefComponentPropsFactory } from '../utils/jsx-types';
4
4
  import { CropArea, CropperComponent, CropperPropsBase } from './typings';
5
- export type CropperModalProps = Omit<ModalProps, 'children' | 'modalType' | 'extendedSplitLeftSideContent' | 'extendedSplitRightSideContent' | 'onCancel' | 'onConfirm' | 'showCancelButton' | 'showModalFooter' | 'showModalHeader' | 'confirmText' | 'title'> & ModalHeaderLayoutProps & {
5
+ export type CropperModalProps = Omit<ModalProps, 'children' | 'modalType' | 'extendedSplitLeftSideContent' | 'extendedSplitRightSideContent' | 'extendedSplitSidebarPosition' | 'onCancel' | 'onConfirm' | 'showCancelButton' | 'showModalFooter' | 'showModalHeader' | 'confirmText' | 'title'> & ModalHeaderLayoutProps & {
6
6
  /**
7
7
  * The text for the cancel button.
8
8
  */
@@ -22,7 +22,7 @@ export type DescriptionProps = DistributiveOmit<DescriptionTitleProps, 'classNam
22
22
  */
23
23
  orientation?: DescriptionOrientation;
24
24
  /**
25
- * Controls the text size of the description content
25
+ * Controls the text size of both the title and the description content
26
26
  * @default 'main'
27
27
  */
28
28
  size?: DescriptionSize;
@@ -14,7 +14,7 @@ const Description = forwardRef(function Description(props, ref) {
14
14
  size: (_a = children.props.size) !== null && _a !== void 0 ? _a : size,
15
15
  })
16
16
  : children;
17
- return (jsx(DescriptionContext.Provider, { value: { size }, children: jsxs("div", { className: cx(descriptionClasses.host, descriptionClasses.orientation(orientation), className), ref: ref, children: [jsx(DescriptionTitle, { ...rest, children: title }), injectedChildren] }) }));
17
+ return (jsx(DescriptionContext.Provider, { value: { size }, children: jsxs("div", { className: cx(descriptionClasses.host, descriptionClasses.orientation(orientation), className), ref: ref, children: [jsx(DescriptionTitle, { ...rest, size: size, children: title }), injectedChildren] }) }));
18
18
  });
19
19
 
20
20
  export { Description as default };
@@ -1,5 +1,5 @@
1
1
  import { IconDefinition } from '@mezzanine-ui/icons';
2
- import { DescriptionWidthType } from '@mezzanine-ui/core/description';
2
+ import { DescriptionSize, DescriptionWidthType } from '@mezzanine-ui/core/description';
3
3
  import { BadgeDotVariant } from '@mezzanine-ui/core/badge';
4
4
  import { Placement } from '@floating-ui/react-dom';
5
5
  interface DescriptionTitleBaseProps {
@@ -15,6 +15,11 @@ interface DescriptionTitleBaseProps {
15
15
  * Title text
16
16
  */
17
17
  children: string;
18
+ /**
19
+ * Controls the text size of the title.
20
+ * @default 'main'
21
+ */
22
+ size?: DescriptionSize;
18
23
  /**
19
24
  * Controls the layout width behavior of the title
20
25
  * @default 'stretch'
@@ -8,8 +8,8 @@ import Icon from '../Icon/Icon.js';
8
8
  import cx from 'clsx';
9
9
 
10
10
  const DescriptionTitle = forwardRef(function DescriptionTitle(props, ref) {
11
- const { badge, className, children, icon, tooltip, tooltipPlacement, widthType = 'stretch', } = props;
12
- return (jsxs("div", { className: cx(descriptionClasses.titleHost, descriptionClasses.titleWidth(widthType), className), ref: ref, children: [badge ? (jsx(Badge, { variant: badge, text: children, className: descriptionClasses.titleText })) : (jsx("span", { className: descriptionClasses.titleText, children: children })), icon ? (tooltip ? (jsx(Tooltip, { title: tooltip, options: {
11
+ const { badge, className, children, icon, size, tooltip, tooltipPlacement, widthType = 'stretch', } = props;
12
+ return (jsxs("div", { className: cx(descriptionClasses.titleHost, descriptionClasses.titleWidth(widthType), size && descriptionClasses.titleSize(size), className), ref: ref, children: [badge ? (jsx(Badge, { variant: badge, text: children, size: size, className: descriptionClasses.titleText })) : (jsx("span", { className: descriptionClasses.titleText, children: children })), icon ? (tooltip ? (jsx(Tooltip, { title: tooltip, options: {
13
13
  placement: tooltipPlacement !== null && tooltipPlacement !== void 0 ? tooltipPlacement : 'top',
14
14
  }, children: ({ onMouseEnter, onMouseLeave, ref }) => (jsx(Icon, { ref: ref, icon: icon, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, size: 16 })) })) : (jsx(Icon, { icon: icon, size: 16 }))) : null] }));
15
15
  });