@mezzanine-ui/react 1.0.0-beta.1 → 1.0.0-beta.3

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 (172) hide show
  1. package/Anchor/Anchor.d.ts +51 -18
  2. package/Anchor/Anchor.js +15 -15
  3. package/Anchor/AnchorGroup.d.ts +34 -0
  4. package/Anchor/AnchorGroup.js +37 -0
  5. package/Anchor/AnchorItem.d.ts +30 -0
  6. package/Anchor/AnchorItem.js +65 -0
  7. package/Anchor/index.d.ts +2 -0
  8. package/Anchor/index.js +1 -0
  9. package/Anchor/utils.d.ts +13 -0
  10. package/Anchor/utils.js +95 -0
  11. package/AutoComplete/AutoComplete.d.ts +194 -0
  12. package/AutoComplete/AutoComplete.js +419 -0
  13. package/AutoComplete/index.d.ts +2 -0
  14. package/AutoComplete/index.js +1 -0
  15. package/AutoComplete/useAutoCompleteCreation.d.ts +33 -0
  16. package/AutoComplete/useAutoCompleteCreation.js +201 -0
  17. package/AutoComplete/useAutoCompleteKeyboard.d.ts +31 -0
  18. package/AutoComplete/useAutoCompleteKeyboard.js +149 -0
  19. package/AutoComplete/useAutoCompleteSearch.d.ts +16 -0
  20. package/AutoComplete/useAutoCompleteSearch.js +69 -0
  21. package/AutoComplete/useCreationTracker.d.ts +17 -0
  22. package/AutoComplete/useCreationTracker.js +47 -0
  23. package/Badge/Badge.js +2 -2
  24. package/Breadcrumb/BreadcrumbItem.d.ts +1 -1
  25. package/Button/Button.js +13 -11
  26. package/Button/index.d.ts +1 -1
  27. package/Button/typings.d.ts +27 -4
  28. package/Description/Description.d.ts +30 -0
  29. package/Description/Description.js +13 -0
  30. package/Description/DescriptionContent.d.ts +41 -0
  31. package/Description/DescriptionContent.js +14 -0
  32. package/Description/DescriptionGroup.d.ts +13 -0
  33. package/Description/DescriptionGroup.js +12 -0
  34. package/Description/DescriptionTitle.d.ts +45 -0
  35. package/Description/DescriptionTitle.js +17 -0
  36. package/Description/index.d.ts +8 -0
  37. package/Description/index.js +4 -0
  38. package/Dropdown/Dropdown.d.ts +43 -3
  39. package/Dropdown/Dropdown.js +154 -35
  40. package/Dropdown/DropdownAction.d.ts +1 -1
  41. package/Dropdown/DropdownAction.js +1 -4
  42. package/Dropdown/DropdownItem.d.ts +21 -4
  43. package/Dropdown/DropdownItem.js +23 -10
  44. package/Dropdown/DropdownItemCard.d.ts +5 -5
  45. package/Dropdown/DropdownItemCard.js +11 -10
  46. package/Dropdown/DropdownStatus.d.ts +2 -2
  47. package/Dropdown/DropdownStatus.js +29 -0
  48. package/Dropdown/dropdownKeydownHandler.d.ts +2 -1
  49. package/Dropdown/dropdownKeydownHandler.js +73 -0
  50. package/Dropdown/highlightText.js +5 -1
  51. package/Dropdown/shortcutTextHandler.d.ts +24 -0
  52. package/Dropdown/shortcutTextHandler.js +171 -0
  53. package/Form/FormControlContext.d.ts +2 -2
  54. package/Form/FormField.d.ts +56 -4
  55. package/Form/FormField.js +10 -6
  56. package/Form/FormHintText.d.ts +24 -1
  57. package/Form/FormHintText.js +4 -4
  58. package/Form/FormLabel.d.ts +6 -3
  59. package/Form/FormLabel.js +5 -3
  60. package/Input/Input.d.ts +29 -3
  61. package/Input/Input.js +22 -6
  62. package/Input/PasswordStrengthIndicator/PasswordStrengthIndicator.js +1 -1
  63. package/Modal/Modal.d.ts +103 -11
  64. package/Modal/Modal.js +14 -9
  65. package/Modal/ModalBodyForVerification.d.ts +59 -0
  66. package/Modal/ModalBodyForVerification.js +99 -0
  67. package/Modal/ModalControl.d.ts +2 -2
  68. package/Modal/ModalControl.js +1 -1
  69. package/Modal/ModalFooter.d.ts +119 -1
  70. package/Modal/ModalFooter.js +15 -3
  71. package/Modal/ModalHeader.d.ts +26 -7
  72. package/Modal/ModalHeader.js +33 -7
  73. package/Modal/index.d.ts +4 -5
  74. package/Modal/index.js +1 -2
  75. package/Modal/useModalContainer.d.ts +12 -3
  76. package/Modal/useModalContainer.js +28 -6
  77. package/Navigation/CollapsedMenu.d.ts +6 -0
  78. package/Navigation/CollapsedMenu.js +16 -0
  79. package/Navigation/Navigation.d.ts +17 -3
  80. package/Navigation/Navigation.js +48 -33
  81. package/Navigation/NavigationFooter.js +4 -2
  82. package/Navigation/NavigationHeader.d.ts +11 -1
  83. package/Navigation/NavigationHeader.js +6 -3
  84. package/Navigation/NavigationOption.d.ts +3 -2
  85. package/Navigation/NavigationOption.js +45 -26
  86. package/Navigation/NavigationOptionCategory.js +20 -2
  87. package/Navigation/context.d.ts +2 -0
  88. package/Navigation/useVisibleItems.d.ts +5 -0
  89. package/Navigation/useVisibleItems.js +54 -0
  90. package/NotificationCenter/NotificationCenter.d.ts +124 -0
  91. package/NotificationCenter/NotificationCenter.js +259 -0
  92. package/NotificationCenter/NotificationCenterDrawer.d.ts +89 -0
  93. package/NotificationCenter/index.d.ts +3 -0
  94. package/NotificationCenter/index.js +1 -0
  95. package/PageFooter/PageFooter.d.ts +19 -9
  96. package/PageFooter/PageFooter.js +10 -10
  97. package/PageHeader/PageHeader.js +4 -12
  98. package/PageToolbar/PageToolbar.d.ts +2 -6
  99. package/PageToolbar/utils.js +4 -12
  100. package/Select/index.d.ts +0 -2
  101. package/Select/index.js +0 -1
  102. package/Slider/useSlider.js +1 -1
  103. package/Table/Table.d.ts +53 -15
  104. package/Table/Table.js +178 -82
  105. package/Table/TableContext.d.ts +18 -42
  106. package/Table/components/TableActionsCell.d.ts +26 -0
  107. package/Table/components/TableActionsCell.js +78 -0
  108. package/Table/components/TableBody.d.ts +2 -5
  109. package/Table/components/TableBody.js +16 -19
  110. package/Table/components/TableBulkActions.d.ts +15 -0
  111. package/Table/components/TableBulkActions.js +26 -0
  112. package/Table/components/TableCell.d.ts +2 -0
  113. package/Table/components/TableCell.js +42 -10
  114. package/Table/components/TableColGroup.js +10 -112
  115. package/Table/components/TableColumnTitleMenu.d.ts +6 -0
  116. package/Table/components/TableColumnTitleMenu.js +20 -0
  117. package/Table/components/TableDragHandleCell.d.ts +2 -0
  118. package/Table/components/TableDragHandleCell.js +8 -1
  119. package/Table/components/TableExpandCell.d.ts +2 -0
  120. package/Table/components/TableExpandCell.js +8 -1
  121. package/Table/components/TableExpandedRow.js +3 -2
  122. package/Table/components/TableHeader.d.ts +2 -4
  123. package/Table/components/TableHeader.js +11 -14
  124. package/Table/components/TableResizeHandle.js +3 -7
  125. package/Table/components/TableRow.js +54 -20
  126. package/Table/components/TableSelectionCell.d.ts +5 -0
  127. package/Table/components/TableSelectionCell.js +12 -1
  128. package/Table/components/index.d.ts +1 -0
  129. package/Table/components/index.js +1 -0
  130. package/Table/hooks/index.d.ts +1 -1
  131. package/Table/hooks/index.js +1 -1
  132. package/Table/hooks/useTableDataSource.d.ts +2 -2
  133. package/Table/hooks/useTableExpansion.js +0 -6
  134. package/Table/hooks/useTableFixedOffsets.d.ts +1 -1
  135. package/Table/hooks/useTableFixedOffsets.js +24 -26
  136. package/Table/hooks/useTableResizedColumns.d.ts +2 -0
  137. package/Table/hooks/useTableResizedColumns.js +22 -0
  138. package/Table/hooks/useTableScroll.d.ts +3 -1
  139. package/Table/hooks/useTableScroll.js +25 -19
  140. package/Table/hooks/useTableSelection.js +32 -8
  141. package/Table/hooks/useTableVirtualization.d.ts +1 -1
  142. package/Table/index.d.ts +4 -4
  143. package/Table/index.js +5 -3
  144. package/Table/utils/calculateColumnWidths.d.ts +28 -0
  145. package/Table/utils/calculateColumnWidths.js +80 -0
  146. package/Table/utils/index.d.ts +2 -0
  147. package/Table/utils/index.js +1 -0
  148. package/Table/utils/useTableRowSelection.d.ts +5 -5
  149. package/Table/utils/useTableRowSelection.js +14 -6
  150. package/Tag/TagGroup.d.ts +3 -0
  151. package/Tag/index.d.ts +2 -0
  152. package/Tag/index.js +1 -0
  153. package/Upload/UploadPictureCard.js +1 -1
  154. package/index.d.ts +36 -20
  155. package/index.js +26 -7
  156. package/package.json +4 -4
  157. package/utils/format-number-with-commas.d.ts +4 -0
  158. package/utils/format-number-with-commas.js +27 -0
  159. package/utils/parse-number-with-commas.d.ts +4 -0
  160. package/utils/parse-number-with-commas.js +22 -0
  161. package/Modal/ModalActions.d.ts +0 -9
  162. package/Modal/ModalActions.js +0 -20
  163. package/Modal/ModalBody.d.ts +0 -7
  164. package/Modal/ModalBody.js +0 -14
  165. package/Notification/Notification.d.ts +0 -54
  166. package/Notification/Notification.js +0 -76
  167. package/Notification/index.d.ts +0 -3
  168. package/Notification/index.js +0 -1
  169. package/Select/AutoComplete.d.ts +0 -107
  170. package/Select/AutoComplete.js +0 -114
  171. package/Table/hooks/useTableColumns.d.ts +0 -8
  172. package/Table/hooks/useTableColumns.js +0 -91
@@ -0,0 +1,54 @@
1
+ import { useRef, useState, useEffect } from 'react';
2
+
3
+ function useVisibleItems(items, collapsed) {
4
+ const contentRef = useRef(null);
5
+ const [visibleCount, setVisibleCount] = useState(null);
6
+ useEffect(() => {
7
+ const contentEl = contentRef.current;
8
+ if (!contentEl)
9
+ return;
10
+ let timeoutId = null;
11
+ const calculateVisibleItems = () => {
12
+ if (!collapsed) {
13
+ setVisibleCount(null);
14
+ return;
15
+ }
16
+ const contentHeight = contentEl.clientHeight;
17
+ const ul = contentEl.querySelector('ul');
18
+ const option = contentEl.querySelector('.mzn-navigation-option--level-1');
19
+ const optionHeight = (option === null || option === void 0 ? void 0 : option.clientHeight) || 0;
20
+ const optionsGapTightFixed = 4;
21
+ if (optionHeight === 0) {
22
+ setVisibleCount(0);
23
+ return;
24
+ }
25
+ if (!ul)
26
+ return;
27
+ const count = Math.floor((contentHeight + optionsGapTightFixed) /
28
+ (optionHeight + optionsGapTightFixed)) - 1;
29
+ setVisibleCount(count);
30
+ };
31
+ const debouncedCalculate = () => {
32
+ if (timeoutId) {
33
+ clearTimeout(timeoutId);
34
+ }
35
+ timeoutId = setTimeout(() => {
36
+ calculateVisibleItems();
37
+ }, 100);
38
+ };
39
+ const resizeObserver = new ResizeObserver(() => {
40
+ debouncedCalculate();
41
+ });
42
+ resizeObserver.observe(contentEl);
43
+ calculateVisibleItems();
44
+ return () => {
45
+ if (timeoutId) {
46
+ clearTimeout(timeoutId);
47
+ }
48
+ resizeObserver.disconnect();
49
+ };
50
+ }, [collapsed, items]);
51
+ return { contentRef, visibleCount };
52
+ }
53
+
54
+ export { useVisibleItems };
@@ -0,0 +1,124 @@
1
+ import { FC, Key } from 'react';
2
+ import { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';
3
+ import { NotificationSeverity, NotificationType } from '@mezzanine-ui/core/notification-center';
4
+ import { ButtonProps } from '../Button';
5
+ import { Notifier, NotifierConfig, NotifierData } from '../Notifier';
6
+ import { SlideProps } from '../Transition';
7
+ export interface NotificationConfigProps extends Pick<NotifierConfig, 'duration'>, Pick<SlideProps, 'onEnter' | 'onEntered' | 'onExit' | 'onExited' | 'easing'> {
8
+ /**
9
+ * Callback function when "View All" button is clicked.
10
+ * This will be called after closing all notifications.
11
+ */
12
+ onViewAll?: VoidFunction;
13
+ /**
14
+ * The text of the "View All" button.
15
+ * @default '查看更多'
16
+ */
17
+ viewAllButtonText?: string;
18
+ }
19
+ export interface NotificationData extends NotifierData, NotificationConfigProps {
20
+ /**
21
+ * The tips to be appended to the notification.
22
+ * Only displayed when the type is 'drawer'.
23
+ */
24
+ appendTips?: string;
25
+ /**
26
+ * Other props of cancel button.
27
+ */
28
+ cancelButtonProps?: ButtonProps;
29
+ /**
30
+ * Cancel button text.
31
+ */
32
+ cancelButtonText?: string;
33
+ /**
34
+ * Other props of confirm button.
35
+ */
36
+ confirmButtonProps?: ButtonProps;
37
+ /**
38
+ * Confirm button text.
39
+ */
40
+ confirmButtonText?: string;
41
+ /**
42
+ * The description of notification.
43
+ */
44
+ description?: string;
45
+ /**
46
+ * The maximum number of notifications to be displayed.
47
+ * Only displayed when the type is 'notification'.
48
+ * @default 3
49
+ */
50
+ maxVisibleNotifications?: number;
51
+ /**
52
+ * The callback function when the badge is clicked.
53
+ * Only displayed when the type is 'drawer'.
54
+ */
55
+ onBadgeClick?: VoidFunction;
56
+ /**
57
+ * The callback function when the badge is selected.
58
+ * Only displayed when the type is 'drawer'.
59
+ */
60
+ onBadgeSelect?: (option: DropdownOption) => void;
61
+ /**
62
+ * Cancel button click event handler. <br />
63
+ * If not provided, the event handler will fallback to a close function using `NotificationCenter.remove`.
64
+ */
65
+ onCancel?: VoidFunction;
66
+ /**
67
+ * Confirm button click event handler. <br />
68
+ * If given, will render action button group.
69
+ */
70
+ onConfirm?: VoidFunction;
71
+ /**
72
+ * The options of the badge.
73
+ * Only displayed when the type is 'drawer'.
74
+ */
75
+ options?: DropdownOption[];
76
+ /**
77
+ * The tips to be prepended to the notification.
78
+ * Only displayed when the type is 'drawer'.
79
+ */
80
+ prependTips?: string;
81
+ /**
82
+ * The identifier of the notification.
83
+ */
84
+ reference?: Key;
85
+ /**
86
+ * The severity of the message.
87
+ * @default info
88
+ */
89
+ severity?: NotificationSeverity;
90
+ /**
91
+ * The props of the badge.
92
+ * Only displayed when the type is 'drawer'.
93
+ */
94
+ showBadge?: boolean;
95
+ /**
96
+ * The time stamp of notification on the drawer list.
97
+ * @default new Date().toLocaleTimeString()
98
+ */
99
+ timeStamp?: string;
100
+ /**
101
+ * The locale of the time stamp.
102
+ * @default 'zh-TW'
103
+ */
104
+ timeStampLocale?: string;
105
+ /**
106
+ * The title of notification.
107
+ */
108
+ title?: string;
109
+ /**
110
+ * The type of notification.
111
+ * @default 'notification'
112
+ */
113
+ type?: NotificationType;
114
+ }
115
+ export interface NotificationCenter extends FC<NotificationData>, Notifier<NotificationData, NotificationConfigProps>, Record<NotificationSeverity, (props?: Omit<NotificationData, 'severity'>) => Key> {
116
+ }
117
+ /**
118
+ * The react component for `mezzanine` notification center.
119
+ *
120
+ * Use the API from the NotificationCenter instance such as `NotificationCenter.success` and `NotificationCenter.error`
121
+ * to display a notification globally.
122
+ */
123
+ declare const NotificationCenter: NotificationCenter;
124
+ export default NotificationCenter;
@@ -0,0 +1,259 @@
1
+ 'use client';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { useState, useRef, useMemo, useEffect, useCallback, Children, isValidElement, Fragment as Fragment$1 } from 'react';
4
+ import { offset, flip } from '@floating-ui/react-dom';
5
+ import { notificationIcons, notificationClasses } from '@mezzanine-ui/core/notification-center';
6
+ import { DotVerticalIcon, CloseIcon } from '@mezzanine-ui/icons';
7
+ import Button from '../Button/Button.js';
8
+ import ButtonGroup from '../Button/ButtonGroup.js';
9
+ import Typography from '../Typography/Typography.js';
10
+ import Icon from '../Icon/Icon.js';
11
+ import Popper from '../Popper/Popper.js';
12
+ import Dropdown from '../Dropdown/Dropdown.js';
13
+ import Badge from '../Badge/Badge.js';
14
+ import Slide from '../Transition/Slide.js';
15
+ import cx from 'clsx';
16
+ import { createNotifier } from '../Notifier/createNotifier.js';
17
+
18
+ const DEFAULT_MAX_VISIBLE_NOTIFICATIONS = 3;
19
+ const NotificationCenterContainer = ({ children }) => {
20
+ const notificationItems = useMemo(() => Children.toArray(children), [children]);
21
+ // Helper function to extract NotificationCenter component from Fragment or direct element
22
+ const extractNotificationCenter = (child) => {
23
+ if (!isValidElement(child)) {
24
+ return null;
25
+ }
26
+ // If it's a Fragment, check its children
27
+ if (child.type === Fragment$1) {
28
+ const fragmentProps = child.props;
29
+ const fragmentChildren = Children.toArray(fragmentProps.children);
30
+ const notificationCenter = fragmentChildren.find((fragmentChild) => isValidElement(fragmentChild));
31
+ return notificationCenter !== null && notificationCenter !== void 0 ? notificationCenter : null;
32
+ }
33
+ // If it's directly a NotificationCenter component
34
+ if (isValidElement(child)) {
35
+ return child;
36
+ }
37
+ return null;
38
+ };
39
+ const maxVisibleNotifications = useMemo(() => {
40
+ var _a;
41
+ const firstNotification = notificationItems
42
+ .map(extractNotificationCenter)
43
+ .find((notification) => notification !== null);
44
+ if (firstNotification) {
45
+ return (_a = firstNotification.props.maxVisibleNotifications) !== null && _a !== void 0 ? _a : DEFAULT_MAX_VISIBLE_NOTIFICATIONS;
46
+ }
47
+ return DEFAULT_MAX_VISIBLE_NOTIFICATIONS;
48
+ }, [notificationItems]);
49
+ const onViewAll = useMemo(() => {
50
+ const firstNotification = notificationItems
51
+ .map(extractNotificationCenter)
52
+ .find((notification) => notification !== null);
53
+ return firstNotification === null || firstNotification === void 0 ? void 0 : firstNotification.props.onViewAll;
54
+ }, [notificationItems]);
55
+ const viewAllButtonText = useMemo(() => {
56
+ var _a;
57
+ const firstNotification = notificationItems
58
+ .map(extractNotificationCenter)
59
+ .find((notification) => notification !== null);
60
+ return (_a = firstNotification === null || firstNotification === void 0 ? void 0 : firstNotification.props.viewAllButtonText) !== null && _a !== void 0 ? _a : '查看更多';
61
+ }, [notificationItems]);
62
+ const hasOverflow = notificationItems.length > maxVisibleNotifications;
63
+ const visibleItems = useMemo(() => notificationItems.slice(0, maxVisibleNotifications), [notificationItems, maxVisibleNotifications]);
64
+ const handleViewAll = () => {
65
+ NotificationCenter.destroy();
66
+ if (onViewAll) {
67
+ onViewAll();
68
+ }
69
+ };
70
+ if (!notificationItems.length) {
71
+ return null;
72
+ }
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] }));
76
+ };
77
+ /**
78
+ * The react component for `mezzanine` notification center.
79
+ *
80
+ * Use the API from the NotificationCenter instance such as `NotificationCenter.success` and `NotificationCenter.error`
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;
85
+ const targetIcon = notificationIcons[severity];
86
+ const [openDropdown, setOpenDropdown] = useState(false);
87
+ const [open, setOpen] = useState(true);
88
+ const [timeStampAnchor, setTimeStampAnchor] = useState(null);
89
+ const timeStampRef = useRef(null);
90
+ const formattedTimeStamp = useMemo(() => {
91
+ try {
92
+ const timestampDate = new Date(timeStamp);
93
+ // Check if the time stamp is a valid date
94
+ if (isNaN(timestampDate.getTime())) {
95
+ return timeStamp;
96
+ }
97
+ const now = Date.now();
98
+ const diffInMs = timestampDate.getTime() - now;
99
+ const diffInSeconds = Math.round(diffInMs / 1000);
100
+ const diffInDays = Math.round(diffInSeconds / 86400);
101
+ if (Math.abs(diffInDays) <= 7) {
102
+ const rtf = new Intl.RelativeTimeFormat(timeStampLocale, { numeric: 'always' });
103
+ const units = [
104
+ { unit: 'day', seconds: 86400 },
105
+ { unit: 'hour', seconds: 3600 },
106
+ { unit: 'minute', seconds: 60 },
107
+ ];
108
+ for (const { unit, seconds } of units) {
109
+ const value = Math.round(diffInSeconds / seconds);
110
+ if (Math.abs(value) >= 1) {
111
+ return rtf.format(value, unit);
112
+ }
113
+ }
114
+ return 'now';
115
+ }
116
+ const hasTimeComponent = /:\d{2}/.test(timeStamp) || timeStamp.includes('T');
117
+ if (hasTimeComponent) {
118
+ const dateFormatter = new Intl.DateTimeFormat(timeStampLocale, {
119
+ year: 'numeric',
120
+ month: '2-digit',
121
+ day: '2-digit',
122
+ hour: '2-digit',
123
+ minute: '2-digit',
124
+ hour12: false,
125
+ });
126
+ return dateFormatter.format(timestampDate).replace(/\//g, '-');
127
+ }
128
+ else {
129
+ const dateFormatter = new Intl.DateTimeFormat(timeStampLocale, {
130
+ year: 'numeric',
131
+ month: '2-digit',
132
+ day: '2-digit',
133
+ });
134
+ return dateFormatter.format(timestampDate).replace(/\//g, '-');
135
+ }
136
+ }
137
+ catch (_a) {
138
+ return timeStamp;
139
+ }
140
+ }, [timeStamp, timeStampLocale]);
141
+ useEffect(() => {
142
+ if (open && duration) {
143
+ const timer = window.setTimeout(() => {
144
+ setOpen(false);
145
+ }, duration);
146
+ return () => {
147
+ window.clearTimeout(timer);
148
+ };
149
+ }
150
+ }, [open, duration]);
151
+ const onBadgeClick = useCallback(() => {
152
+ if (onBadgeClickProp) {
153
+ onBadgeClickProp();
154
+ }
155
+ if (options && options.length > 0) {
156
+ setOpenDropdown(true);
157
+ }
158
+ }, [onBadgeClickProp, setOpenDropdown, options]);
159
+ const onClose = () => {
160
+ setOpen(false);
161
+ if (onCloseProp) {
162
+ onCloseProp(reference);
163
+ }
164
+ };
165
+ const onConfirm = onConfirmProp
166
+ ? () => {
167
+ setOpen(false);
168
+ onConfirmProp();
169
+ }
170
+ : undefined;
171
+ const onCancel = onCancelProp
172
+ ? () => {
173
+ setOpen(false);
174
+ onCancelProp();
175
+ }
176
+ : undefined;
177
+ const onExited = (node) => {
178
+ if (onExitedProp) {
179
+ onExitedProp(node);
180
+ }
181
+ NotificationCenter.remove(reference);
182
+ };
183
+ const onSelect = useCallback((option) => {
184
+ if (onBadgeSelectProp) {
185
+ onBadgeSelectProp(option);
186
+ }
187
+ }, [onBadgeSelectProp]);
188
+ const handleNotificationMouseEnter = () => {
189
+ if (type === 'drawer') {
190
+ setTimeout(() => {
191
+ if (timeStampRef.current) {
192
+ setTimeStampAnchor(timeStampRef.current);
193
+ }
194
+ }, 0);
195
+ }
196
+ };
197
+ const handleNotificationMouseLeave = () => {
198
+ if (type === 'drawer') {
199
+ setTimeStampAnchor(null);
200
+ }
201
+ };
202
+ const showConfirmButton = Boolean(confirmButtonText && onConfirmProp);
203
+ const showCancelButton = Boolean(cancelButtonText && (onCancelProp || onCloseProp));
204
+ const hideButtons = !(type === 'notification' && (showConfirmButton || showCancelButton));
205
+ const notificationContent = (jsxs("div", { className: cx(notificationClasses.host, notificationClasses.severity(severity), notificationClasses.type(type)), onMouseEnter: handleNotificationMouseEnter, onMouseLeave: handleNotificationMouseLeave, children: [targetIcon
206
+ ? (jsx("div", { className: notificationClasses.iconContainer, children: jsx(Icon, { icon: targetIcon, className: notificationClasses.severityIcon }) }))
207
+ : 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
208
+ ? (jsx(Button, { onClick: onConfirm, size: "minor", ...confirmButtonProps, children: confirmButtonText }))
209
+ : jsx(Fragment, {}), showCancelButton
210
+ ? (jsx(Button, { onClick: onCancel || onClose, size: "minor", variant: "base-secondary", ...cancelButtonProps, children: cancelButtonText }))
211
+ : jsx(Fragment, {})] })), type === 'drawer' && (jsxs(Fragment, { children: [jsx(Popper, { anchor: timeStampAnchor, open: Boolean(timeStampAnchor), arrow: {
212
+ className: notificationClasses.timeStampPopperArrow,
213
+ enabled: true,
214
+ padding: 0,
215
+ }, style: {
216
+ zIndex: 'var(--mzn-z-index-popover)',
217
+ }, options: {
218
+ placement: 'bottom',
219
+ middleware: [
220
+ offset({ mainAxis: 8 }),
221
+ flip(),
222
+ ],
223
+ }, 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'
224
+ ? (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 })] }) }))
225
+ : (jsx(Icon, { icon: CloseIcon, className: notificationClasses.closeIcon, size: 16, onClick: onClose }))] }));
226
+ if (type === 'notification') {
227
+ return (jsxs(Fragment, { children: [prependTips && jsx(Typography, { className: notificationClasses.prependTips, children: prependTips }), jsx(Slide, { in: open, appear: true, onExited: onExited, ...restTransitionProps, children: notificationContent }), appendTips && jsx(Typography, { className: notificationClasses.appendTips, children: appendTips })] }));
228
+ }
229
+ return (jsxs(Fragment, { children: [prependTips && jsx(Typography, { className: notificationClasses.prependTips, children: prependTips }), notificationContent, appendTips && jsx(Typography, { className: notificationClasses.appendTips, children: appendTips })] }));
230
+ });
231
+ const { add: addNotifier, config, destroy, remove } = createNotifier({
232
+ duration: false,
233
+ render: (notif) => {
234
+ const { key, ...restNotif } = notif;
235
+ return (jsx(NotificationCenter, { ...restNotif, reference: key }, key));
236
+ },
237
+ renderContainer: (children) => (jsx(NotificationCenterContainer, { children: children })),
238
+ setRoot: (root) => {
239
+ root === null || root === void 0 ? void 0 : root.setAttribute('class', notificationClasses.root);
240
+ },
241
+ });
242
+ NotificationCenter.add = (notif) => {
243
+ if (notif.type === 'drawer') {
244
+ return 'NOT_SET';
245
+ }
246
+ return addNotifier(notif);
247
+ };
248
+ NotificationCenter.config = config;
249
+ NotificationCenter.destroy = destroy;
250
+ NotificationCenter.remove = remove;
251
+ ['success', 'warning', 'error', 'info'].forEach((severity) => {
252
+ NotificationCenter[severity] = (props) => NotificationCenter.add({
253
+ ...props,
254
+ severity: severity || 'info',
255
+ type: 'notification',
256
+ });
257
+ });
258
+
259
+ export { NotificationCenter as default };
@@ -0,0 +1,89 @@
1
+ import { type ChangeEventHandler, type ComponentProps, type Key, type ReactElement } from 'react';
2
+ import { DrawerSize } from '@mezzanine-ui/core/drawer';
3
+ import { type IconDefinition } from '@mezzanine-ui/icons';
4
+ import { type DrawerProps } from '../Drawer';
5
+ import NotificationCenter, { type NotificationData } from './NotificationCenter';
6
+ type NotificationDataForDrawer = NotificationData & {
7
+ key: Key;
8
+ type: 'drawer';
9
+ };
10
+ type NotificationCenterDrawerPropsBase = Pick<DrawerProps, 'open' | 'onClose'> & {
11
+ /**
12
+ * The label of the all radio.
13
+ */
14
+ allRadioLabel?: string;
15
+ /**
16
+ * The label of the custom radio.
17
+ */
18
+ customRadioLabel?: string;
19
+ /**
20
+ * The default value of the radio group.
21
+ */
22
+ defaultValue?: string;
23
+ /**
24
+ * The size of the drawer.
25
+ * @default 'narrow'
26
+ */
27
+ drawerSize?: DrawerSize;
28
+ /**
29
+ * The icon of the empty notification.
30
+ */
31
+ emptyNotificationIcon?: IconDefinition;
32
+ /**
33
+ * The title of the empty notification.
34
+ */
35
+ emptyNotificationTitle?: string;
36
+ /**
37
+ * The callback function when the custom button is clicked.
38
+ */
39
+ onCustomButtonClick?: VoidFunction;
40
+ /**
41
+ * The callback function when the radio group value changes.
42
+ */
43
+ onRadioChange?: ChangeEventHandler<HTMLInputElement>;
44
+ /**
45
+ * The label of the read radio.
46
+ */
47
+ readRadioLabel?: string;
48
+ /**
49
+ * Controls whether to display the toolbar.
50
+ * @default true
51
+ */
52
+ showToolbar?: boolean;
53
+ /**
54
+ * Controls whether to display the unread button.
55
+ * @default false
56
+ */
57
+ showUnreadButton?: boolean;
58
+ /**
59
+ * The title of the drawer.
60
+ */
61
+ title?: string;
62
+ /**
63
+ * The label of the unread radio.
64
+ */
65
+ unreadRadioLabel?: string;
66
+ /**
67
+ * The value of the radio group.
68
+ */
69
+ value?: string;
70
+ };
71
+ export type NotificationCenterDrawerProps = (NotificationCenterDrawerPropsBase & {
72
+ /**
73
+ * The children of the drawer.
74
+ * Use this when you want to manually render NotificationCenter components.
75
+ * Can be a single NotificationCenter element or an array of them.
76
+ */
77
+ children: ReactElement<ComponentProps<typeof NotificationCenter>> | ReactElement<ComponentProps<typeof NotificationCenter>>[];
78
+ notificationList?: never;
79
+ }) | (NotificationCenterDrawerPropsBase & {
80
+ /**
81
+ * The list of notifications to render.
82
+ * Use this when you want to pass notification data and let the component render them.
83
+ * Each notification must have `key` and `type: 'drawer'`.
84
+ */
85
+ notificationList: NotificationDataForDrawer[];
86
+ children?: never;
87
+ });
88
+ declare const NotificationCenterDrawer: (props: NotificationCenterDrawerProps) => import("react/jsx-runtime").JSX.Element;
89
+ export default NotificationCenterDrawer;
@@ -0,0 +1,3 @@
1
+ export type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';
2
+ export { default } from './NotificationCenter';
3
+ export type { NotificationConfigProps, NotificationData, } from './NotificationCenter';
@@ -0,0 +1 @@
1
+ export { default } from './NotificationCenter.js';
@@ -1,5 +1,6 @@
1
1
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
2
2
  import type { ButtonProps } from '../Button';
3
+ import { DropdownProps } from '../Dropdown';
3
4
  /**
4
5
  * Single button configuration - only primary button is allowed
5
6
  */
@@ -41,14 +42,23 @@ type PageFooterStandardProps = PageFooterBaseProps & {
41
42
  */
42
43
  type?: 'standard';
43
44
  /**
44
- * Standard type: A ghost button with text.
45
- * Children of the button.
45
+ * The text/label (children) for the supporting action button in the PageFooter annotation.
46
46
  */
47
- annotation?: string;
47
+ supportingActionName?: ButtonProps['children'];
48
+ /**
49
+ * The HTML button type for the supporting action (e.g., 'button', 'submit', 'reset').
50
+ */
51
+ supportingActionType?: ButtonProps['type'];
52
+ /**
53
+ * Click handler for the supporting action button in the PageFooter annotation.
54
+ */
55
+ supportingActionOnClick?: ButtonProps['onClick'];
48
56
  /**
49
- * Standard type: Button click handler.
57
+ * Visual style variant of the supporting action button in the PageFooter
58
+ * (for example, 'base-ghost', 'base-secondary').
59
+ * @default 'base-ghost'
50
60
  */
51
- onAnnotationClick?: ButtonProps['onClick'];
61
+ supportingActionVariant?: ButtonProps['variant'];
52
62
  };
53
63
  type PageFooterOverflowProps = PageFooterBaseProps & {
54
64
  /**
@@ -57,13 +67,13 @@ type PageFooterOverflowProps = PageFooterBaseProps & {
57
67
  type: 'overflow';
58
68
  /**
59
69
  * Overflow type: Icon for the icon-only button.
60
- * @TODO Consider Dropdown integration after Dropdown redesign.
70
+ * @default DotVerticalIcon
61
71
  */
62
- annotation?: ButtonProps['icon'];
72
+ supportingActionIcon?: ButtonProps['icon'];
63
73
  /**
64
- * Overflow type: Button click handler.
74
+ * Dropdown props for the supporting action button.
65
75
  */
66
- onAnnotationClick?: ButtonProps['onClick'];
76
+ dropdownProps: Partial<DropdownProps>;
67
77
  };
68
78
  type PageFooterInformationProps = PageFooterBaseProps & {
69
79
  /**
@@ -4,11 +4,13 @@ import { pageFooterClasses } from '@mezzanine-ui/core/page-footer';
4
4
  import Button from '../Button/Button.js';
5
5
  import ButtonGroup from '../Button/ButtonGroup.js';
6
6
  import Typography from '../Typography/Typography.js';
7
+ import { DotVerticalIcon } from '@mezzanine-ui/icons';
8
+ import Dropdown from '../Dropdown/Dropdown.js';
7
9
  import cx from 'clsx';
8
10
 
9
11
  const PageFooter = forwardRef(function PageFooter(props, ref) {
10
12
  var _a;
11
- const { actions, annotation, annotationClassName, className, type = 'standard', warningMessage, ...rest } = props;
13
+ const { actions, annotationClassName, className, type = 'standard', warningMessage, ...rest } = props;
12
14
  // Filter out onAnnotationClick from rest props to avoid React warnings
13
15
  if ('onAnnotationClick' in rest) {
14
16
  delete rest.onAnnotationClick;
@@ -16,21 +18,19 @@ const PageFooter = forwardRef(function PageFooter(props, ref) {
16
18
  const { children: primaryButtonText, ...restPrimaryButtonProps } = (_a = actions === null || actions === void 0 ? void 0 : actions.primaryButton) !== null && _a !== void 0 ? _a : {};
17
19
  // Render annotation based on type
18
20
  const renderAnnotation = () => {
19
- if (!annotation)
20
- return null;
21
21
  switch (type) {
22
22
  case 'standard': {
23
- return (jsx(Button, { onClick: 'onAnnotationClick' in props
24
- ? props.onAnnotationClick
25
- : undefined, size: "main", variant: "base-ghost", children: annotation }));
23
+ const { supportingActionName, supportingActionType, supportingActionOnClick, supportingActionVariant = 'base-ghost', } = props;
24
+ return (jsx(Button, { size: "main", type: supportingActionType, onClick: supportingActionOnClick, variant: supportingActionVariant, children: supportingActionName }));
26
25
  }
27
26
  case 'overflow': {
28
- // @TODO Consider Dropdown integration after Dropdown redesign
29
- return (jsx(Button, { icon: annotation, onClick: 'onAnnotationClick' in props
30
- ? props.onAnnotationClick
31
- : undefined, size: "main", variant: "base-ghost" }));
27
+ const { supportingActionIcon, dropdownProps } = props;
28
+ return (jsx(Dropdown, { ...dropdownProps, options: dropdownProps.options || [], placement: dropdownProps.placement || 'top', children: jsx(Button, { type: "button", iconType: "icon-only", icon: supportingActionIcon || DotVerticalIcon, size: "main", variant: "base-ghost" }) }));
32
29
  }
33
30
  case 'information': {
31
+ const { annotation } = props;
32
+ if (!annotation)
33
+ return null;
34
34
  return (jsx(Typography, { color: "text-neutral", variant: "caption", children: annotation }));
35
35
  }
36
36
  default:
@@ -27,20 +27,15 @@ const getBreadcrumbAndToolbar = (children) => {
27
27
  }
28
28
  else if (child.type === Button) {
29
29
  backButtonOrLink = cloneElement(child, {
30
- icon: {
31
- position: 'icon-only',
32
- src: ChevronLeftIcon,
33
- },
30
+ iconType: 'icon-only',
31
+ icon: ChevronLeftIcon,
34
32
  size: 'sub',
35
33
  variant: 'base-tertiary',
36
34
  });
37
35
  }
38
36
  else if (child.type === 'a') {
39
37
  backButtonOrLink = cloneElement(child, {
40
- children: (jsx(Button, { component: 'div', icon: {
41
- position: 'icon-only',
42
- src: ChevronLeftIcon,
43
- }, size: "sub", variant: "base-tertiary" })),
38
+ children: (jsx(Button, { component: 'div', iconType: "icon-only", icon: ChevronLeftIcon, size: "sub", variant: "base-tertiary" })),
44
39
  });
45
40
  }
46
41
  else {
@@ -60,10 +55,7 @@ const PageHeader = forwardRef(function PageHeader(props, ref) {
60
55
  const { children, className, description, onBackClick, title, titleComponent = 'h2', ...rest } = props;
61
56
  const { backButtonOrLink, breadcrumb, pageToolbar } = getBreadcrumbAndToolbar(children);
62
57
  // prop onBack takes precedence over backButtonOrLink
63
- const backButton = onBackClick ? (jsx(Button, { icon: {
64
- position: 'icon-only',
65
- src: ChevronLeftIcon,
66
- }, onClick: onBackClick, size: "sub", variant: "base-tertiary" })) : (backButtonOrLink);
58
+ const backButton = onBackClick ? (jsx(Button, { iconType: "icon-only", icon: ChevronLeftIcon, onClick: onBackClick, size: "sub", variant: "base-tertiary" })) : (backButtonOrLink);
67
59
  return (jsxs("header", { ...rest, className: cx(pageHeaderClasses.host, className), ref: ref, children: [breadcrumb, jsxs("span", { className: pageHeaderClasses.headerContent, children: [jsxs("span", { className: pageHeaderClasses.pageTitleWithIcon, children: [backButton && jsx("div", { children: backButton }), jsxs("div", { className: pageHeaderClasses.pageTitleText, children: [jsx(Typography, { align: "left", color: "text-neutral-solid", component: titleComponent, variant: "h2", children: title }), description && (jsx(Typography, { align: "left", color: "text-neutral", component: "p", variant: "caption", children: description }))] })] }), pageToolbar] })] }));
68
60
  });
69
61