@mezzanine-ui/react 1.0.0-beta.5 → 1.0.0-beta.6

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 (75) hide show
  1. package/AutoComplete/AutoComplete.d.ts +5 -0
  2. package/AutoComplete/AutoComplete.js +3 -2
  3. package/Breadcrumb/BreadcrumbDropdown.js +1 -1
  4. package/Breadcrumb/BreadcrumbItem.js +1 -1
  5. package/Breadcrumb/BreadcrumbOverflowMenuItem.js +1 -1
  6. package/DateTimePicker/DateTimePicker.js +8 -4
  7. package/DateTimeRangePicker/DateTimeRangePicker.d.ts +34 -0
  8. package/DateTimeRangePicker/DateTimeRangePicker.js +118 -0
  9. package/DateTimeRangePicker/index.d.ts +2 -0
  10. package/DateTimeRangePicker/index.js +1 -0
  11. package/Drawer/Drawer.d.ts +132 -1
  12. package/Drawer/Drawer.js +47 -3
  13. package/Dropdown/Dropdown.d.ts +3 -3
  14. package/Dropdown/Dropdown.js +7 -29
  15. package/Form/FormField.d.ts +14 -2
  16. package/Form/FormField.js +3 -3
  17. package/Input/Input.js +3 -19
  18. package/Input/SelectButton/SelectButton.d.ts +25 -4
  19. package/Input/SelectButton/SelectButton.js +21 -9
  20. package/Modal/MediaPreviewModal.d.ts +11 -0
  21. package/Modal/MediaPreviewModal.js +24 -7
  22. package/Modal/Modal.d.ts +1 -1
  23. package/Modal/Modal.js +1 -1
  24. package/Modal/useModalContainer.js +6 -2
  25. package/MultipleDatePicker/MultipleDatePicker.d.ts +62 -0
  26. package/MultipleDatePicker/MultipleDatePicker.js +176 -0
  27. package/MultipleDatePicker/MultipleDatePickerTrigger.d.ts +56 -0
  28. package/MultipleDatePicker/MultipleDatePickerTrigger.js +92 -0
  29. package/MultipleDatePicker/index.d.ts +6 -0
  30. package/MultipleDatePicker/index.js +3 -0
  31. package/MultipleDatePicker/useMultipleDatePickerValue.d.ts +55 -0
  32. package/MultipleDatePicker/useMultipleDatePickerValue.js +68 -0
  33. package/NotificationCenter/NotificationCenterDrawer.d.ts +10 -52
  34. package/NotificationCenter/NotificationCenterDrawer.js +128 -0
  35. package/NotificationCenter/index.d.ts +2 -0
  36. package/NotificationCenter/index.js +1 -0
  37. package/OverflowTooltip/index.d.ts +2 -2
  38. package/Picker/RangePickerTrigger.js +1 -1
  39. package/Section/Section.d.ts +32 -0
  40. package/Section/Section.js +62 -0
  41. package/Section/index.d.ts +2 -0
  42. package/Select/Select.d.ts +9 -4
  43. package/Select/Select.js +2 -2
  44. package/Select/TreeSelect.d.ts +10 -5
  45. package/Select/TreeSelect.js +12 -5
  46. package/TimePanel/TimePanelColumn.js +16 -14
  47. package/TimeRangePicker/TimeRangePicker.d.ts +29 -0
  48. package/TimeRangePicker/TimeRangePicker.js +96 -0
  49. package/TimeRangePicker/index.d.ts +3 -0
  50. package/TimeRangePicker/index.js +2 -0
  51. package/TimeRangePicker/useTimeRangePickerValue.d.ts +30 -0
  52. package/TimeRangePicker/useTimeRangePickerValue.js +92 -0
  53. package/Transition/Rotate.js +2 -5
  54. package/Tree/TreeNode.js +2 -2
  55. package/index.d.ts +9 -6
  56. package/index.js +7 -6
  57. package/package.json +4 -4
  58. package/AppBar/AppBar.d.ts +0 -14
  59. package/AppBar/AppBar.js +0 -33
  60. package/AppBar/AppBarBrand.d.ts +0 -4
  61. package/AppBar/AppBarBrand.js +0 -11
  62. package/AppBar/AppBarMain.d.ts +0 -4
  63. package/AppBar/AppBarMain.js +0 -11
  64. package/AppBar/AppBarSupport.d.ts +0 -4
  65. package/AppBar/AppBarSupport.js +0 -11
  66. package/AppBar/index.d.ts +0 -8
  67. package/AppBar/index.js +0 -4
  68. package/Popconfirm/Popconfirm.d.ts +0 -16
  69. package/Popconfirm/Popconfirm.js +0 -15
  70. package/Popconfirm/index.d.ts +0 -2
  71. package/Popconfirm/index.js +0 -1
  72. package/Popover/Popover.d.ts +0 -23
  73. package/Popover/Popover.js +0 -35
  74. package/Popover/index.d.ts +0 -2
  75. package/Popover/index.js +0 -1
@@ -148,6 +148,11 @@ export interface AutoCompleteBaseProps extends Omit<SelectTriggerProps, 'active'
148
148
  * The z-index of the dropdown.
149
149
  */
150
150
  dropdownZIndex?: number | string;
151
+ /**
152
+ * Whether to enable portal for the dropdown.
153
+ * @default true
154
+ */
155
+ globalPortal?: boolean;
151
156
  /**
152
157
  * Callback fired when the dropdown list reaches the bottom.
153
158
  * Only fires when `menuMaxHeight` is set and the list is scrollable.
@@ -46,7 +46,7 @@ function isOptionSelected(option, value, isMultiple) {
46
46
  */
47
47
  const AutoComplete = forwardRef(function AutoComplete(props, ref) {
48
48
  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, keepSearchTextOnBlur = false, inputPosition = 'outside', inputProps, inputRef, loading = false, loadingText, menuMaxHeight, mode = 'single', name, onClear: onClearProp, onChange: onChangeProp, onInsert, onSearch, onSearchTextChange, onVisibilityChange, open: openProp, options: optionsProp, placeholder = '', prefix, required = requiredFromFormControl || false, searchDebounceTime = 300, searchTextControlRef, size, trimOnCreate = true, value: valueProp, createActionText, createActionTextTemplate = '建立 "{text}"', dropdownZIndex, onReachBottom, onLeaveBottom, } = props;
49
+ const { addable = false, asyncData = false, className, createSeparators = [',', '+', '\n'], defaultValue, disabled = disabledFromFormControl || false, disabledOptionsFilter = false, emptyText, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, id, keepSearchTextOnBlur = false, inputPosition = 'outside', inputProps, inputRef, loading = false, loadingText, menuMaxHeight, mode = 'single', name, onClear: onClearProp, onChange: onChangeProp, onInsert, onSearch, onSearchTextChange, onVisibilityChange, open: openProp, options: optionsProp, placeholder = '', prefix, required = requiredFromFormControl || false, searchDebounceTime = 300, searchTextControlRef, size, trimOnCreate = true, value: valueProp, createActionText, createActionTextTemplate = '建立 "{text}"', dropdownZIndex, globalPortal = true, onReachBottom, onLeaveBottom, } = props;
50
50
  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
51
51
  const isMultiple = mode === 'multiple';
52
52
  const isSingle = !isMultiple;
@@ -424,13 +424,14 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
424
424
  return (jsx(SelectControlContext.Provider, { value: context, children: jsx("div", { ref: nodeRef, className: cx(autocompleteClasses.host, {
425
425
  [autocompleteClasses.hostFullWidth]: fullWidth,
426
426
  [autocompleteClasses.hostInsideClosed]: inputPosition === 'inside' && !open,
427
+ [autocompleteClasses.hostMode(mode)]: mode,
427
428
  }), children: jsx(Dropdown, { actionText: shouldShowCreateAction
428
429
  ? (createActionText
429
430
  ? createActionText(insertText)
430
431
  : createActionTextTemplate.replace('{text}', insertText))
431
432
  : undefined, activeIndex: activeIndex, disabled: isInputDisabled, emptyText: emptyText, followText: searchText, inputPosition: inputPosition, isMatchInputValue: true, listboxId: menuId, loadingText: loadingText, maxHeight: menuMaxHeight, mode: mode, onActionCustom: shouldShowCreateAction
432
433
  ? handleActionCustom
433
- : undefined, onItemHover: setActiveIndex, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: open, options: dropdownOptionsForRender, placement: "bottom", sameWidth: true, showDropdownActions: shouldShowCreateAction, showActionShowTopBar: shouldShowCreateAction, status: dropdownStatus, type: "default", value: dropdownValue, zIndex: dropdownZIndex, onReachBottom: onReachBottom, onLeaveBottom: onLeaveBottom, children: jsx(SelectTrigger, { ref: composedRef, active: open, className: className, clearable: true, isForceClearable: true, disabled: isInputDisabled, fullWidth: fullWidth, inputRef: inputRef, mode: mode, onTagClose: wrappedOnChange, onClear: handleClear, placeholder: getPlaceholder(), prefix: prefix, readOnly: false, required: required, type: error ? 'error' : 'default', inputProps: {
434
+ : undefined, onItemHover: setActiveIndex, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: open, options: dropdownOptionsForRender, 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, isForceClearable: true, disabled: isInputDisabled, fullWidth: fullWidth, inputRef: inputRef, mode: mode, onTagClose: wrappedOnChange, onClear: handleClear, placeholder: getPlaceholder(), prefix: prefix, readOnly: false, required: required, type: error ? 'error' : 'default', inputProps: {
434
435
  ...resolvedInputProps,
435
436
  onClick: (e) => {
436
437
  var _a;
@@ -16,7 +16,7 @@ const BreadcrumbDropdown = forwardRef(function BreadcrumbDropdown(props, ref) {
16
16
  setOpen(!open);
17
17
  onClick === null || onClick === void 0 ? void 0 : onClick();
18
18
  };
19
- return (jsx("span", { className: cx(breadcrumbItemClasses.host, className), ref: ref, children: jsx(Dropdown, { onClose: () => setOpen(false), onOpen: () => handleClick(), options: options, placement: "bottom-start", ...rest, children: jsxs("button", { className: cx(breadcrumbItemClasses.trigger, open && breadcrumbItemClasses.expanded, current && breadcrumbItemClasses.current), type: "button", children: [name && (jsx(Typography, { variant: current ? 'caption-highlight' : 'caption', children: name })), jsx(Rotate, { in: open, children: jsx(Icon, { className: breadcrumbItemClasses.icon, icon: ChevronDownIcon, size: 14 }) })] }) }) }));
19
+ return (jsx("span", { className: cx(breadcrumbItemClasses.host, open && breadcrumbItemClasses.expanded, current && breadcrumbItemClasses.current, className), ref: ref, children: jsx(Dropdown, { onClose: () => setOpen(false), onOpen: () => handleClick(), options: options, placement: "bottom-start", ...rest, children: jsxs("button", { className: breadcrumbItemClasses.trigger, type: "button", children: [name && (jsx(Typography, { variant: current ? 'caption-highlight' : 'caption', children: name })), jsx(Rotate, { in: open, children: jsx(Icon, { className: breadcrumbItemClasses.icon, icon: ChevronDownIcon, size: 14 }) })] }) }) }));
20
20
  });
21
21
 
22
22
  export { BreadcrumbDropdown as default };
@@ -21,7 +21,7 @@ const BreadcrumbItem = forwardRef(function BreadcrumbItem(props, ref) {
21
21
  const handleClick = () => {
22
22
  onClick === null || onClick === void 0 ? void 0 : onClick();
23
23
  };
24
- return (jsx("span", { ...rest, className: cx(breadcrumbItemClasses.host, className), ref: ref, children: jsx(TriggerComponent, { className: cx(breadcrumbItemClasses.trigger, current && breadcrumbItemClasses.current), href: TriggerComponent === 'a' ? href : undefined, onClick: TriggerComponent === 'a' ? handleClick : undefined, rel: TriggerComponent === 'a' ? rel : undefined, target: TriggerComponent === 'a' ? target : undefined, children: name && (jsx(Typography, { variant: current ? 'caption-highlight' : 'caption', children: name })) }) }));
24
+ return (jsx("span", { ...rest, className: cx(breadcrumbItemClasses.host, current && breadcrumbItemClasses.current, className), ref: ref, children: jsx(TriggerComponent, { className: breadcrumbItemClasses.trigger, href: TriggerComponent === 'a' ? href : undefined, onClick: TriggerComponent === 'a' ? handleClick : undefined, rel: TriggerComponent === 'a' ? rel : undefined, target: TriggerComponent === 'a' ? target : undefined, children: name && (jsx(Typography, { variant: current ? 'caption-highlight' : 'caption', children: name })) }) }));
25
25
  });
26
26
 
27
27
  export { BreadcrumbItem as default };
@@ -21,7 +21,7 @@ const BreadcrumbOverflowMenuItem = forwardRef(function BreadcrumbOverflowMenuIte
21
21
  const handleClick = () => {
22
22
  onClick === null || onClick === void 0 ? void 0 : onClick();
23
23
  };
24
- return (jsx("span", { ...rest, className: cx(breadcrumbOverflowMenuItemClasses.host, className), ref: ref, children: jsx(TriggerComponent, { className: cx(breadcrumbOverflowMenuItemClasses.trigger), href: TriggerComponent === 'a' ? href : undefined, onClick: TriggerComponent === 'a' ? handleClick : undefined, rel: TriggerComponent === 'a' ? rel : undefined, target: TriggerComponent === 'a' ? target : undefined, children: jsx(Typography, { variant: 'label-primary', children: name }) }) }));
24
+ return (jsx("span", { ...rest, className: cx(breadcrumbOverflowMenuItemClasses.host, className), ref: ref, children: jsx(TriggerComponent, { className: breadcrumbOverflowMenuItemClasses.trigger, href: TriggerComponent === 'a' ? href : undefined, onClick: TriggerComponent === 'a' ? handleClick : undefined, rel: TriggerComponent === 'a' ? rel : undefined, target: TriggerComponent === 'a' ? target : undefined, children: jsx(Typography, { variant: 'label-primary', children: name }) }) }));
25
25
  });
26
26
 
27
27
  export { BreadcrumbOverflowMenuItem as default };
@@ -103,7 +103,7 @@ const DateTimePicker = forwardRef(function DateTimePicker(props, ref) {
103
103
  onChangeProp(combined);
104
104
  }
105
105
  }, [combineDateTime, onChangeProp]);
106
- // Handle left complete - auto focus right
106
+ // Handle left complete
107
107
  const onLeftComplete = useCallback(() => {
108
108
  if (timeValue) {
109
109
  onClose();
@@ -115,10 +115,14 @@ const DateTimePicker = forwardRef(function DateTimePicker(props, ref) {
115
115
  }, 0);
116
116
  }
117
117
  }, [timeValue, onClose]);
118
- // Handle left complete - auto focus right
118
+ // Handle right complete
119
119
  const onRightComplete = useCallback(() => {
120
120
  if (dateValue) {
121
- onClose();
121
+ /** Don't close the time panel */
122
+ setTimeout(() => {
123
+ var _a;
124
+ (_a = inputRightRef.current) === null || _a === void 0 ? void 0 : _a.focus();
125
+ }, 0);
122
126
  }
123
127
  else {
124
128
  setTimeout(() => {
@@ -126,7 +130,7 @@ const DateTimePicker = forwardRef(function DateTimePicker(props, ref) {
126
130
  (_a = inputLeftRef.current) === null || _a === void 0 ? void 0 : _a.focus();
127
131
  }, 0);
128
132
  }
129
- }, [dateValue, onClose]);
133
+ }, [dateValue]);
130
134
  // Handle date change from input
131
135
  const onChangeLeft = useCallback((isoValue) => {
132
136
  if (!isoValue) {
@@ -0,0 +1,34 @@
1
+ import { DateType } from '@mezzanine-ui/core/calendar';
2
+ import { DateTimePickerProps } from '../DateTimePicker';
3
+ export type DateTimeRangePickerValue = [
4
+ DateType | undefined,
5
+ DateType | undefined
6
+ ];
7
+ export interface DateTimeRangePickerProps extends Omit<DateTimePickerProps, 'defaultValue' | 'onChange' | 'value' | 'ref' | 'prefix'> {
8
+ /**
9
+ * CSS class name for the host element.
10
+ */
11
+ className?: string;
12
+ /**
13
+ * The direction of the two date-time pickers.
14
+ * @default 'row'
15
+ */
16
+ direction?: 'row' | 'column';
17
+ /**
18
+ * Change handler for the range value.
19
+ * Called when either from or to value changes.
20
+ */
21
+ onChange?: (value: DateTimeRangePickerValue) => void;
22
+ /**
23
+ * Current value of date-time range picker.
24
+ * Array of [from, to] where each can be a DateType or undefined.
25
+ */
26
+ value?: DateTimeRangePickerValue;
27
+ }
28
+ /**
29
+ * The react component for `mezzanine` date-time range picker.
30
+ * This component combines two DateTimePicker components for selecting a date-time range.
31
+ * Notice that any component related to date-time picker should be used along with `CalendarContext`.
32
+ */
33
+ declare const DateTimeRangePicker: import("react").ForwardRefExoticComponent<DateTimeRangePickerProps & import("react").RefAttributes<HTMLDivElement>>;
34
+ export default DateTimeRangePicker;
@@ -0,0 +1,118 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { dateTimeRangePickerClasses } from '@mezzanine-ui/core/date-time-range-picker';
4
+ import { LongTailArrowDownIcon, LongTailArrowRightIcon } from '@mezzanine-ui/icons';
5
+ import { forwardRef, useMemo, useCallback } from 'react';
6
+ import DateTimePicker from '../DateTimePicker/DateTimePicker.js';
7
+ import Icon from '../Icon/Icon.js';
8
+ import cx from 'clsx';
9
+
10
+ /**
11
+ * The react component for `mezzanine` date-time range picker.
12
+ * This component combines two DateTimePicker components for selecting a date-time range.
13
+ * Notice that any component related to date-time picker should be used along with `CalendarContext`.
14
+ */
15
+ const DateTimeRangePicker = forwardRef(function DateTimeRangePicker(props, ref) {
16
+ const { className, direction = 'row', onChange: onChangeProp, value: valueProp,
17
+ // Shared DateTimePicker props
18
+ calendarProps, calendarRef, clearable, disabled, disabledMonthSwitch, disabledYearSwitch, disableOnDoubleNext, disableOnDoublePrev, disableOnNext, disableOnPrev, displayMonthLocale, error, fadeProps, formatDate, formatTime, fullWidth, hideHour, hideMinute, hideSecond, hourStep, isDateDisabled, isHalfYearDisabled, isMonthDisabled, isQuarterDisabled, isWeekDisabled, isYearDisabled, minuteStep, mode, onClear, onPanelToggle, placeholderLeft, placeholderRight, popperProps, popperPropsTime, readOnly, referenceDate, required, secondStep, size, } = props;
19
+ const fromValue = useMemo(() => valueProp === null || valueProp === void 0 ? void 0 : valueProp[0], [valueProp]);
20
+ const toValue = useMemo(() => valueProp === null || valueProp === void 0 ? void 0 : valueProp[1], [valueProp]);
21
+ // Handler for "from" DateTimePicker change
22
+ const handleFromChange = useCallback((newFrom) => {
23
+ if (!onChangeProp)
24
+ return;
25
+ onChangeProp([newFrom, toValue]);
26
+ }, [onChangeProp, toValue]);
27
+ // Handler for "to" DateTimePicker change
28
+ const handleToChange = useCallback((newTo) => {
29
+ if (!onChangeProp)
30
+ return;
31
+ onChangeProp([fromValue, newTo]);
32
+ }, [onChangeProp, fromValue]);
33
+ const sharedProps = useMemo(() => ({
34
+ calendarProps,
35
+ calendarRef,
36
+ clearable,
37
+ disabled,
38
+ disabledMonthSwitch,
39
+ disabledYearSwitch,
40
+ disableOnDoubleNext,
41
+ disableOnDoublePrev,
42
+ disableOnNext,
43
+ disableOnPrev,
44
+ displayMonthLocale,
45
+ error,
46
+ fadeProps,
47
+ formatDate,
48
+ formatTime,
49
+ fullWidth,
50
+ hideHour,
51
+ hideMinute,
52
+ hideSecond,
53
+ hourStep,
54
+ isDateDisabled,
55
+ isHalfYearDisabled,
56
+ isMonthDisabled,
57
+ isQuarterDisabled,
58
+ isWeekDisabled,
59
+ isYearDisabled,
60
+ minuteStep,
61
+ mode,
62
+ onClear,
63
+ onPanelToggle,
64
+ placeholderLeft,
65
+ placeholderRight,
66
+ popperProps,
67
+ popperPropsTime,
68
+ readOnly,
69
+ referenceDate,
70
+ required,
71
+ secondStep,
72
+ size,
73
+ }), [
74
+ calendarProps,
75
+ calendarRef,
76
+ clearable,
77
+ disabled,
78
+ disabledMonthSwitch,
79
+ disabledYearSwitch,
80
+ disableOnDoubleNext,
81
+ disableOnDoublePrev,
82
+ disableOnNext,
83
+ disableOnPrev,
84
+ displayMonthLocale,
85
+ error,
86
+ fadeProps,
87
+ formatDate,
88
+ formatTime,
89
+ fullWidth,
90
+ hideHour,
91
+ hideMinute,
92
+ hideSecond,
93
+ hourStep,
94
+ isDateDisabled,
95
+ isHalfYearDisabled,
96
+ isMonthDisabled,
97
+ isQuarterDisabled,
98
+ isWeekDisabled,
99
+ isYearDisabled,
100
+ minuteStep,
101
+ mode,
102
+ onClear,
103
+ onPanelToggle,
104
+ placeholderLeft,
105
+ placeholderRight,
106
+ popperProps,
107
+ popperPropsTime,
108
+ readOnly,
109
+ referenceDate,
110
+ required,
111
+ secondStep,
112
+ size,
113
+ ]);
114
+ const ArrowIcon = direction === 'column' ? LongTailArrowDownIcon : LongTailArrowRightIcon;
115
+ return (jsxs("div", { ref: ref, className: cx(dateTimeRangePickerClasses.host, direction === 'column' ? dateTimeRangePickerClasses.column : dateTimeRangePickerClasses.row, className), children: [jsx(DateTimePicker, { ...sharedProps, onChange: handleFromChange, value: fromValue }), jsx(Icon, { className: dateTimeRangePickerClasses.arrow, icon: ArrowIcon }), jsx(DateTimePicker, { ...sharedProps, onChange: handleToChange, value: toValue })] }));
116
+ });
117
+
118
+ export { DateTimeRangePicker as default };
@@ -0,0 +1,2 @@
1
+ export { default as DateTimeRangePicker } from './DateTimeRangePicker';
2
+ export type { DateTimeRangePickerProps, DateTimeRangePickerValue, } from './DateTimeRangePicker';
@@ -0,0 +1 @@
1
+ export { default as DateTimeRangePicker } from './DateTimeRangePicker.js';
@@ -1,11 +1,39 @@
1
+ import React, { type ChangeEventHandler } from 'react';
1
2
  import { DrawerSize } from '@mezzanine-ui/core/drawer';
3
+ import { IconDefinition } from '@mezzanine-ui/icons';
4
+ import { ButtonIconType, ButtonSize, ButtonVariant } from '@mezzanine-ui/core/button';
2
5
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
6
  import { BackdropProps } from '../Backdrop';
4
7
  export interface DrawerProps extends NativeElementPropsWithoutKeyAndRef<'div'>, Pick<BackdropProps, 'container' | 'disableCloseOnBackdropClick' | 'disablePortal' | 'onBackdropClick' | 'onClose' | 'open'> {
8
+ /**
9
+ * Disabled state for the ghost action button.
10
+ */
11
+ bottomGhostActionDisabled?: boolean;
12
+ /**
13
+ * Icon for the ghost action button.
14
+ */
15
+ bottomGhostActionIcon?: IconDefinition;
16
+ /**
17
+ * Icon type for the ghost action button.
18
+ */
19
+ bottomGhostActionIconType?: ButtonIconType;
20
+ /**
21
+ * Loading state for the ghost action button.
22
+ */
23
+ bottomGhostActionLoading?: boolean;
24
+ /**
25
+ * Size for the ghost action button.
26
+ */
27
+ bottomGhostActionSize?: ButtonSize;
5
28
  /**
6
29
  * Text for the ghost action button in the bottom action area.
7
30
  */
8
31
  bottomGhostActionText?: string;
32
+ /**
33
+ * Variant for the ghost action button.
34
+ * @default 'base-ghost'
35
+ */
36
+ bottomGhostActionVariant?: ButtonVariant;
9
37
  /**
10
38
  * Click handler for the ghost action button in the bottom action area.
11
39
  */
@@ -18,14 +46,110 @@ export interface DrawerProps extends NativeElementPropsWithoutKeyAndRef<'div'>,
18
46
  * Click handler for the secondary action button in the bottom action area.
19
47
  */
20
48
  bottomOnSecondaryActionClick?: VoidFunction;
49
+ /**
50
+ * Disabled state for the primary action button.
51
+ */
52
+ bottomPrimaryActionDisabled?: boolean;
53
+ /**
54
+ * Icon for the primary action button.
55
+ */
56
+ bottomPrimaryActionIcon?: IconDefinition;
57
+ /**
58
+ * Icon type for the primary action button.
59
+ */
60
+ bottomPrimaryActionIconType?: ButtonIconType;
61
+ /**
62
+ * Loading state for the primary action button.
63
+ */
64
+ bottomPrimaryActionLoading?: boolean;
65
+ /**
66
+ * Size for the primary action button.
67
+ */
68
+ bottomPrimaryActionSize?: ButtonSize;
21
69
  /**
22
70
  * Text for the primary action button in the bottom action area.
23
71
  */
24
72
  bottomPrimaryActionText?: string;
73
+ /**
74
+ * Variant for the primary action button.
75
+ * @default 'base-primary'
76
+ */
77
+ bottomPrimaryActionVariant?: ButtonVariant;
78
+ /**
79
+ * Disabled state for the secondary action button.
80
+ */
81
+ bottomSecondaryActionDisabled?: boolean;
82
+ /**
83
+ * Icon for the secondary action button.
84
+ */
85
+ bottomSecondaryActionIcon?: IconDefinition;
86
+ /**
87
+ * Icon type for the secondary action button.
88
+ */
89
+ bottomSecondaryActionIconType?: ButtonIconType;
90
+ /**
91
+ * Loading state for the secondary action button.
92
+ */
93
+ bottomSecondaryActionLoading?: boolean;
94
+ /**
95
+ * Size for the secondary action button.
96
+ */
97
+ bottomSecondaryActionSize?: ButtonSize;
25
98
  /**
26
99
  * Text for the secondary action button in the bottom action area.
27
100
  */
28
101
  bottomSecondaryActionText?: string;
102
+ /**
103
+ * Variant for the secondary action button.
104
+ * @default 'base-secondary'
105
+ */
106
+ bottomSecondaryActionVariant?: ButtonVariant;
107
+ /**
108
+ * The label of the all radio in control bar.
109
+ */
110
+ controlBarAllRadioLabel?: string;
111
+ /**
112
+ * The label of the custom button in control bar.
113
+ */
114
+ controlBarCustomButtonLabel?: string;
115
+ /**
116
+ * The default value of the radio group in control bar.
117
+ */
118
+ controlBarDefaultValue?: string;
119
+ /**
120
+ * Whether the control bar content is empty (for disabling custom button).
121
+ */
122
+ controlBarIsEmpty?: boolean;
123
+ /**
124
+ * The callback function when the custom button is clicked in control bar.
125
+ */
126
+ controlBarOnCustomButtonClick?: VoidFunction;
127
+ /**
128
+ * The callback function when the radio group value changes in control bar.
129
+ */
130
+ controlBarOnRadioChange?: ChangeEventHandler<HTMLInputElement>;
131
+ /**
132
+ * The label of the read radio in control bar.
133
+ */
134
+ controlBarReadRadioLabel?: string;
135
+ /**
136
+ * Controls whether to display the control bar.
137
+ * @default false
138
+ */
139
+ controlBarShow?: boolean;
140
+ /**
141
+ * Controls whether to display the unread button in control bar.
142
+ * @default false
143
+ */
144
+ controlBarShowUnreadButton?: boolean;
145
+ /**
146
+ * The label of the unread radio in control bar.
147
+ */
148
+ controlBarUnreadRadioLabel?: string;
149
+ /**
150
+ * The value of the radio group in control bar.
151
+ */
152
+ controlBarValue?: string;
29
153
  /**
30
154
  * Controls whether to disable closing drawer while escape key down.
31
155
  * @default false
@@ -43,11 +167,18 @@ export interface DrawerProps extends NativeElementPropsWithoutKeyAndRef<'div'>,
43
167
  * Controls whether to display the header area.
44
168
  */
45
169
  isHeaderDisplay?: boolean;
170
+ /**
171
+ * Custom render function for the control bar area.
172
+ * The control bar will be rendered between the header and content areas.
173
+ * If provided, this will override the default control bar rendering and control bar-related props.
174
+ * @returns ReactNode - The custom control bar element
175
+ */
176
+ renderControlBar?: () => React.ReactNode;
46
177
  /**
47
178
  * Controls the width of the drawer.
48
179
  * @default 'medium'
49
180
  */
50
181
  size?: DrawerSize;
51
182
  }
52
- declare const Drawer: import("react").ForwardRefExoticComponent<DrawerProps & import("react").RefAttributes<HTMLDivElement>>;
183
+ declare const Drawer: React.ForwardRefExoticComponent<DrawerProps & React.RefAttributes<HTMLDivElement>>;
53
184
  export default Drawer;
package/Drawer/Drawer.js CHANGED
@@ -1,22 +1,66 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { forwardRef, useState } from 'react';
2
+ import { forwardRef, useState, useMemo } from 'react';
3
3
  import { drawerClasses } from '@mezzanine-ui/core/drawer';
4
4
  import { useDocumentEscapeKeyDown } from '../hooks/useDocumentEscapeKeyDown.js';
5
5
  import useTopStack from '../_internal/SlideFadeOverlay/useTopStack.js';
6
6
  import ClearActions from '../ClearActions/ClearActions.js';
7
7
  import Button from '../Button/Button.js';
8
+ import Radio from '../Radio/Radio.js';
9
+ import RadioGroup from '../Radio/RadioGroup.js';
8
10
  import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
9
11
  import Backdrop from '../Backdrop/Backdrop.js';
10
12
  import Slide from '../Transition/Slide.js';
11
13
  import cx from 'clsx';
12
14
 
13
15
  const Drawer = forwardRef((props, ref) => {
14
- const { bottomGhostActionText, bottomOnGhostActionClick, bottomOnPrimaryActionClick, bottomOnSecondaryActionClick, bottomPrimaryActionText, bottomSecondaryActionText, children, className, container, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disablePortal, headerTitle, isBottomDisplay, isHeaderDisplay, onBackdropClick, onClose, open, size = 'medium', ...rest } = props;
16
+ const { bottomGhostActionDisabled, bottomGhostActionIcon, bottomGhostActionIconType, bottomGhostActionLoading, bottomGhostActionSize, bottomGhostActionText, bottomGhostActionVariant = 'base-ghost', bottomOnGhostActionClick, bottomOnPrimaryActionClick, bottomOnSecondaryActionClick, bottomPrimaryActionDisabled, bottomPrimaryActionIcon, bottomPrimaryActionIconType, bottomPrimaryActionLoading, bottomPrimaryActionSize, bottomPrimaryActionText, bottomPrimaryActionVariant = 'base-primary', bottomSecondaryActionDisabled, bottomSecondaryActionIcon, bottomSecondaryActionIconType, bottomSecondaryActionLoading, bottomSecondaryActionSize, bottomSecondaryActionText, bottomSecondaryActionVariant = 'base-secondary', children, className, container, controlBarAllRadioLabel, controlBarCustomButtonLabel = '全部已讀', controlBarDefaultValue, controlBarIsEmpty = false, controlBarOnCustomButtonClick, controlBarOnRadioChange, controlBarReadRadioLabel, controlBarShow = false, controlBarShowUnreadButton = false, controlBarUnreadRadioLabel, controlBarValue, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disablePortal, headerTitle, isBottomDisplay, isHeaderDisplay, onBackdropClick, onClose, open, renderControlBar: customRenderControlBar, size = 'medium', ...rest } = props;
15
17
  const [exited, setExited] = useState(true);
16
18
  /**
17
19
  * Escape keydown close: escape will only close the top drawer
18
20
  */
19
21
  const checkIsOnTheTop = useTopStack(open);
22
+ const renderControlBar = useMemo(() => {
23
+ // If custom renderControlBar is provided, use it
24
+ if (customRenderControlBar) {
25
+ return customRenderControlBar;
26
+ }
27
+ // Default control bar implementation
28
+ if (!controlBarShow) {
29
+ return undefined;
30
+ }
31
+ return () => {
32
+ const radios = [];
33
+ if (controlBarAllRadioLabel) {
34
+ radios.push(jsx(Radio, { type: "segment", value: "all", children: controlBarAllRadioLabel }, "all"));
35
+ }
36
+ if (controlBarReadRadioLabel) {
37
+ radios.push(jsx(Radio, { type: "segment", value: "read", children: controlBarReadRadioLabel }, "read"));
38
+ }
39
+ if (controlBarUnreadRadioLabel && controlBarShowUnreadButton) {
40
+ radios.push(jsx(Radio, { type: "segment", value: "unread", children: controlBarUnreadRadioLabel }, "unread"));
41
+ }
42
+ const hasRadios = radios.length > 0;
43
+ const hasButton = controlBarOnCustomButtonClick !== undefined;
44
+ // Don't render if neither radios nor button are provided
45
+ if (!hasRadios && !hasButton) {
46
+ return null;
47
+ }
48
+ return (jsxs("div", { className: cx(drawerClasses.controlBar, !hasRadios && hasButton && drawerClasses.controlBarButtonOnly), children: [hasRadios && (jsx(RadioGroup, { defaultValue: controlBarDefaultValue !== null && controlBarDefaultValue !== void 0 ? controlBarDefaultValue : 'all', onChange: controlBarOnRadioChange, size: "minor", type: "segment", value: controlBarValue, children: radios })), hasButton && (jsx(Button, { disabled: controlBarIsEmpty, onClick: controlBarOnCustomButtonClick, size: "minor", type: "button", variant: "base-ghost", children: controlBarCustomButtonLabel }))] }));
49
+ };
50
+ }, [
51
+ controlBarAllRadioLabel,
52
+ controlBarCustomButtonLabel,
53
+ controlBarDefaultValue,
54
+ controlBarIsEmpty,
55
+ controlBarOnCustomButtonClick,
56
+ controlBarOnRadioChange,
57
+ controlBarReadRadioLabel,
58
+ controlBarShow,
59
+ controlBarShowUnreadButton,
60
+ controlBarUnreadRadioLabel,
61
+ controlBarValue,
62
+ customRenderControlBar,
63
+ ]);
20
64
  useDocumentEscapeKeyDown(() => {
21
65
  if (!open || disableCloseOnEscapeKeyDown || !onClose) {
22
66
  return;
@@ -37,7 +81,7 @@ const Drawer = forwardRef((props, ref) => {
37
81
  }, easing: {
38
82
  enter: MOTION_EASING.entrance,
39
83
  exit: MOTION_EASING.exit,
40
- }, in: open, onEntered: () => setExited(false), onExited: () => setExited(true), ref: ref, children: jsxs("div", { ...rest, className: cx(drawerClasses.host, drawerClasses.right, drawerClasses.size(size), className), children: [isHeaderDisplay && (jsxs("div", { className: drawerClasses.header, children: [headerTitle, jsx(ClearActions, { onClick: onClose })] })), jsx("div", { className: drawerClasses.content, children: children }), isBottomDisplay && (jsxs("div", { className: drawerClasses.bottom, children: [jsx("div", { children: bottomGhostActionText && bottomOnGhostActionClick && (jsx(Button, { onClick: bottomOnGhostActionClick, type: "button", variant: "base-ghost", children: bottomGhostActionText })) }), jsxs("div", { className: drawerClasses['bottom__actions'], children: [bottomSecondaryActionText && bottomOnSecondaryActionClick && (jsx(Button, { onClick: bottomOnSecondaryActionClick, type: "button", variant: "base-secondary", children: bottomSecondaryActionText })), bottomPrimaryActionText && bottomOnPrimaryActionClick && (jsx(Button, { onClick: bottomOnPrimaryActionClick, type: "button", variant: "base-primary", children: bottomPrimaryActionText }))] })] }))] }) }) }));
84
+ }, in: open, onEntered: () => setExited(false), onExited: () => setExited(true), ref: ref, children: jsxs("div", { ...rest, className: cx(drawerClasses.host, drawerClasses.right, drawerClasses.size(size), className), children: [isHeaderDisplay && (jsxs("div", { className: drawerClasses.header, children: [headerTitle, jsx(ClearActions, { onClick: onClose })] })), renderControlBar === null || renderControlBar === void 0 ? void 0 : renderControlBar(), jsx("div", { className: drawerClasses.content, children: children }), isBottomDisplay && (jsxs("div", { className: drawerClasses.bottom, children: [jsx("div", { children: bottomGhostActionText && bottomOnGhostActionClick && (jsx(Button, { disabled: bottomGhostActionDisabled, icon: bottomGhostActionIcon, iconType: bottomGhostActionIconType, loading: bottomGhostActionLoading, onClick: bottomOnGhostActionClick, size: bottomGhostActionSize, type: "button", variant: bottomGhostActionVariant, children: bottomGhostActionText })) }), jsxs("div", { className: drawerClasses['bottom__actions'], children: [bottomSecondaryActionText && bottomOnSecondaryActionClick && (jsx(Button, { disabled: bottomSecondaryActionDisabled, icon: bottomSecondaryActionIcon, iconType: bottomSecondaryActionIconType, loading: bottomSecondaryActionLoading, onClick: bottomOnSecondaryActionClick, size: bottomSecondaryActionSize, type: "button", variant: bottomSecondaryActionVariant, children: bottomSecondaryActionText })), bottomPrimaryActionText && bottomOnPrimaryActionClick && (jsx(Button, { disabled: bottomPrimaryActionDisabled, icon: bottomPrimaryActionIcon, iconType: bottomPrimaryActionIconType, loading: bottomPrimaryActionLoading, onClick: bottomOnPrimaryActionClick, size: bottomPrimaryActionSize, type: "button", variant: bottomPrimaryActionVariant, children: bottomPrimaryActionText }))] })] }))] }) }) }));
41
85
  });
42
86
 
43
87
  export { Drawer as default };
@@ -163,12 +163,12 @@ export interface DropdownProps extends DropdownItemSharedProps {
163
163
  */
164
164
  emptyIcon?: IconDefinition;
165
165
  /**
166
- * Whether to disable portal.
166
+ * Whether to enable portal.
167
167
  * This prop is only relevant when `inputPosition` is set to 'outside'.
168
168
  * Controls whether the dropdown content is rendered within the current hierarchy or portaled to the body.
169
- * @default false
169
+ * @default true
170
170
  */
171
- disablePortal?: boolean;
171
+ globalPortal?: boolean;
172
172
  /**
173
173
  * Callback fired when the dropdown list reaches the bottom.
174
174
  * Only fires when `maxHeight` is set and the list is scrollable.
@@ -3,7 +3,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useId, useMemo, useState, useRef, useCallback, useEffect, cloneElement, createElement } from 'react';
4
4
  import cx from 'clsx';
5
5
  import { dropdownClasses } from '@mezzanine-ui/core/dropdown/dropdown';
6
- import { size, offset, autoUpdate } from '@floating-ui/react-dom';
6
+ import { size, offset } from '@floating-ui/react-dom';
7
7
  import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
8
8
  import { TransitionGroup } from 'react-transition-group';
9
9
  import Button from '../Button/Button.js';
@@ -14,7 +14,7 @@ import DropdownItem from './DropdownItem.js';
14
14
  import Popper from '../Popper/Popper.js';
15
15
 
16
16
  function Dropdown(props) {
17
- const { activeIndex: activeIndexProp, id, children, options = [], type = 'default', maxHeight, disabled = false, showDropdownActions = false, actionCancelText, actionConfirmText, actionText, actionClearText, actionCustomButtonProps, showActionShowTopBar, isMatchInputValue = false, inputPosition = 'outside', placement = 'bottom', customWidth, sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, open: openProp, onVisibilityChange, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, zIndex, status, loadingText, emptyText, emptyIcon, followText: followTextProp, disablePortal = false, onReachBottom, onLeaveBottom, onScroll, mode, value, scrollbarDefer, scrollbarDisabled, scrollbarMaxWidth, scrollbarOptions, } = props;
17
+ const { activeIndex: activeIndexProp, id, children, options = [], type = 'default', maxHeight, disabled = false, showDropdownActions = false, actionCancelText, actionConfirmText, actionText, actionClearText, actionCustomButtonProps, showActionShowTopBar, isMatchInputValue = false, inputPosition = 'outside', placement = 'bottom', customWidth, sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, open: openProp, onVisibilityChange, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, zIndex, status, loadingText, emptyText, emptyIcon, followText: followTextProp, globalPortal = true, onReachBottom, onLeaveBottom, onScroll, mode, value, scrollbarDefer, scrollbarDisabled, scrollbarMaxWidth, scrollbarOptions, } = props;
18
18
  const isInline = inputPosition === 'inside';
19
19
  const inputId = useId();
20
20
  const defaultListboxId = `${inputId}-listbox`;
@@ -101,9 +101,7 @@ function Dropdown(props) {
101
101
  if (!customWidth) {
102
102
  return null;
103
103
  }
104
- const widthValue = typeof customWidth === 'number'
105
- ? `${customWidth}px`
106
- : customWidth;
104
+ const widthValue = typeof customWidth === 'number' ? `${customWidth}px` : customWidth;
107
105
  return {
108
106
  name: 'customWidth',
109
107
  fn: ({ elements }) => {
@@ -138,7 +136,9 @@ function Dropdown(props) {
138
136
  fn: ({ elements }) => {
139
137
  const zIndexNum = typeof zIndexValue === 'number'
140
138
  ? zIndexValue
141
- : (typeof zIndexValue === 'string' ? parseInt(zIndexValue, 10) || zIndexValue : 1);
139
+ : typeof zIndexValue === 'string'
140
+ ? parseInt(zIndexValue, 10) || zIndexValue
141
+ : 1;
142
142
  Object.assign(elements.floating.style, {
143
143
  zIndex: zIndexNum,
144
144
  });
@@ -177,28 +177,6 @@ function Dropdown(props) {
177
177
  const anchorRef = useRef(null);
178
178
  const popperRef = useRef(null);
179
179
  const popperControllerRef = useRef(null);
180
- // Auto-update popper position when anchor element size changes
181
- useEffect(() => {
182
- if (!isOpen || isInline || !anchorRef.current || !popperControllerRef.current) {
183
- return;
184
- }
185
- const update = popperControllerRef.current.update;
186
- if (!update) {
187
- return;
188
- }
189
- // Get floating element from controller refs
190
- // Check refs exists before accessing nested properties
191
- const refs = popperControllerRef.current.refs;
192
- if (!refs) {
193
- return;
194
- }
195
- const floatingElement = refs.floating.current;
196
- if (!floatingElement) {
197
- return;
198
- }
199
- const cleanup = autoUpdate(anchorRef.current, floatingElement, update);
200
- return cleanup;
201
- }, [isOpen, isInline]);
202
180
  // Extract combobox props logic to avoid duplication
203
181
  const getComboboxProps = useMemo(() => {
204
182
  const childWithRef = children;
@@ -363,7 +341,7 @@ function Dropdown(props) {
363
341
  }, [isInline, isOpen, setOpen]);
364
342
  return (jsxs("div", { id: id, ref: containerRef, className: cx(dropdownClasses.root, dropdownClasses.inputPosition(inputPosition)), children: [isInline && (jsxs(TransitionGroup, { component: null, children: [!isOpen && inlineTriggerElement && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-trigger", in: true },
365
343
  jsx("div", { children: inlineTriggerElement }))), isOpen && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-list", in: true },
366
- jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, className: dropdownClasses.popperWithPortal, controllerRef: popperControllerRef, open: isOpen, disablePortal: disablePortal, options: {
344
+ jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, className: dropdownClasses.popperWithPortal, controllerRef: popperControllerRef, open: isOpen, disablePortal: !globalPortal, options: {
367
345
  placement: popoverPlacement,
368
346
  middleware: [
369
347
  offsetMiddleware,