@mezzanine-ui/react 1.0.0-rc.5 → 1.0.0-rc.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 +36 -0
- package/Accordion/Accordion.js +37 -1
- package/AlertBanner/AlertBanner.d.ts +30 -1
- package/AlertBanner/AlertBanner.js +56 -12
- package/AutoComplete/AutoComplete.d.ts +75 -11
- package/AutoComplete/AutoComplete.js +145 -25
- package/AutoComplete/AutoCompleteInside.d.ts +54 -0
- package/AutoComplete/AutoCompleteInside.js +17 -0
- package/AutoComplete/useAutoCompleteKeyboard.d.ts +2 -1
- package/AutoComplete/useAutoCompleteKeyboard.js +4 -1
- package/Backdrop/Backdrop.d.ts +33 -1
- package/Backdrop/Backdrop.js +33 -1
- package/Badge/Badge.d.ts +26 -1
- package/Badge/Badge.js +26 -1
- package/Badge/typings.d.ts +15 -0
- package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
- package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
- package/Breadcrumb/typings.d.ts +1 -1
- package/Button/Button.d.ts +26 -1
- package/Button/Button.js +33 -5
- package/COMPONENTS.md +267 -0
- package/Calendar/useCalendarControls.d.ts +18 -0
- package/Calendar/useCalendarControls.js +18 -0
- package/Card/FourThumbnailCard.js +1 -1
- package/Card/SingleThumbnailCard.js +1 -1
- package/Checkbox/Checkbox.d.ts +30 -1
- package/Checkbox/Checkbox.js +54 -4
- package/Checkbox/CheckboxGroup.d.ts +39 -1
- package/Checkbox/CheckboxGroup.js +49 -3
- package/Cropper/Cropper.d.ts +2 -2
- package/Cropper/Cropper.js +2 -3
- package/Cropper/CropperElement.js +1 -1
- package/Cropper/utils/cropper-calculations.d.ts +1 -1
- package/Cropper/utils/cropper-calculations.js +16 -14
- package/DatePicker/DatePicker.d.ts +36 -2
- package/DatePicker/DatePicker.js +36 -2
- package/DateRangePicker/useDateRangePickerValue.d.ts +24 -0
- package/DateRangePicker/useDateRangePickerValue.js +24 -0
- package/Description/Description.d.ts +1 -1
- package/Description/Description.js +1 -1
- package/Description/DescriptionTitle.d.ts +6 -1
- package/Description/DescriptionTitle.js +2 -2
- package/Drawer/Drawer.d.ts +78 -33
- package/Drawer/Drawer.js +72 -34
- package/Dropdown/Dropdown.d.ts +63 -1
- package/Dropdown/Dropdown.js +203 -9
- package/Dropdown/DropdownItem.d.ts +26 -2
- package/Dropdown/DropdownItem.js +91 -43
- package/Dropdown/DropdownItemCard.d.ts +3 -2
- package/Dropdown/DropdownItemCard.js +8 -5
- package/Dropdown/DropdownStatus.d.ts +2 -2
- package/Dropdown/DropdownStatus.js +3 -2
- package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
- package/Dropdown/dropdownKeydownHandler.js +14 -7
- package/Empty/icons/EmptyMainInitialDataIcon.js +1 -1
- package/Empty/icons/EmptyMainNotificationIcon.js +1 -1
- package/Empty/icons/EmptyMainResultIcon.js +1 -1
- package/Empty/icons/EmptyMainSystemIcon.js +1 -1
- package/FilterArea/Filter.d.ts +25 -2
- package/FilterArea/Filter.js +43 -2
- package/FilterArea/FilterArea.d.ts +43 -4
- package/FilterArea/FilterArea.js +39 -4
- package/FilterArea/FilterAreaContext.d.ts +6 -0
- package/FilterArea/FilterAreaContext.js +6 -0
- package/FilterArea/FilterLine.d.ts +19 -0
- package/FilterArea/FilterLine.js +19 -0
- package/FilterArea/index.d.ts +2 -2
- package/FilterArea/index.js +1 -1
- package/Form/FormField.d.ts +43 -2
- package/Form/FormField.js +40 -3
- package/Form/typings.d.ts +2 -0
- package/Form/useAutoCompleteValueControl.d.ts +20 -0
- package/Form/useAutoCompleteValueControl.js +20 -0
- package/Form/useCheckboxControlValue.d.ts +19 -0
- package/Form/useCheckboxControlValue.js +19 -0
- package/Form/useControlValueState.d.ts +16 -0
- package/Form/useControlValueState.js +16 -0
- package/Form/useCustomControlValue.d.ts +17 -0
- package/Form/useCustomControlValue.js +17 -0
- package/Form/useInputControlValue.d.ts +19 -0
- package/Form/useInputControlValue.js +19 -0
- package/Form/useInputWithClearControlValue.d.ts +20 -0
- package/Form/useInputWithClearControlValue.js +20 -0
- package/Form/useRadioControlValue.d.ts +19 -0
- package/Form/useRadioControlValue.js +19 -0
- package/Form/useSelectValueControl.d.ts +19 -0
- package/Form/useSelectValueControl.js +19 -0
- package/Form/useSwitchControlValue.d.ts +18 -0
- package/Form/useSwitchControlValue.js +18 -0
- package/Icon/Icon.d.ts +23 -1
- package/Icon/Icon.js +23 -1
- package/Input/Input.d.ts +31 -1
- package/Input/Input.js +31 -1
- package/Input/SpinnerButton/SpinnerButton.js +1 -1
- package/Message/Message.d.ts +56 -6
- package/Message/Message.js +59 -52
- package/Modal/Modal.d.ts +76 -4
- package/Modal/Modal.js +117 -6
- package/Modal/ModalBodyForVerification.js +3 -1
- package/Modal/ModalFooter.d.ts +21 -6
- package/Modal/ModalHeader.d.ts +50 -11
- package/Modal/ModalHeader.js +1 -1
- package/Modal/index.d.ts +1 -1
- package/Navigation/Navigation.js +20 -11
- package/Navigation/NavigationHeader.js +2 -1
- package/Navigation/NavigationIconButton.d.ts +8 -0
- package/Navigation/NavigationIconButton.js +2 -2
- package/Navigation/NavigationOption.js +6 -3
- package/Navigation/NavigationOverflowMenu.js +9 -6
- package/Navigation/NavigationUserMenu.js +3 -1
- package/Navigation/context.d.ts +1 -0
- package/NotificationCenter/NotificationCenter.d.ts +59 -9
- package/NotificationCenter/NotificationCenter.js +150 -54
- package/NotificationCenter/NotificationCenterDrawer.d.ts +56 -6
- package/NotificationCenter/NotificationCenterDrawer.js +6 -8
- package/NotificationCenter/index.d.ts +1 -1
- package/OverflowTooltip/OverflowTooltip.js +46 -5
- package/PageFooter/PageFooter.d.ts +1 -1
- package/PageFooter/PageFooter.js +8 -16
- package/Pagination/Pagination.d.ts +38 -1
- package/Pagination/Pagination.js +38 -1
- package/Pagination/PaginationPageSize.js +1 -1
- package/Pagination/usePagination.d.ts +20 -0
- package/Pagination/usePagination.js +20 -0
- package/Progress/Progress.d.ts +24 -1
- package/Progress/Progress.js +24 -1
- package/README.md +82 -0
- package/Radio/Radio.d.ts +35 -1
- package/Radio/Radio.js +50 -12
- package/Radio/RadioGroup.d.ts +2 -2
- package/Radio/RadioGroupContext.d.ts +3 -2
- package/Section/SectionGroup.d.ts +20 -0
- package/Section/SectionGroup.js +16 -0
- package/Section/index.d.ts +2 -0
- package/Section/index.js +1 -0
- package/Select/Select.d.ts +55 -1
- package/Select/Select.js +37 -2
- package/Select/typings.d.ts +9 -0
- package/{Selection/Selection.d.ts → SelectionCard/SelectionCard.d.ts} +23 -11
- package/{Selection/Selection.js → SelectionCard/SelectionCard.js} +14 -14
- package/SelectionCard/SelectionCardGroup.d.ts +28 -0
- package/SelectionCard/index.d.ts +12 -0
- package/SelectionCard/index.js +5 -0
- package/Separator/Separator.d.ts +19 -1
- package/Separator/Separator.js +19 -1
- package/Slider/Slider.d.ts +11 -0
- package/Slider/useSlider.d.ts +21 -0
- package/Spin/Spin.d.ts +14 -5
- package/Spin/Spin.js +6 -8
- package/Stepper/Stepper.d.ts +34 -1
- package/Stepper/Stepper.js +34 -1
- package/Stepper/useStepper.d.ts +18 -0
- package/Stepper/useStepper.js +18 -0
- package/Tab/Tab.d.ts +33 -1
- package/Tab/Tab.js +33 -1
- package/Table/Table.d.ts +51 -3
- package/Table/Table.js +52 -4
- package/Table/hooks/useTableDataSource.d.ts +4 -4
- package/Table/hooks/useTableDataSource.js +1 -1
- package/Tag/Tag.d.ts +27 -1
- package/Tag/Tag.js +27 -1
- package/Tag/typings.d.ts +28 -0
- package/Textarea/Textarea.d.ts +28 -1
- package/Textarea/Textarea.js +25 -1
- package/TimePicker/TimePicker.js +1 -1
- package/TimeRangePicker/TimeRangePicker.js +1 -1
- package/Toggle/Toggle.d.ts +34 -1
- package/Toggle/Toggle.js +34 -1
- package/Tooltip/Tooltip.d.ts +39 -1
- package/Tooltip/Tooltip.js +39 -1
- package/Tooltip/useDelayMouseEnterLeave.d.ts +17 -0
- package/Tooltip/useDelayMouseEnterLeave.js +17 -0
- package/Typography/Typography.d.ts +23 -1
- package/Typography/Typography.js +23 -1
- package/Upload/Upload.d.ts +13 -7
- package/Upload/Upload.js +55 -20
- package/Upload/UploadItem.d.ts +3 -3
- package/Upload/UploadItem.js +11 -8
- package/Upload/UploadPictureCard.d.ts +5 -0
- package/Upload/UploadPictureCard.js +10 -6
- package/Upload/Uploader.d.ts +32 -31
- package/Upload/Uploader.js +10 -9
- package/hooks/useClickAway.d.ts +16 -0
- package/hooks/useClickAway.js +16 -0
- package/hooks/useComposeRefs.d.ts +15 -0
- package/hooks/useComposeRefs.js +15 -0
- package/hooks/useDocumentEscapeKeyDown.d.ts +15 -0
- package/hooks/useDocumentEscapeKeyDown.js +15 -0
- package/hooks/useDocumentEvents.d.ts +16 -0
- package/hooks/useDocumentEvents.js +16 -0
- package/hooks/useDocumentTabKeyDown.d.ts +15 -0
- package/hooks/useDocumentTabKeyDown.js +15 -0
- package/hooks/useIsomorphicLayoutEffect.d.ts +16 -0
- package/hooks/useIsomorphicLayoutEffect.js +16 -0
- package/hooks/useLastCallback.d.ts +15 -0
- package/hooks/useLastCallback.js +15 -0
- package/hooks/useLastValue.d.ts +18 -0
- package/hooks/useLastValue.js +18 -0
- package/hooks/usePreviousValue.d.ts +14 -0
- package/hooks/usePreviousValue.js +14 -0
- package/hooks/useScrollLock.d.ts +6 -4
- package/hooks/useScrollLock.js +6 -4
- package/hooks/useTopStack.d.ts +17 -0
- package/hooks/useTopStack.js +17 -0
- package/hooks/useWindowWidth.d.ts +14 -0
- package/hooks/useWindowWidth.js +14 -0
- package/index.d.ts +6 -4
- package/index.js +3 -2
- package/llms.txt +200 -0
- package/package.json +5 -4
- package/Selection/SelectionGroup.d.ts +0 -28
- package/Selection/index.d.ts +0 -12
- package/Selection/index.js +0 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useRef, useCallback, createElement, useMemo } from 'react';
|
|
1
|
+
import { useState, use, useRef, useCallback, createElement, useMemo } from 'react';
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { navigationOverflowMenuClasses } from '@mezzanine-ui/core/navigation';
|
|
4
4
|
import NavigationIconButton from './NavigationIconButton.js';
|
|
@@ -6,13 +6,16 @@ import { DotHorizontalIcon } from '@mezzanine-ui/icons';
|
|
|
6
6
|
import NavigationOverflowMenuOption from './NavigationOverflowMenuOption.js';
|
|
7
7
|
import { TransitionGroup } from 'react-transition-group';
|
|
8
8
|
import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
|
|
9
|
-
import { NavigationOptionLevelContext } from './context.js';
|
|
9
|
+
import { NavigationActivatedContext, NavigationOptionLevelContext } from './context.js';
|
|
10
10
|
import { useDocumentEvents } from '../hooks/useDocumentEvents.js';
|
|
11
11
|
import Popper from '../Popper/Popper.js';
|
|
12
12
|
import Translate from '../Transition/Translate.js';
|
|
13
|
+
import Scrollbar from '../Scrollbar/Scrollbar.js';
|
|
13
14
|
|
|
14
15
|
const NavigationOverflowMenu = ({ items, }) => {
|
|
15
16
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
17
|
+
const { activatedPath, collapsedHiddenKeys } = use(NavigationActivatedContext);
|
|
18
|
+
const isActive = activatedPath.length > 0 && collapsedHiddenKeys.has(activatedPath[0]);
|
|
16
19
|
const targetRef = useRef(null);
|
|
17
20
|
const popperRef = useRef(null);
|
|
18
21
|
const [level1path, setLevel1path] = useState([]);
|
|
@@ -75,16 +78,16 @@ const NavigationOverflowMenu = ({ items, }) => {
|
|
|
75
78
|
touchend: handleClickAway,
|
|
76
79
|
};
|
|
77
80
|
}, [menuOpen]);
|
|
78
|
-
return (jsxs(Fragment, { children: [jsx(NavigationIconButton, { ref: targetRef, icon: DotHorizontalIcon, onClick: () => setMenuOpen(!menuOpen) }), jsx(Popper, { anchor: targetRef.current, disablePortal: true, open: menuOpen, options: {
|
|
81
|
+
return (jsxs(Fragment, { children: [jsx(NavigationIconButton, { active: isActive, ref: targetRef, icon: DotHorizontalIcon, onClick: () => setMenuOpen(!menuOpen) }), jsx(Popper, { anchor: targetRef.current, disablePortal: true, open: menuOpen, options: {
|
|
79
82
|
placement: 'right-end',
|
|
80
83
|
}, ref: popperRef, children: jsx(TransitionGroup, { component: null, children: menuOpen && (createElement(Translate, { ...translateProps, from: 'bottom', key: "popper-list", in: true },
|
|
81
|
-
jsx("li", { className: navigationOverflowMenuClasses.host, children: jsxs("span", { className: navigationOverflowMenuClasses.content, "aria-haspopup": "true", "aria-expanded": menuOpen, children: [jsx(
|
|
84
|
+
jsx("li", { className: navigationOverflowMenuClasses.host, children: jsxs("span", { className: navigationOverflowMenuClasses.content, "aria-haspopup": "true", "aria-expanded": menuOpen, children: [jsx(Scrollbar, { className: navigationOverflowMenuClasses.subMenu, children: jsx("ul", { children: renderedItems }) }), jsx(NavigationOptionLevelContext.Provider, { value: {
|
|
82
85
|
level: 1,
|
|
83
86
|
path: level1path,
|
|
84
|
-
}, children: level2Items.length > 0 && (jsx(
|
|
87
|
+
}, children: level2Items.length > 0 && (jsx(Scrollbar, { className: navigationOverflowMenuClasses.subMenu, children: jsx("ul", { children: renderedItems2 }) })) }), jsx(NavigationOptionLevelContext.Provider, { value: {
|
|
85
88
|
level: 2,
|
|
86
89
|
path: level2path,
|
|
87
|
-
}, children: level3Items.length > 0 && (jsx(
|
|
90
|
+
}, children: level3Items.length > 0 && (jsx(Scrollbar, { className: navigationOverflowMenuClasses.subMenu, children: jsx("ul", { children: renderedItems3 }) })) })] }) }))) }) })] }));
|
|
88
91
|
};
|
|
89
92
|
|
|
90
93
|
export { NavigationOverflowMenu };
|
|
@@ -37,7 +37,9 @@ const NavigationUserMenu = forwardRef((props, ref) => {
|
|
|
37
37
|
}, onClose: () => {
|
|
38
38
|
setOpen(false);
|
|
39
39
|
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
40
|
-
}, children: jsx("button", { className: cx(navigationUserMenuClasses.host, open && navigationUserMenuClasses.open, className), ref: ref, type: "button", children: jsx(Tooltip, { disablePortal: false,
|
|
40
|
+
}, children: jsx("button", { className: cx(navigationUserMenuClasses.host, open && navigationUserMenuClasses.open, className), ref: ref, type: "button", children: jsx(Tooltip, { disablePortal: false, offsetMainAxis: collapsed
|
|
41
|
+
? 8 + 6 /* 6 is the padding of the item */
|
|
42
|
+
: 8 + 8 /* 8 is the padding of the item and avatar */, options: {
|
|
41
43
|
placement: collapsed ? 'right' : 'top',
|
|
42
44
|
}, title: (collapsed || userNameOverflow) && !open ? children : undefined, children: ({ onMouseEnter, onMouseLeave, ref: tooltipRef }) => (jsxs("span", { className: navigationUserMenuClasses.content, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, ref: tooltipRef, children: [jsx("span", { className: navigationUserMenuClasses.avatar, children: imgError || !imgSrc ? (jsx(Icon, { icon: UserIcon })) : (jsx("img", { alt: "User avatar", className: navigationUserMenuClasses.avatar, src: imgSrc, onError: () => setImgError(true) })) }), children && (jsx("span", { className: navigationUserMenuClasses.userName, children: jsx("span", { ref: userNameRef, children: children }) })), jsx(Icon, { className: navigationUserMenuClasses.icon, icon: ChevronDownIcon })] })) }) }) }));
|
|
43
45
|
});
|
package/Navigation/context.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare const NavigationActivatedContext: import("react").Context<{
|
|
|
10
10
|
activatedPath: string[];
|
|
11
11
|
activatedPathKey: string;
|
|
12
12
|
collapsed: boolean;
|
|
13
|
+
collapsedHiddenKeys: Set<string>;
|
|
13
14
|
currentPathname: string | null;
|
|
14
15
|
filterText: string;
|
|
15
16
|
handleCollapseChange: (newCollapsed: boolean) => void;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { FC, Key } from 'react';
|
|
1
|
+
import { FC, Key, PropsWithChildren } from 'react';
|
|
2
2
|
import { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';
|
|
3
3
|
import { NotificationSeverity, NotificationType } from '@mezzanine-ui/core/notification-center';
|
|
4
4
|
import { ButtonProps } from '../Button';
|
|
5
|
-
import {
|
|
6
|
-
import { SlideProps } from '../Transition';
|
|
7
|
-
export interface NotificationConfigProps extends Pick<NotifierConfig, 'duration'>, Pick<SlideProps, '
|
|
5
|
+
import { NotifierConfig, NotifierData } from '../Notifier';
|
|
6
|
+
import { type SlideProps } from '../Transition';
|
|
7
|
+
export interface NotificationConfigProps extends Pick<NotifierConfig, 'duration'>, Pick<SlideProps, 'easing' | 'from' | 'onEnter' | 'onEntered' | 'onExit' | 'onExited'> {
|
|
8
8
|
/**
|
|
9
9
|
* Callback function when "View All" button is clicked.
|
|
10
10
|
* This will be called after closing all notifications.
|
|
@@ -112,13 +112,63 @@ export interface NotificationData extends NotifierData, NotificationConfigProps
|
|
|
112
112
|
*/
|
|
113
113
|
type?: NotificationType;
|
|
114
114
|
}
|
|
115
|
-
|
|
115
|
+
/** Props accepted by NotificationCenter severity shorthand methods such as `NotificationCenter.success`. */
|
|
116
|
+
export type NotificationCenterShorthandProps = Omit<NotificationData, 'severity'>;
|
|
117
|
+
/** Static severity shorthand methods attached to the {@link NotificationCenter} component. */
|
|
118
|
+
export interface NotificationCenterSeverityMethods {
|
|
119
|
+
/** Display an error notification. */
|
|
120
|
+
error: (props?: NotificationCenterShorthandProps) => Key;
|
|
121
|
+
/** Display an informational notification. */
|
|
122
|
+
info: (props?: NotificationCenterShorthandProps) => Key;
|
|
123
|
+
/** Display a success notification. */
|
|
124
|
+
success: (props?: NotificationCenterShorthandProps) => Key;
|
|
125
|
+
/** Display a warning notification. */
|
|
126
|
+
warning: (props?: NotificationCenterShorthandProps) => Key;
|
|
116
127
|
}
|
|
117
128
|
/**
|
|
118
|
-
*
|
|
129
|
+
* 通知中心元件,支援即時提示(`notification`)與抽屜清單(`drawer`)兩種呈現模式。
|
|
119
130
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
131
|
+
* 以 `<NotificationCenter type="notification" />` 宣告式渲染單筆浮動通知;
|
|
132
|
+
* 或使用靜態方法 `NotificationCenter.add()`、`.success()`、`.error()` 等命令式 API
|
|
133
|
+
* 觸發通知,並可透過回傳的 key 呼叫 `remove()` 收回。
|
|
134
|
+
* `type: 'drawer'` 模式下,請搭配 `NotificationCenterDrawer` 以清單方式顯示。
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```tsx
|
|
138
|
+
* import NotificationCenter from '@mezzanine-ui/react/NotificationCenter';
|
|
139
|
+
*
|
|
140
|
+
* // 宣告式:嵌入畫面中的通知
|
|
141
|
+
* <NotificationCenter
|
|
142
|
+
* reference="notify-1"
|
|
143
|
+
* severity="success"
|
|
144
|
+
* title="儲存成功"
|
|
145
|
+
* type="notification"
|
|
146
|
+
* />
|
|
147
|
+
*
|
|
148
|
+
* // 命令式:觸發浮動通知
|
|
149
|
+
* NotificationCenter.success({ title: '儲存成功', description: '資料已更新。' });
|
|
150
|
+
* NotificationCenter.error({ title: '操作失敗', description: '請稍後再試。' });
|
|
151
|
+
*
|
|
152
|
+
* // 手動控制生命週期
|
|
153
|
+
* const key = NotificationCenter.add({ severity: 'info', title: '處理中…', type: 'notification' });
|
|
154
|
+
* NotificationCenter.remove(key);
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @see {@link NotificationData} 所有可用的通知設定項
|
|
158
|
+
* @see {@link NotificationCenterDrawer} 抽屜清單模式的容器元件
|
|
122
159
|
*/
|
|
123
|
-
declare const NotificationCenter:
|
|
160
|
+
declare const NotificationCenter: FC<PropsWithChildren<NotificationData>> & {
|
|
161
|
+
add: (notif: NotificationData & {
|
|
162
|
+
key?: Key;
|
|
163
|
+
}) => Key;
|
|
164
|
+
config: (configs: NotificationConfigProps) => void;
|
|
165
|
+
destroy: VoidFunction;
|
|
166
|
+
remove: (key: Key) => void;
|
|
167
|
+
error: (props?: NotificationCenterShorthandProps) => Key;
|
|
168
|
+
info: (props?: NotificationCenterShorthandProps) => Key;
|
|
169
|
+
success: (props?: NotificationCenterShorthandProps) => Key;
|
|
170
|
+
warning: (props?: NotificationCenterShorthandProps) => Key;
|
|
171
|
+
};
|
|
172
|
+
/** Full type of the {@link NotificationCenter} component including all static notification API methods. */
|
|
173
|
+
export type NotificationCenterType = typeof NotificationCenter;
|
|
124
174
|
export default NotificationCenter;
|
|
@@ -1,21 +1,57 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { useState, useRef, useMemo, useEffect, useCallback, Children, isValidElement, Fragment as Fragment$1 } from 'react';
|
|
3
|
+
import { useState, useRef, useMemo, useEffect, useCallback, cloneElement, Children, isValidElement, Fragment as Fragment$1 } from 'react';
|
|
4
4
|
import { offset, flip } from '@floating-ui/react-dom';
|
|
5
5
|
import { notificationIcons, notificationClasses } from '@mezzanine-ui/core/notification-center';
|
|
6
|
-
import {
|
|
6
|
+
import { DotHorizontalIcon, CloseIcon } from '@mezzanine-ui/icons';
|
|
7
|
+
import { MOTION_DURATION, MOTION_EASING } from '@mezzanine-ui/system/motion';
|
|
7
8
|
import Button from '../Button/Button.js';
|
|
8
9
|
import ButtonGroup from '../Button/ButtonGroup.js';
|
|
10
|
+
import { reflow } from '../Transition/reflow.js';
|
|
11
|
+
import { useSetNodeTransition } from '../Transition/useSetNodeTransition.js';
|
|
9
12
|
import Typography from '../Typography/Typography.js';
|
|
13
|
+
import { useComposeRefs } from '../hooks/useComposeRefs.js';
|
|
10
14
|
import Icon from '../Icon/Icon.js';
|
|
11
15
|
import Popper from '../Popper/Popper.js';
|
|
12
16
|
import Dropdown from '../Dropdown/Dropdown.js';
|
|
13
17
|
import Badge from '../Badge/Badge.js';
|
|
14
|
-
import
|
|
15
|
-
import cx from 'clsx';
|
|
18
|
+
import Transition from '../Transition/Transition.js';
|
|
16
19
|
import { createNotifier } from '../Notifier/createNotifier.js';
|
|
20
|
+
import cx from 'clsx';
|
|
17
21
|
|
|
18
22
|
const DEFAULT_MAX_VISIBLE_NOTIFICATIONS = 3;
|
|
23
|
+
const NOTIFICATION_SLIDE_FADE_DURATION = {
|
|
24
|
+
enter: MOTION_DURATION.slow,
|
|
25
|
+
exit: MOTION_DURATION.moderate,
|
|
26
|
+
};
|
|
27
|
+
const NOTIFICATION_SLIDE_FADE_EASING = {
|
|
28
|
+
enter: MOTION_EASING.standard,
|
|
29
|
+
exit: MOTION_EASING.exit,
|
|
30
|
+
};
|
|
31
|
+
function getNotificationSlideFadeStyle(state, inProp, from) {
|
|
32
|
+
if (state === 'entering' || state === 'entered') {
|
|
33
|
+
return {
|
|
34
|
+
opacity: 1,
|
|
35
|
+
transform: 'translate3d(0, 0, 0)',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const style = state === 'exiting'
|
|
39
|
+
? {
|
|
40
|
+
opacity: 0,
|
|
41
|
+
transform: 'translate3d(0, 0, 0)',
|
|
42
|
+
}
|
|
43
|
+
: {
|
|
44
|
+
opacity: 0,
|
|
45
|
+
transform: {
|
|
46
|
+
top: 'translate3d(0, -100%, 0)',
|
|
47
|
+
right: 'translate3d(100%, 0, 0)',
|
|
48
|
+
}[from],
|
|
49
|
+
};
|
|
50
|
+
if (state === 'exited' && !inProp) {
|
|
51
|
+
style.visibility = 'hidden';
|
|
52
|
+
}
|
|
53
|
+
return style;
|
|
54
|
+
}
|
|
19
55
|
const NotificationCenterContainer = ({ children }) => {
|
|
20
56
|
const notificationItems = useMemo(() => Children.toArray(children), [children]);
|
|
21
57
|
// Helper function to extract NotificationCenter component from Fragment or direct element
|
|
@@ -42,7 +78,7 @@ const NotificationCenterContainer = ({ children }) => {
|
|
|
42
78
|
.map(extractNotificationCenter)
|
|
43
79
|
.find((notification) => notification !== null);
|
|
44
80
|
if (firstNotification) {
|
|
45
|
-
return (_a = firstNotification.props.maxVisibleNotifications) !== null && _a !== void 0 ? _a : DEFAULT_MAX_VISIBLE_NOTIFICATIONS;
|
|
81
|
+
return ((_a = firstNotification.props.maxVisibleNotifications) !== null && _a !== void 0 ? _a : DEFAULT_MAX_VISIBLE_NOTIFICATIONS);
|
|
46
82
|
}
|
|
47
83
|
return DEFAULT_MAX_VISIBLE_NOTIFICATIONS;
|
|
48
84
|
}, [notificationItems]);
|
|
@@ -70,23 +106,36 @@ const NotificationCenterContainer = ({ children }) => {
|
|
|
70
106
|
if (!notificationItems.length) {
|
|
71
107
|
return null;
|
|
72
108
|
}
|
|
73
|
-
return (jsxs(Fragment, { children: [visibleItems, hasOverflow
|
|
74
|
-
? (jsx("div", { className: notificationClasses.viewAllButton, children: jsx(Button, { onClick: handleViewAll, size: "main", variant: "base-secondary", className: notificationClasses.viewAllButtonText, children: viewAllButtonText }) }))
|
|
75
|
-
: null] }));
|
|
109
|
+
return (jsxs(Fragment, { children: [visibleItems, hasOverflow ? (jsx("div", { className: notificationClasses.viewAllButton, children: jsx(Button, { onClick: handleViewAll, size: "main", variant: "base-secondary", className: notificationClasses.viewAllButtonText, children: viewAllButtonText }) })) : null] }));
|
|
76
110
|
};
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* to display a notification globally.
|
|
82
|
-
*/
|
|
83
|
-
const NotificationCenter = ((props) => {
|
|
84
|
-
const { type = 'notification', cancelButtonProps = {}, cancelButtonText = 'Cancel', confirmButtonProps = {}, confirmButtonText = 'Confirm', description, duration, onCancel: onCancelProp, onClose: onCloseProp, onConfirm: onConfirmProp, onExited: onExitedProp, reference, severity = 'info', title, timeStamp = new Date().toLocaleTimeString(), timeStampLocale = 'zh-TW', showBadge, onBadgeClick: onBadgeClickProp, options, onBadgeSelect: onBadgeSelectProp, prependTips, appendTips, ...restTransitionProps } = props;
|
|
111
|
+
/** @internal Underlying React element rendered by the NotificationCenter notifier. */
|
|
112
|
+
const NotificationCenterFC = (props) => {
|
|
113
|
+
var _a;
|
|
114
|
+
const { type = 'notification', cancelButtonProps = {}, cancelButtonText = 'Cancel', confirmButtonProps = {}, confirmButtonText = 'Confirm', description, duration, onCancel: onCancelProp, onClose: onCloseProp, onConfirm: onConfirmProp, onExited: onExitedProp, reference, severity = 'info', title, timeStamp = new Date().toLocaleTimeString(), timeStampLocale = 'zh-TW', showBadge, onBadgeClick: onBadgeClickProp, options, onBadgeSelect: onBadgeSelectProp, prependTips, appendTips, from = 'top', ...restTransitionProps } = props;
|
|
85
115
|
const targetIcon = notificationIcons[severity];
|
|
86
116
|
const [openDropdown, setOpenDropdown] = useState(false);
|
|
87
117
|
const [open, setOpen] = useState(true);
|
|
88
118
|
const [timeStampAnchor, setTimeStampAnchor] = useState(null);
|
|
89
119
|
const timeStampRef = useRef(null);
|
|
120
|
+
const notificationSlideFadeNodeRef = useRef(null);
|
|
121
|
+
const slideFadeResolvedDuration = NOTIFICATION_SLIDE_FADE_DURATION;
|
|
122
|
+
const slideFadeEasing = (_a = restTransitionProps.easing) !== null && _a !== void 0 ? _a : NOTIFICATION_SLIDE_FADE_EASING;
|
|
123
|
+
const slideFadeDelay = 0;
|
|
124
|
+
const [setSlideTransition, resetSlideTransition] = useSetNodeTransition({
|
|
125
|
+
delay: slideFadeDelay,
|
|
126
|
+
duration: slideFadeResolvedDuration,
|
|
127
|
+
easing: slideFadeEasing,
|
|
128
|
+
properties: ['transform'],
|
|
129
|
+
}, undefined);
|
|
130
|
+
const [setFadeTransition, resetFadeTransition] = useSetNodeTransition({
|
|
131
|
+
delay: slideFadeDelay,
|
|
132
|
+
duration: slideFadeResolvedDuration,
|
|
133
|
+
easing: slideFadeEasing,
|
|
134
|
+
properties: ['opacity'],
|
|
135
|
+
}, undefined);
|
|
136
|
+
const notificationSlideFadeComposedRef = useComposeRefs([
|
|
137
|
+
notificationSlideFadeNodeRef,
|
|
138
|
+
]);
|
|
90
139
|
const isToday = useMemo(() => {
|
|
91
140
|
try {
|
|
92
141
|
const timestampDate = new Date(timeStamp);
|
|
@@ -120,7 +169,9 @@ const NotificationCenter = ((props) => {
|
|
|
120
169
|
if (isToday) {
|
|
121
170
|
const diffInMs = timestampDate.getTime() - now;
|
|
122
171
|
const diffInSeconds = Math.round(diffInMs / 1000);
|
|
123
|
-
const rtf = new Intl.RelativeTimeFormat(timeStampLocale, {
|
|
172
|
+
const rtf = new Intl.RelativeTimeFormat(timeStampLocale, {
|
|
173
|
+
numeric: 'always',
|
|
174
|
+
});
|
|
124
175
|
const units = [
|
|
125
176
|
{ unit: 'hour', seconds: 3600 },
|
|
126
177
|
{ unit: 'minute', seconds: 60 },
|
|
@@ -178,7 +229,7 @@ const NotificationCenter = ((props) => {
|
|
|
178
229
|
}, [onBadgeClickProp, setOpenDropdown, options]);
|
|
179
230
|
const onClose = () => {
|
|
180
231
|
setOpen(false);
|
|
181
|
-
if (onCloseProp) {
|
|
232
|
+
if (onCloseProp && reference !== undefined) {
|
|
182
233
|
onCloseProp(reference);
|
|
183
234
|
}
|
|
184
235
|
};
|
|
@@ -198,7 +249,9 @@ const NotificationCenter = ((props) => {
|
|
|
198
249
|
if (onExitedProp) {
|
|
199
250
|
onExitedProp(node);
|
|
200
251
|
}
|
|
201
|
-
|
|
252
|
+
if (reference) {
|
|
253
|
+
NotificationCenter.remove(reference);
|
|
254
|
+
}
|
|
202
255
|
};
|
|
203
256
|
const onSelect = useCallback((option) => {
|
|
204
257
|
if (onBadgeSelectProp) {
|
|
@@ -221,14 +274,9 @@ const NotificationCenter = ((props) => {
|
|
|
221
274
|
};
|
|
222
275
|
const showConfirmButton = Boolean(confirmButtonText && onConfirmProp);
|
|
223
276
|
const showCancelButton = Boolean(cancelButtonText && (onCancelProp || onCloseProp));
|
|
224
|
-
const hideButtons = !(type === 'notification' &&
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
: null, jsxs("div", { className: notificationClasses.body, children: [jsxs("div", { className: notificationClasses.bodyContent, children: [jsx("h4", { className: notificationClasses.title, children: title }), jsx(Typography, { className: notificationClasses.content, children: description })] }), !hideButtons && (jsxs(ButtonGroup, { className: notificationClasses.action, children: [showConfirmButton
|
|
228
|
-
? (jsx(Button, { onClick: onConfirm, size: "minor", ...confirmButtonProps, children: confirmButtonText }))
|
|
229
|
-
: jsx(Fragment, {}), showCancelButton
|
|
230
|
-
? (jsx(Button, { onClick: onCancel || onClose, size: "minor", variant: "base-secondary", ...cancelButtonProps, children: cancelButtonText }))
|
|
231
|
-
: jsx(Fragment, {})] })), type === 'drawer' && (jsxs(Fragment, { children: [isToday && (jsx(Popper, { anchor: timeStampAnchor, open: Boolean(timeStampAnchor), arrow: {
|
|
277
|
+
const hideButtons = !(type === 'notification' &&
|
|
278
|
+
(showConfirmButton || showCancelButton));
|
|
279
|
+
const notificationContent = (jsxs("div", { className: cx(notificationClasses.host, notificationClasses.severity(severity), notificationClasses.type(type)), onMouseEnter: handleNotificationMouseEnter, onMouseLeave: handleNotificationMouseLeave, children: [targetIcon ? (jsx("div", { className: notificationClasses.iconContainer, children: jsx(Icon, { icon: targetIcon, className: notificationClasses.severityIcon, size: 32 }) })) : null, jsxs("div", { className: notificationClasses.body, children: [jsxs("div", { className: notificationClasses.bodyContent, children: [jsx("h4", { className: notificationClasses.title, children: title }), jsx(Typography, { className: notificationClasses.content, children: description })] }), !hideButtons && (jsxs(ButtonGroup, { className: notificationClasses.action, children: [showCancelButton ? (jsx(Button, { onClick: onCancel || onClose, size: "minor", variant: "base-secondary", ...cancelButtonProps, children: cancelButtonText })) : null, showConfirmButton ? (jsx(Button, { onClick: onConfirm, size: "minor", ...confirmButtonProps, children: confirmButtonText })) : null] })), type === 'drawer' && (jsxs(Fragment, { children: [isToday && (jsx(Popper, { anchor: timeStampAnchor, open: Boolean(timeStampAnchor), arrow: {
|
|
232
280
|
className: notificationClasses.timeStampPopperArrow,
|
|
233
281
|
enabled: true,
|
|
234
282
|
padding: 0,
|
|
@@ -236,44 +284,92 @@ const NotificationCenter = ((props) => {
|
|
|
236
284
|
zIndex: 'var(--mzn-z-index-popover)',
|
|
237
285
|
}, options: {
|
|
238
286
|
placement: 'bottom',
|
|
239
|
-
middleware: [
|
|
240
|
-
|
|
241
|
-
flip(),
|
|
242
|
-
],
|
|
243
|
-
}, children: jsx("div", { className: notificationClasses.timeStampPopper, children: jsx(Typography, { className: notificationClasses.timeStampText, children: timeStamp }) }) })), jsx(Typography, { ref: timeStampRef, className: notificationClasses.timeStamp, children: formattedTimeStamp })] }))] }), type === 'drawer'
|
|
244
|
-
? (jsx(Dropdown, { open: openDropdown, onClose: () => setOpenDropdown(false), onVisibilityChange: (open) => setOpenDropdown(open), options: options !== null && options !== void 0 ? options : [], onSelect: onSelect, placement: "bottom-end", zIndex: 'var(--mzn-z-index-popover)', children: jsxs(Button, { variant: "base-ghost", size: "minor", onClick: onClose, children: [showBadge && jsx(Badge, { variant: "dot-error" }), jsx(Icon, { icon: DotVerticalIcon, className: notificationClasses.closeIcon, size: 16, onClick: onBadgeClick })] }) }))
|
|
245
|
-
: (jsx(Icon, { icon: CloseIcon, className: notificationClasses.closeIcon, size: 16, onClick: onClose }))] }));
|
|
287
|
+
middleware: [offset({ mainAxis: 8 }), flip()],
|
|
288
|
+
}, children: jsx("div", { className: notificationClasses.timeStampPopper, children: jsx(Typography, { className: notificationClasses.timeStampText, children: timeStamp }) }) })), jsx(Typography, { className: notificationClasses.timeStamp, ref: timeStampRef, variant: "label-secondary", children: formattedTimeStamp })] }))] }), type === 'drawer' ? (jsx(Dropdown, { open: openDropdown, onClose: () => setOpenDropdown(false), onVisibilityChange: (open) => setOpenDropdown(open), options: options !== null && options !== void 0 ? options : [], onSelect: onSelect, placement: "bottom-end", zIndex: 'var(--mzn-z-index-popover)', children: jsxs(Button, { variant: "base-ghost", onClick: onClose, className: notificationClasses.dotIconButton, children: [showBadge && jsx(Badge, { variant: "dot-error" }), jsx(Icon, { icon: DotHorizontalIcon, className: notificationClasses.closeIcon, size: 16, onClick: onBadgeClick })] }) })) : (jsx(Icon, { icon: CloseIcon, className: notificationClasses.closeIcon, size: 16, onClick: onClose }))] }));
|
|
246
289
|
if (type === 'notification') {
|
|
247
|
-
return (jsx(
|
|
290
|
+
return (jsx(Transition, { ...restTransitionProps, appear: true, duration: slideFadeResolvedDuration, in: open, nodeRef: notificationSlideFadeNodeRef, onEnter: (node, isAppearing) => {
|
|
291
|
+
var _a;
|
|
292
|
+
setSlideTransition(node, 'enter');
|
|
293
|
+
reflow(node);
|
|
294
|
+
(_a = restTransitionProps.onEnter) === null || _a === void 0 ? void 0 : _a.call(restTransitionProps, node, isAppearing);
|
|
295
|
+
}, onEntered: (node, isAppearing) => {
|
|
296
|
+
var _a;
|
|
297
|
+
resetSlideTransition(node);
|
|
298
|
+
(_a = restTransitionProps.onEntered) === null || _a === void 0 ? void 0 : _a.call(restTransitionProps, node, isAppearing);
|
|
299
|
+
}, onExited: (node) => {
|
|
300
|
+
resetFadeTransition(node);
|
|
301
|
+
onExited(node);
|
|
302
|
+
}, onExit: (node) => {
|
|
303
|
+
var _a;
|
|
304
|
+
setFadeTransition(node, 'exit');
|
|
305
|
+
(_a = restTransitionProps.onExit) === null || _a === void 0 ? void 0 : _a.call(restTransitionProps, node);
|
|
306
|
+
}, children: (state) => cloneElement(notificationContent, {
|
|
307
|
+
ref: notificationSlideFadeComposedRef,
|
|
308
|
+
style: {
|
|
309
|
+
...getNotificationSlideFadeStyle(state, open, from),
|
|
310
|
+
...notificationContent.props.style,
|
|
311
|
+
},
|
|
312
|
+
}) }));
|
|
248
313
|
}
|
|
249
|
-
return (jsxs(Fragment, { children: [prependTips && jsx(Typography, { className: notificationClasses.prependTips, children: prependTips }), notificationContent, appendTips && jsx(Typography, { className: notificationClasses.appendTips, children: appendTips })] }));
|
|
250
|
-
}
|
|
251
|
-
const { add: addNotifier, config, destroy, remove } = createNotifier({
|
|
314
|
+
return (jsxs(Fragment, { children: [prependTips && (jsx(Typography, { className: notificationClasses.prependTips, children: prependTips })), notificationContent, appendTips && (jsx(Typography, { className: notificationClasses.appendTips, children: appendTips }))] }));
|
|
315
|
+
};
|
|
316
|
+
const { add: addNotifier, config, destroy, remove, } = createNotifier({
|
|
252
317
|
duration: false,
|
|
253
318
|
render: (notif) => {
|
|
254
319
|
const { key, ...restNotif } = notif;
|
|
255
|
-
return
|
|
320
|
+
return jsx(NotificationCenterFC, { ...restNotif, reference: key }, key);
|
|
256
321
|
},
|
|
257
322
|
renderContainer: (children) => (jsx(NotificationCenterContainer, { children: children })),
|
|
258
323
|
setRoot: (root) => {
|
|
259
324
|
root === null || root === void 0 ? void 0 : root.setAttribute('class', notificationClasses.root);
|
|
260
325
|
},
|
|
261
326
|
});
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
327
|
+
/**
|
|
328
|
+
* 通知中心元件,支援即時提示(`notification`)與抽屜清單(`drawer`)兩種呈現模式。
|
|
329
|
+
*
|
|
330
|
+
* 以 `<NotificationCenter type="notification" />` 宣告式渲染單筆浮動通知;
|
|
331
|
+
* 或使用靜態方法 `NotificationCenter.add()`、`.success()`、`.error()` 等命令式 API
|
|
332
|
+
* 觸發通知,並可透過回傳的 key 呼叫 `remove()` 收回。
|
|
333
|
+
* `type: 'drawer'` 模式下,請搭配 `NotificationCenterDrawer` 以清單方式顯示。
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```tsx
|
|
337
|
+
* import NotificationCenter from '@mezzanine-ui/react/NotificationCenter';
|
|
338
|
+
*
|
|
339
|
+
* // 宣告式:嵌入畫面中的通知
|
|
340
|
+
* <NotificationCenter
|
|
341
|
+
* reference="notify-1"
|
|
342
|
+
* severity="success"
|
|
343
|
+
* title="儲存成功"
|
|
344
|
+
* type="notification"
|
|
345
|
+
* />
|
|
346
|
+
*
|
|
347
|
+
* // 命令式:觸發浮動通知
|
|
348
|
+
* NotificationCenter.success({ title: '儲存成功', description: '資料已更新。' });
|
|
349
|
+
* NotificationCenter.error({ title: '操作失敗', description: '請稍後再試。' });
|
|
350
|
+
*
|
|
351
|
+
* // 手動控制生命週期
|
|
352
|
+
* const key = NotificationCenter.add({ severity: 'info', title: '處理中…', type: 'notification' });
|
|
353
|
+
* NotificationCenter.remove(key);
|
|
354
|
+
* ```
|
|
355
|
+
*
|
|
356
|
+
* @see {@link NotificationData} 所有可用的通知設定項
|
|
357
|
+
* @see {@link NotificationCenterDrawer} 抽屜清單模式的容器元件
|
|
358
|
+
*/
|
|
359
|
+
const NotificationCenter = Object.assign(NotificationCenterFC, {
|
|
360
|
+
add: (notif) => {
|
|
361
|
+
if (notif.type === 'drawer') {
|
|
362
|
+
return 'NOT_SET';
|
|
363
|
+
}
|
|
364
|
+
return addNotifier(notif);
|
|
365
|
+
},
|
|
366
|
+
config,
|
|
367
|
+
destroy,
|
|
368
|
+
remove,
|
|
369
|
+
error: (props) => addNotifier({ ...props, severity: 'error', type: 'notification' }),
|
|
370
|
+
info: (props) => addNotifier({ ...props, severity: 'info', type: 'notification' }),
|
|
371
|
+
success: (props) => addNotifier({ ...props, severity: 'success', type: 'notification' }),
|
|
372
|
+
warning: (props) => addNotifier({ ...props, severity: 'warning', type: 'notification' }),
|
|
277
373
|
});
|
|
278
374
|
|
|
279
375
|
export { NotificationCenter as default };
|
|
@@ -1,13 +1,63 @@
|
|
|
1
1
|
import { type ComponentProps, type Key, type ReactElement } from 'react';
|
|
2
2
|
import { DrawerSize } from '@mezzanine-ui/core/drawer';
|
|
3
|
-
import { type IconDefinition } from '@mezzanine-ui/icons';
|
|
4
3
|
import { type DrawerProps } from '../Drawer';
|
|
5
4
|
import NotificationCenter, { type NotificationData } from './NotificationCenter';
|
|
6
5
|
type NotificationDataForDrawer = NotificationData & {
|
|
7
6
|
key: Key;
|
|
8
7
|
type: 'drawer';
|
|
9
8
|
};
|
|
10
|
-
type NotificationCenterDrawerPropsBase = Pick<DrawerProps, '
|
|
9
|
+
type NotificationCenterDrawerPropsBase = Pick<DrawerProps, 'onClose' | 'open'> & {
|
|
10
|
+
/**
|
|
11
|
+
* Label for the "all" filter option in the filter bar.
|
|
12
|
+
*/
|
|
13
|
+
filterBarAllRadioLabel?: DrawerProps['filterAreaAllRadioLabel'];
|
|
14
|
+
/**
|
|
15
|
+
* Label for the custom action button in the filter bar.
|
|
16
|
+
*/
|
|
17
|
+
filterBarCustomButtonLabel?: DrawerProps['filterAreaCustomButtonLabel'];
|
|
18
|
+
/**
|
|
19
|
+
* Default selected filter value in the filter bar.
|
|
20
|
+
*/
|
|
21
|
+
filterBarDefaultValue?: DrawerProps['filterAreaDefaultValue'];
|
|
22
|
+
/**
|
|
23
|
+
* Callback fired when the custom action button is clicked.
|
|
24
|
+
*/
|
|
25
|
+
filterBarOnCustomButtonClick?: DrawerProps['filterAreaOnCustomButtonClick'];
|
|
26
|
+
/**
|
|
27
|
+
* Callback fired when the filter option changes.
|
|
28
|
+
*/
|
|
29
|
+
filterBarOnRadioChange?: DrawerProps['filterAreaOnRadioChange'];
|
|
30
|
+
/**
|
|
31
|
+
* Label for the "read" filter option in the filter bar.
|
|
32
|
+
*/
|
|
33
|
+
filterBarReadRadioLabel?: DrawerProps['filterAreaReadRadioLabel'];
|
|
34
|
+
/**
|
|
35
|
+
* Whether to show the filter bar.
|
|
36
|
+
*/
|
|
37
|
+
filterBarShow?: DrawerProps['filterAreaShow'];
|
|
38
|
+
/**
|
|
39
|
+
* Whether to show the "unread only" shortcut button.
|
|
40
|
+
*/
|
|
41
|
+
filterBarShowUnreadButton?: DrawerProps['filterAreaShowUnreadButton'];
|
|
42
|
+
/**
|
|
43
|
+
* Label for the "unread" filter option in the filter bar.
|
|
44
|
+
*/
|
|
45
|
+
filterBarUnreadRadioLabel?: DrawerProps['filterAreaUnreadRadioLabel'];
|
|
46
|
+
/**
|
|
47
|
+
* Controlled selected filter value in the filter bar.
|
|
48
|
+
*/
|
|
49
|
+
filterBarValue?: DrawerProps['filterAreaValue'];
|
|
50
|
+
/**
|
|
51
|
+
* Options for the filter bar dropdown.
|
|
52
|
+
* When non-empty, the right-side filter area button is replaced by a Dropdown
|
|
53
|
+
* triggered by a `DotHorizontalIcon` icon button.
|
|
54
|
+
*/
|
|
55
|
+
filterBarOptions?: DrawerProps['filterAreaOptions'];
|
|
56
|
+
/**
|
|
57
|
+
* Callback fired when a filter bar dropdown option is selected.
|
|
58
|
+
* Only used when `filterBarOptions` is non-empty.
|
|
59
|
+
*/
|
|
60
|
+
filterBarOnSelect?: DrawerProps['filterAreaOnSelect'];
|
|
11
61
|
/**
|
|
12
62
|
* The size of the drawer.
|
|
13
63
|
* @default 'narrow'
|
|
@@ -18,14 +68,14 @@ type NotificationCenterDrawerPropsBase = Pick<DrawerProps, 'controlBarAllRadioLa
|
|
|
18
68
|
* @default '更早'
|
|
19
69
|
*/
|
|
20
70
|
earlierLabel?: string;
|
|
21
|
-
/**
|
|
22
|
-
* The icon of the empty notification.
|
|
23
|
-
*/
|
|
24
|
-
emptyNotificationIcon?: IconDefinition;
|
|
25
71
|
/**
|
|
26
72
|
* The title of the empty notification.
|
|
27
73
|
*/
|
|
28
74
|
emptyNotificationTitle?: string;
|
|
75
|
+
/**
|
|
76
|
+
* The description of the empty notification.
|
|
77
|
+
*/
|
|
78
|
+
emptyNotificationDescription?: string;
|
|
29
79
|
/**
|
|
30
80
|
* The label for the "past 7 days" time group.
|
|
31
81
|
* @default '過去七天'
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import { notificationClasses } from '@mezzanine-ui/core/notification-center';
|
|
5
|
-
import { NotificationIcon } from '@mezzanine-ui/icons';
|
|
6
|
-
import Typography from '../Typography/Typography.js';
|
|
7
5
|
import NotificationCenter from './NotificationCenter.js';
|
|
8
6
|
import Drawer from '../Drawer/Drawer.js';
|
|
9
|
-
import
|
|
7
|
+
import Empty from '../Empty/Empty.js';
|
|
10
8
|
|
|
11
9
|
const isValidTime = (timestamp) => {
|
|
12
10
|
if (!timestamp)
|
|
@@ -57,7 +55,7 @@ const getTimeGroup = (timestamp, now) => {
|
|
|
57
55
|
return 'earlier';
|
|
58
56
|
};
|
|
59
57
|
const NotificationCenterDrawer = (props) => {
|
|
60
|
-
const { children,
|
|
58
|
+
const { children, filterBarAllRadioLabel, filterBarCustomButtonLabel, filterBarDefaultValue, filterBarOnCustomButtonClick, filterBarOnRadioChange, filterBarOnSelect, filterBarReadRadioLabel, filterBarShow, filterBarShowUnreadButton, filterBarUnreadRadioLabel, filterBarValue, filterBarOptions, drawerSize = 'narrow', earlierLabel, emptyNotificationTitle = '目前沒有新的通知', emptyNotificationDescription = '當有新的系統通知時,將會顯示在這裡。', notificationList, onClose, open, past7DaysLabel, title, todayLabel, yesterdayLabel, ...restDrawerProps } = props;
|
|
61
59
|
const isEmpty = useMemo(() => {
|
|
62
60
|
if (notificationList) {
|
|
63
61
|
return notificationList.length === 0;
|
|
@@ -69,7 +67,7 @@ const NotificationCenterDrawer = (props) => {
|
|
|
69
67
|
}, [notificationList, children]);
|
|
70
68
|
const renderNotifications = useMemo(() => {
|
|
71
69
|
const renderEmptyNotifications = () => {
|
|
72
|
-
return (
|
|
70
|
+
return (jsx("div", { className: notificationClasses.emptyNotifications, children: jsx(Empty, { type: "notification", size: "main", title: emptyNotificationTitle, description: emptyNotificationDescription }) }));
|
|
73
71
|
};
|
|
74
72
|
if (isEmpty) {
|
|
75
73
|
return renderEmptyNotifications();
|
|
@@ -115,14 +113,14 @@ const NotificationCenterDrawer = (props) => {
|
|
|
115
113
|
isEmpty,
|
|
116
114
|
notificationList,
|
|
117
115
|
children,
|
|
118
|
-
emptyNotificationIcon,
|
|
119
116
|
emptyNotificationTitle,
|
|
117
|
+
emptyNotificationDescription,
|
|
120
118
|
earlierLabel,
|
|
121
119
|
past7DaysLabel,
|
|
122
120
|
todayLabel,
|
|
123
121
|
yesterdayLabel,
|
|
124
122
|
]);
|
|
125
|
-
return (jsx(Drawer, { className: notificationClasses.drawer,
|
|
123
|
+
return (jsx(Drawer, { className: notificationClasses.drawer, filterAreaAllRadioLabel: filterBarAllRadioLabel, filterAreaCustomButtonLabel: filterBarCustomButtonLabel, filterAreaDefaultValue: filterBarDefaultValue, filterAreaIsEmpty: isEmpty, filterAreaOnCustomButtonClick: filterBarOnCustomButtonClick, filterAreaOnRadioChange: filterBarOnRadioChange, filterAreaOnSelect: filterBarOnSelect, filterAreaReadRadioLabel: filterBarReadRadioLabel, filterAreaShow: filterBarShow, filterAreaShowUnreadButton: filterBarShowUnreadButton, filterAreaUnreadRadioLabel: filterBarUnreadRadioLabel, filterAreaValue: filterBarValue, filterAreaOptions: filterBarOptions, headerTitle: title, isHeaderDisplay: Boolean(title), onClose: onClose, open: open, size: drawerSize, ...restDrawerProps, children: jsx("div", { className: notificationClasses.notificationsContainer, children: renderNotifications }) }));
|
|
126
124
|
};
|
|
127
125
|
|
|
128
126
|
export { NotificationCenterDrawer as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';
|
|
2
2
|
export { default } from './NotificationCenter';
|
|
3
|
-
export type { NotificationConfigProps, NotificationData, } from './NotificationCenter';
|
|
3
|
+
export type { NotificationCenterShorthandProps, NotificationCenterSeverityMethods, NotificationCenterType, NotificationConfigProps, NotificationData, } from './NotificationCenter';
|
|
4
4
|
export { default as NotificationCenterDrawer } from './NotificationCenterDrawer';
|
|
5
5
|
export type { NotificationCenterDrawerProps } from './NotificationCenterDrawer';
|