@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
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { forwardRef, useRef, useMemo, useCallback } from 'react';
4
+ import { multipleDatePickerClasses } from '@mezzanine-ui/core/multiple-date-picker';
5
+ import TagGroup from '../Tag/TagGroup.js';
6
+ import { useSelectTriggerTags } from '../Select/useSelectTriggerTags.js';
7
+ import Tag from '../Tag/Tag.js';
8
+ import OverflowCounterTag from '../OverflowTooltip/OverflowCounterTag.js';
9
+ import TextField from '../TextField/TextField.js';
10
+ import cx from 'clsx';
11
+
12
+ /**
13
+ * The trigger component for MultipleDatePicker.
14
+ * Displays selected dates as tags within a TextField.
15
+ */
16
+ const MultipleDatePickerTrigger = forwardRef(function MultipleDatePickerTrigger(props, ref) {
17
+ const { active = false, className, clearable = true, disabled = false, error = false, fullWidth = false, onTagClose, overflowStrategy = 'counter', placeholder, readOnly = false, required = false, size = 'main', suffix, value = [], ...restTextFieldProps } = props;
18
+ const tagsContainerRef = useRef(null);
19
+ const tagsRef = useRef(null);
20
+ const tagSize = size === 'main' ? 'main' : 'sub';
21
+ // Convert DateValue[] to SelectValue[] for useSelectTriggerTags
22
+ const selectValues = useMemo(() => value.map((v) => ({ id: v.id, name: v.name })), [value]);
23
+ const { overflowSelections, renderFakeTags, visibleSelections } = useSelectTriggerTags({
24
+ containerRef: tagsContainerRef,
25
+ enabled: overflowStrategy === 'counter',
26
+ size: tagSize,
27
+ tagsRef,
28
+ value: selectValues,
29
+ });
30
+ const displaySelections = useMemo(() => (overflowStrategy === 'counter' ? visibleSelections : selectValues), [overflowStrategy, selectValues, visibleSelections]);
31
+ // Find the original DateValue by id
32
+ const findDateValue = useCallback((id) => value.find((v) => v.id === id), [value]);
33
+ const handleTagClose = useCallback((id) => (e) => {
34
+ e.stopPropagation();
35
+ const dateValue = findDateValue(id);
36
+ if (dateValue && onTagClose) {
37
+ onTagClose(dateValue.date);
38
+ }
39
+ }, [findDateValue, onTagClose]);
40
+ const tagChildren = useMemo(() => {
41
+ const tags = displaySelections.map((selection) => {
42
+ if (readOnly) {
43
+ return (jsx(Tag, { label: selection.name, readOnly: true, size: tagSize, type: "static" }, selection.id));
44
+ }
45
+ return (jsx(Tag, { disabled: disabled, label: selection.name, onClose: handleTagClose(selection.id), size: tagSize, type: "dismissable" }, selection.id));
46
+ });
47
+ if (overflowStrategy === 'counter' && overflowSelections.length) {
48
+ tags.push(jsx(OverflowCounterTag, { disabled: disabled, onClick: (e) => {
49
+ e.stopPropagation();
50
+ }, onTagDismiss: (tagIndex) => {
51
+ const target = overflowSelections[tagIndex];
52
+ if (!target)
53
+ return;
54
+ const dateValue = findDateValue(target.id);
55
+ if (dateValue && onTagClose) {
56
+ onTagClose(dateValue.date);
57
+ }
58
+ }, readOnly: readOnly, tagSize: tagSize, tags: overflowSelections.map((s) => s.name) }, "overflow-counter"));
59
+ }
60
+ return tags;
61
+ }, [
62
+ disabled,
63
+ displaySelections,
64
+ findDateValue,
65
+ handleTagClose,
66
+ onTagClose,
67
+ overflowSelections,
68
+ overflowStrategy,
69
+ readOnly,
70
+ tagSize,
71
+ ]);
72
+ const hasValue = value.length > 0;
73
+ // TextField requires disabled and readonly to be mutually exclusive
74
+ let interactiveProps = {};
75
+ if (disabled) {
76
+ interactiveProps = { disabled: true };
77
+ }
78
+ else if (readOnly) {
79
+ interactiveProps = { readonly: true };
80
+ }
81
+ return (jsx(TextField, { ...restTextFieldProps, ...interactiveProps, active: active, className: cx(multipleDatePickerClasses.trigger, {
82
+ [multipleDatePickerClasses.triggerSelected]: hasValue,
83
+ [multipleDatePickerClasses.triggerDisabled]: disabled,
84
+ [multipleDatePickerClasses.triggerReadOnly]: readOnly,
85
+ }, className), clearable: !readOnly && clearable && hasValue, error: error, fullWidth: fullWidth, ref: ref, size: size, suffix: suffix, children: jsx("div", { ref: tagsContainerRef, className: cx(multipleDatePickerClasses.triggerTagsWrapper, {
86
+ [multipleDatePickerClasses.triggerTagsWrapperEllipsis]: overflowStrategy === 'counter',
87
+ }), children: hasValue ? (jsxs(Fragment, { children: [jsxs("div", { className: cx(multipleDatePickerClasses.triggerTags, {
88
+ [multipleDatePickerClasses.triggerTagsEllipsis]: overflowStrategy === 'counter',
89
+ }), ref: tagsRef, children: [jsx(TagGroup, { children: tagChildren }), overflowStrategy === 'counter' ? renderFakeTags() : null] }), jsx("input", { "aria-disabled": disabled, "aria-multiline": false, "aria-readonly": readOnly, "aria-required": required, className: cx(multipleDatePickerClasses.triggerInput, multipleDatePickerClasses.triggerInputAbsolute), disabled: disabled, readOnly: true, tabIndex: -1, type: "text", value: "" })] })) : (jsx("input", { "aria-disabled": disabled, "aria-multiline": false, "aria-readonly": readOnly, "aria-required": required, className: multipleDatePickerClasses.triggerInput, disabled: disabled, placeholder: placeholder, readOnly: true, tabIndex: -1, type: "text", value: "" })) }) }));
90
+ });
91
+
92
+ export { MultipleDatePickerTrigger as default };
@@ -0,0 +1,6 @@
1
+ export { default } from './MultipleDatePicker';
2
+ export type { MultipleDatePickerProps } from './MultipleDatePicker';
3
+ export { default as MultipleDatePickerTrigger } from './MultipleDatePickerTrigger';
4
+ export type { DateValue as MultipleDatePickerDateValue, MultipleDatePickerTriggerProps, } from './MultipleDatePickerTrigger';
5
+ export { useMultipleDatePickerValue } from './useMultipleDatePickerValue';
6
+ export type { UseMultipleDatePickerValueProps, UseMultipleDatePickerValueReturn, } from './useMultipleDatePickerValue';
@@ -0,0 +1,3 @@
1
+ export { default } from './MultipleDatePicker.js';
2
+ export { default as MultipleDatePickerTrigger } from './MultipleDatePickerTrigger.js';
3
+ export { useMultipleDatePickerValue } from './useMultipleDatePickerValue.js';
@@ -0,0 +1,55 @@
1
+ import { DateType } from '@mezzanine-ui/core/calendar';
2
+ import { MultipleDatePickerValue } from '@mezzanine-ui/core/multiple-date-picker';
3
+ export interface UseMultipleDatePickerValueProps {
4
+ /**
5
+ * The format pattern for displaying dates (e.g., "YYYY-MM-DD")
6
+ */
7
+ format: string;
8
+ /**
9
+ * Maximum number of dates that can be selected
10
+ */
11
+ maxSelections?: number;
12
+ /**
13
+ * Controlled value
14
+ */
15
+ value?: MultipleDatePickerValue;
16
+ }
17
+ export interface UseMultipleDatePickerValueReturn {
18
+ /**
19
+ * The internal value (pending changes)
20
+ */
21
+ internalValue: MultipleDatePickerValue;
22
+ /**
23
+ * Toggle a date in/out of selection
24
+ */
25
+ toggleDate: (date: DateType) => void;
26
+ /**
27
+ * Remove a specific date from selection
28
+ */
29
+ removeDate: (date: DateType) => void;
30
+ /**
31
+ * Clear all selected dates
32
+ */
33
+ clearAll: () => void;
34
+ /**
35
+ * Check if a date is currently selected
36
+ */
37
+ isDateSelected: (date: DateType) => boolean;
38
+ /**
39
+ * Check if selection has reached max limit
40
+ */
41
+ isMaxReached: boolean;
42
+ /**
43
+ * Confirm the current selection (returns the value to be passed to onChange)
44
+ */
45
+ getConfirmValue: () => MultipleDatePickerValue;
46
+ /**
47
+ * Cancel and revert to original value
48
+ */
49
+ revertToValue: () => void;
50
+ /**
51
+ * Format a date to display string
52
+ */
53
+ formatDate: (date: DateType) => string;
54
+ }
55
+ export declare function useMultipleDatePickerValue({ format, maxSelections, value, }: UseMultipleDatePickerValueProps): UseMultipleDatePickerValueReturn;
@@ -0,0 +1,68 @@
1
+ 'use client';
2
+ import { useCallback, useState, useEffect } from 'react';
3
+ import { useCalendarContext } from '../Calendar/CalendarContext.js';
4
+
5
+ function useMultipleDatePickerValue({ format, maxSelections, value = [], }) {
6
+ const { formatToString, isBefore, isSameDate, locale } = useCalendarContext();
7
+ // Sort dates in chronological order
8
+ const sortDates = useCallback((dates) => {
9
+ return [...dates].sort((a, b) => {
10
+ if (isSameDate(a, b))
11
+ return 0;
12
+ return isBefore(a, b) ? -1 : 1;
13
+ });
14
+ }, [isBefore, isSameDate]);
15
+ // Internal state for pending changes
16
+ const [internalValue, setInternalValue] = useState(() => sortDates(value));
17
+ // Sync internal value when controlled value changes
18
+ useEffect(() => {
19
+ setInternalValue(sortDates(value));
20
+ }, [sortDates, value]);
21
+ const formatDate = useCallback((date) => {
22
+ return formatToString(locale, date, format);
23
+ }, [formatToString, locale, format]);
24
+ const isDateSelected = useCallback((date) => {
25
+ return internalValue.some((d) => isSameDate(d, date));
26
+ }, [internalValue, isSameDate]);
27
+ const isMaxReached = typeof maxSelections === 'number' && internalValue.length >= maxSelections;
28
+ const toggleDate = useCallback((date) => {
29
+ setInternalValue((prev) => {
30
+ const existingIndex = prev.findIndex((d) => isSameDate(d, date));
31
+ if (existingIndex >= 0) {
32
+ // Remove the date
33
+ return prev.filter((_, index) => index !== existingIndex);
34
+ }
35
+ // Check max limit before adding
36
+ if (typeof maxSelections === 'number' && prev.length >= maxSelections) {
37
+ return prev;
38
+ }
39
+ // Add the date and sort
40
+ return sortDates([...prev, date]);
41
+ });
42
+ }, [isSameDate, maxSelections, sortDates]);
43
+ const removeDate = useCallback((date) => {
44
+ setInternalValue((prev) => prev.filter((d) => !isSameDate(d, date)));
45
+ }, [isSameDate]);
46
+ const clearAll = useCallback(() => {
47
+ setInternalValue([]);
48
+ }, []);
49
+ const getConfirmValue = useCallback(() => {
50
+ return internalValue;
51
+ }, [internalValue]);
52
+ const revertToValue = useCallback(() => {
53
+ setInternalValue(sortDates(value));
54
+ }, [sortDates, value]);
55
+ return {
56
+ clearAll,
57
+ formatDate,
58
+ getConfirmValue,
59
+ internalValue,
60
+ isDateSelected,
61
+ isMaxReached,
62
+ removeDate,
63
+ revertToValue,
64
+ toggleDate,
65
+ };
66
+ }
67
+
68
+ export { useMultipleDatePickerValue };
@@ -1,4 +1,4 @@
1
- import { type ChangeEventHandler, type ComponentProps, type Key, type ReactElement } from 'react';
1
+ import { type ComponentProps, type Key, type ReactElement } from 'react';
2
2
  import { DrawerSize } from '@mezzanine-ui/core/drawer';
3
3
  import { type IconDefinition } from '@mezzanine-ui/icons';
4
4
  import { type DrawerProps } from '../Drawer';
@@ -7,24 +7,17 @@ type NotificationDataForDrawer = NotificationData & {
7
7
  key: Key;
8
8
  type: 'drawer';
9
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;
10
+ type NotificationCenterDrawerPropsBase = Pick<DrawerProps, 'controlBarAllRadioLabel' | 'controlBarCustomButtonLabel' | 'controlBarDefaultValue' | 'controlBarOnCustomButtonClick' | 'controlBarOnRadioChange' | 'controlBarReadRadioLabel' | 'controlBarShow' | 'controlBarShowUnreadButton' | 'controlBarUnreadRadioLabel' | 'controlBarValue' | 'onClose' | 'open' | 'renderControlBar'> & {
23
11
  /**
24
12
  * The size of the drawer.
25
13
  * @default 'narrow'
26
14
  */
27
15
  drawerSize?: DrawerSize;
16
+ /**
17
+ * The label of the "earlier" time group.
18
+ * @default '更早'
19
+ */
20
+ earlierLabel?: string;
28
21
  /**
29
22
  * The icon of the empty notification.
30
23
  */
@@ -34,49 +27,14 @@ type NotificationCenterDrawerPropsBase = Pick<DrawerProps, 'open' | 'onClose'> &
34
27
  */
35
28
  emptyNotificationTitle?: string;
36
29
  /**
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
30
+ * The label for the "past 7 days" time group.
31
+ * @default '過去七天'
56
32
  */
57
- showUnreadButton?: boolean;
33
+ past7DaysLabel?: string;
58
34
  /**
59
35
  * The title of the drawer.
60
36
  */
61
37
  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
- * The label for the "earlier" time group.
72
- * @default '更早'
73
- */
74
- earlierLabel?: string;
75
- /**
76
- * The label for the "past 7 days" time group.
77
- * @default '過去七天'
78
- */
79
- past7DaysLabel?: string;
80
38
  /**
81
39
  * The label for the "today" time group.
82
40
  * @default '今天'
@@ -0,0 +1,128 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { useMemo } from 'react';
4
+ import { notificationClasses } from '@mezzanine-ui/core/notification-center';
5
+ import { NotificationIcon } from '@mezzanine-ui/icons';
6
+ import Typography from '../Typography/Typography.js';
7
+ import NotificationCenter from './NotificationCenter.js';
8
+ import Drawer from '../Drawer/Drawer.js';
9
+ import Icon from '../Icon/Icon.js';
10
+
11
+ const isValidTime = (timestamp) => {
12
+ if (!timestamp)
13
+ return false;
14
+ const date = new Date(timestamp);
15
+ return !Number.isNaN(date.getTime());
16
+ };
17
+ const getValidTime = (timestamp) => {
18
+ if (!isValidTime(timestamp))
19
+ return 0;
20
+ const date = new Date(timestamp);
21
+ return date.getTime();
22
+ };
23
+ const DEFAULT_TIME_GROUP_LABELS = {
24
+ today: '今天',
25
+ yesterday: '昨天',
26
+ past7Days: '過去七天',
27
+ earlier: '更早',
28
+ };
29
+ const TIME_GROUP_ORDER = [
30
+ 'today',
31
+ 'yesterday',
32
+ 'past7Days',
33
+ 'earlier',
34
+ ];
35
+ const getTimeGroup = (timestamp, now) => {
36
+ if (!isValidTime(timestamp))
37
+ return 'earlier';
38
+ const notificationDate = new Date(timestamp);
39
+ const nowStartOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
40
+ const notificationStartOfDay = new Date(notificationDate.getFullYear(), notificationDate.getMonth(), notificationDate.getDate());
41
+ // Today: same calendar day
42
+ if (notificationStartOfDay.getTime() === nowStartOfDay.getTime()) {
43
+ return 'today';
44
+ }
45
+ // Yesterday: previous calendar day
46
+ const yesterdayStartOfDay = new Date(nowStartOfDay);
47
+ yesterdayStartOfDay.setDate(yesterdayStartOfDay.getDate() - 1);
48
+ if (notificationStartOfDay.getTime() === yesterdayStartOfDay.getTime()) {
49
+ return 'yesterday';
50
+ }
51
+ // Past 7 days: within 7 days but not today or yesterday
52
+ const diffInDays = (now.getTime() - notificationDate.getTime()) / (1000 * 60 * 60 * 24);
53
+ if (diffInDays <= 7) {
54
+ return 'past7Days';
55
+ }
56
+ // Earlier: more than 7 days ago
57
+ return 'earlier';
58
+ };
59
+ const NotificationCenterDrawer = (props) => {
60
+ const { children, controlBarAllRadioLabel, controlBarCustomButtonLabel, controlBarDefaultValue, controlBarOnCustomButtonClick, controlBarOnRadioChange, controlBarReadRadioLabel, controlBarShow, controlBarShowUnreadButton, controlBarUnreadRadioLabel, controlBarValue, drawerSize = 'narrow', earlierLabel, emptyNotificationIcon = NotificationIcon, emptyNotificationTitle = '目前沒有新的通知', notificationList, onClose, open, past7DaysLabel, renderControlBar, title, todayLabel, yesterdayLabel, ...restDrawerProps } = props;
61
+ const isEmpty = useMemo(() => {
62
+ if (notificationList) {
63
+ return notificationList.length === 0;
64
+ }
65
+ if (Array.isArray(children)) {
66
+ return children.length === 0;
67
+ }
68
+ return !children;
69
+ }, [notificationList, children]);
70
+ const renderNotifications = useMemo(() => {
71
+ const renderEmptyNotifications = () => {
72
+ return (jsxs("div", { className: notificationClasses.emptyNotifications, children: [jsx(Icon, { icon: emptyNotificationIcon, size: 28 }), jsx(Typography, { children: emptyNotificationTitle })] }));
73
+ };
74
+ if (isEmpty) {
75
+ return renderEmptyNotifications();
76
+ }
77
+ // Check if notificationList is provided
78
+ if (notificationList) {
79
+ // Sort once by timestamp (newest first), then group while maintaining order
80
+ const sorted = [...notificationList].sort((a, b) => {
81
+ const aTime = getValidTime(a.timeStamp);
82
+ const bTime = getValidTime(b.timeStamp);
83
+ return bTime - aTime;
84
+ });
85
+ // Group sorted notifications
86
+ const now = new Date();
87
+ const grouped = sorted.reduce((acc, notification) => {
88
+ var _a;
89
+ const group = getTimeGroup(notification.timeStamp, now);
90
+ ((_a = acc[group]) !== null && _a !== void 0 ? _a : (acc[group] = [])).push(notification);
91
+ return acc;
92
+ }, {});
93
+ // Get time group labels from props or use defaults
94
+ const timeGroupLabels = {
95
+ earlier: earlierLabel !== null && earlierLabel !== void 0 ? earlierLabel : DEFAULT_TIME_GROUP_LABELS.earlier,
96
+ past7Days: past7DaysLabel !== null && past7DaysLabel !== void 0 ? past7DaysLabel : DEFAULT_TIME_GROUP_LABELS.past7Days,
97
+ today: todayLabel !== null && todayLabel !== void 0 ? todayLabel : DEFAULT_TIME_GROUP_LABELS.today,
98
+ yesterday: yesterdayLabel !== null && yesterdayLabel !== void 0 ? yesterdayLabel : DEFAULT_TIME_GROUP_LABELS.yesterday,
99
+ };
100
+ // Render notifications with prependTips for first item in each group
101
+ return TIME_GROUP_ORDER.flatMap((group) => {
102
+ const notifications = grouped[group];
103
+ if (!(notifications === null || notifications === void 0 ? void 0 : notifications.length)) {
104
+ return [];
105
+ }
106
+ return notifications.map((notification, index) => {
107
+ const { key, ...restNotification } = notification;
108
+ return (jsx(NotificationCenter, { ...restNotification, prependTips: index === 0 ? timeGroupLabels[group] : undefined, reference: key, type: "drawer" }, key));
109
+ });
110
+ });
111
+ }
112
+ // Return children (can be single element or array)
113
+ return Array.isArray(children) ? children : children;
114
+ }, [
115
+ isEmpty,
116
+ notificationList,
117
+ children,
118
+ emptyNotificationIcon,
119
+ emptyNotificationTitle,
120
+ earlierLabel,
121
+ past7DaysLabel,
122
+ todayLabel,
123
+ yesterdayLabel,
124
+ ]);
125
+ return (jsx(Drawer, { className: notificationClasses.drawer, controlBarAllRadioLabel: controlBarAllRadioLabel, controlBarCustomButtonLabel: controlBarCustomButtonLabel, controlBarDefaultValue: controlBarDefaultValue, controlBarIsEmpty: isEmpty, controlBarOnCustomButtonClick: controlBarOnCustomButtonClick, controlBarOnRadioChange: controlBarOnRadioChange, controlBarReadRadioLabel: controlBarReadRadioLabel, controlBarShow: controlBarShow, controlBarShowUnreadButton: controlBarShowUnreadButton, controlBarUnreadRadioLabel: controlBarUnreadRadioLabel, controlBarValue: controlBarValue, headerTitle: title, isHeaderDisplay: Boolean(title), onClose: onClose, open: open, renderControlBar: renderControlBar, size: drawerSize, ...restDrawerProps, children: renderNotifications }));
126
+ };
127
+
128
+ export { NotificationCenterDrawer as default };
@@ -1,3 +1,5 @@
1
1
  export type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';
2
2
  export { default } from './NotificationCenter';
3
3
  export type { NotificationConfigProps, NotificationData, } from './NotificationCenter';
4
+ export { default as NotificationCenterDrawer } from './NotificationCenterDrawer';
5
+ export type { NotificationCenterDrawerProps } from './NotificationCenterDrawer';
@@ -1 +1,2 @@
1
1
  export { default } from './NotificationCenter.js';
2
+ export { default as NotificationCenterDrawer } from './NotificationCenterDrawer.js';
@@ -1,2 +1,2 @@
1
- export { default, OverflowTooltipProps } from './OverflowTooltip';
2
- export { default as OverflowCounterTag, OverflowCounterTagProps, } from './OverflowCounterTag';
1
+ export { default, type OverflowTooltipProps } from './OverflowTooltip';
2
+ export { default as OverflowCounterTag, type OverflowCounterTagProps, } from './OverflowCounterTag';
@@ -69,7 +69,7 @@ const RangePickerTrigger = forwardRef(function RangePickerTrigger(props, ref) {
69
69
  const handleToBlur = useCallback((e) => {
70
70
  onToBlur === null || onToBlur === void 0 ? void 0 : onToBlur(e);
71
71
  }, [onToBlur]);
72
- return (jsxs(TextField, { ...restTextFieldProps, ...defaultTextFieldProps, ref: ref, active: !!inputFromValue || !!inputToValue, className: cx(pickerClasses.host, className), clearable: !readOnly && clearable, suffix: suffix !== null && suffix !== void 0 ? suffix : defaultSuffix, children: [jsx(FormattedInput, { ...inputFromProps, ref: fromRef, "aria-disabled": disabled, "aria-label": "Start date", "aria-multiline": false, "aria-readonly": readOnly, "aria-required": required, disabled: disabled, errorMessages: errorMessagesFrom, format: format, onBlur: handleFromBlur, onChange: handleFromChange, onFocus: handleFromFocus, placeholder: inputFromPlaceholder, readOnly: readOnly, required: required, validate: validateFrom, value: inputFromValue }), jsx(Icon, { icon: LongTailArrowRightIcon, className: pickerClasses.arrowIcon, "aria-hidden": "true" }), jsx(FormattedInput, { ...inputToProps, ref: toRef, "aria-disabled": disabled, "aria-label": "End date", "aria-multiline": false, "aria-readonly": readOnly, "aria-required": required, disabled: disabled, errorMessages: errorMessagesTo, format: format, onBlur: handleToBlur, onChange: handleToChange, onFocus: handleToFocus, placeholder: inputToPlaceholder, readOnly: readOnly, required: required, validate: validateTo, value: inputToValue })] }));
72
+ return (jsxs(TextField, { ...restTextFieldProps, ...defaultTextFieldProps, ref: ref, className: cx(pickerClasses.host, className), clearable: !readOnly && clearable, suffix: suffix !== null && suffix !== void 0 ? suffix : defaultSuffix, children: [jsx(FormattedInput, { ...inputFromProps, ref: fromRef, "aria-disabled": disabled, "aria-label": "Start date", "aria-multiline": false, "aria-readonly": readOnly, "aria-required": required, disabled: disabled, errorMessages: errorMessagesFrom, format: format, onBlur: handleFromBlur, onChange: handleFromChange, onFocus: handleFromFocus, placeholder: inputFromPlaceholder, readOnly: readOnly, required: required, validate: validateFrom, value: inputFromValue }), jsx(Icon, { icon: LongTailArrowRightIcon, className: pickerClasses.arrowIcon, "aria-hidden": "true" }), jsx(FormattedInput, { ...inputToProps, ref: toRef, "aria-disabled": disabled, "aria-label": "End date", "aria-multiline": false, "aria-readonly": readOnly, "aria-required": required, disabled: disabled, errorMessages: errorMessagesTo, format: format, onBlur: handleToBlur, onChange: handleToChange, onFocus: handleToFocus, placeholder: inputToPlaceholder, readOnly: readOnly, required: required, validate: validateTo, value: inputToValue })] }));
73
73
  });
74
74
 
75
75
  export { RangePickerTrigger as default };
@@ -0,0 +1,32 @@
1
+ import { ReactElement } from 'react';
2
+ import { ContentHeaderProps } from '../ContentHeader';
3
+ import { FilterAreaProps } from '../FilterArea';
4
+ import { TabProps } from '../Tab';
5
+ export interface SectionProps {
6
+ /**
7
+ * Additional style for the section container.
8
+ */
9
+ className?: string;
10
+ /**
11
+ * Accept `<ContentHeader />` component from `mezzanine`.
12
+ * Other components will trigger a warning.
13
+ */
14
+ contentHeader?: ReactElement<ContentHeaderProps>;
15
+ /**
16
+ * Accept `<FilterArea />` component from `mezzanine`.
17
+ * Other components will trigger a warning.
18
+ */
19
+ filterArea?: ReactElement<FilterAreaProps>;
20
+ /**
21
+ * Accept `<Tab />` component from `mezzanine`.
22
+ * Other components will trigger a warning.
23
+ */
24
+ tab?: ReactElement<TabProps>;
25
+ }
26
+ /**
27
+ * The react component for `mezzanine` section.
28
+ */
29
+ declare const Section: import("react").ForwardRefExoticComponent<SectionProps & {
30
+ children?: import("react").ReactNode | undefined;
31
+ } & import("react").RefAttributes<HTMLDivElement>>;
32
+ export default Section;
@@ -0,0 +1,62 @@
1
+ import { jsxs } from 'react/jsx-runtime';
2
+ import { forwardRef, cloneElement, isValidElement } from 'react';
3
+ import { sectionClasses } from '@mezzanine-ui/core/section';
4
+ import ContentHeader from '../ContentHeader/ContentHeader.js';
5
+ import FilterArea from '../FilterArea/FilterArea.js';
6
+ import Tab from '../Tab/Tab.js';
7
+ import cx from 'clsx';
8
+
9
+ function getDisplayName(element) {
10
+ const type = element.type;
11
+ if (typeof type === 'string') {
12
+ return type;
13
+ }
14
+ return type.displayName
15
+ || type.name
16
+ || 'Unknown';
17
+ }
18
+ function isContentHeaderElement(element) {
19
+ return isValidElement(element) && element.type === ContentHeader;
20
+ }
21
+ function isFilterAreaElement(element) {
22
+ return isValidElement(element) && element.type === FilterArea;
23
+ }
24
+ function isTabElement(element) {
25
+ return isValidElement(element) && element.type === Tab;
26
+ }
27
+ /**
28
+ * The react component for `mezzanine` section.
29
+ */
30
+ const Section = forwardRef(function Section(props, ref) {
31
+ const { children, className, contentHeader, filterArea, tab, } = props;
32
+ let renderedContentHeader = null;
33
+ let renderedFilterArea = null;
34
+ let renderedTab = null;
35
+ if (contentHeader) {
36
+ if (isContentHeaderElement(contentHeader)) {
37
+ renderedContentHeader = cloneElement(contentHeader, { size: 'sub' });
38
+ }
39
+ else if (isValidElement(contentHeader)) {
40
+ console.warn(`[Section] Invalid contentHeader type: <${getDisplayName(contentHeader)}>. Only <ContentHeader /> component from @mezzanine-ui/react is allowed.`);
41
+ }
42
+ }
43
+ if (filterArea) {
44
+ if (isFilterAreaElement(filterArea)) {
45
+ renderedFilterArea = cloneElement(filterArea, { size: 'sub' });
46
+ }
47
+ else if (isValidElement(filterArea)) {
48
+ console.warn(`[Section] Invalid filterArea type: <${getDisplayName(filterArea)}>. Only <FilterArea /> component from @mezzanine-ui/react is allowed.`);
49
+ }
50
+ }
51
+ if (tab) {
52
+ if (isTabElement(tab)) {
53
+ renderedTab = tab;
54
+ }
55
+ else if (isValidElement(tab)) {
56
+ console.warn(`[Section] Invalid tab type: <${getDisplayName(tab)}>. Only <Tab /> component from @mezzanine-ui/react is allowed.`);
57
+ }
58
+ }
59
+ return (jsxs("div", { ref: ref, className: cx(sectionClasses.host, className), children: [renderedContentHeader, renderedFilterArea, renderedTab, children] }));
60
+ });
61
+
62
+ export { Section as default };
@@ -0,0 +1,2 @@
1
+ export type { SectionProps } from './Section';
2
+ export { default } from './Section';
@@ -23,10 +23,6 @@ export interface SelectBaseProps extends Omit<SelectTriggerProps, 'active' | 'in
23
23
  * The other native props for input element.
24
24
  */
25
25
  inputProps?: Omit<SelectTriggerInputProps, 'onBlur' | 'onChange' | 'onFocus' | 'placeholder' | 'role' | 'value' | `aria-${'controls' | 'expanded' | 'owns'}`>;
26
- /**
27
- * Whether to disable portal.
28
- */
29
- disablePortal?: boolean;
30
26
  /**
31
27
  * The max height of the dropdown list.
32
28
  */
@@ -60,6 +56,15 @@ export interface SelectBaseProps extends Omit<SelectTriggerProps, 'active' | 'in
60
56
  * The size of input.
61
57
  */
62
58
  size?: SelectInputSize;
59
+ /**
60
+ * The z-index of the dropdown.
61
+ */
62
+ dropdownZIndex?: number | string;
63
+ /**
64
+ * Whether to enable portal for the dropdown.
65
+ * @default true
66
+ */
67
+ globalPortal?: boolean;
63
68
  }
64
69
  export type SelectMultipleProps = SelectBaseProps & {
65
70
  /**
package/Select/Select.js CHANGED
@@ -14,7 +14,7 @@ import cx from 'clsx';
14
14
 
15
15
  const Select = forwardRef(function Select(props, ref) {
16
16
  const { disabled: disabledFromFormControl, fullWidth: fullWidthFromFormControl, required: requiredFromFormControl, severity, } = useContext(FormControlContext) || {};
17
- const { children, className, clearable = false, defaultValue, disabled = disabledFromFormControl || false, disablePortal = false, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, inputProps, inputRef, menuMaxHeight, mode = 'single', onBlur, onChange: onChangeProp, onClear: onClearProp, onFocus, onScroll, options: optionsProp, placeholder = '', prefix, readOnly = false, renderValue, required = requiredFromFormControl || false, size, suffixActionIcon, type = 'default', value: valueProp, } = props;
17
+ const { children, className, clearable = false, defaultValue, disabled = disabledFromFormControl || false, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, inputProps, inputRef, menuMaxHeight, mode = 'single', onBlur, onChange: onChangeProp, onClear: onClearProp, onFocus, onScroll, options: optionsProp, placeholder = '', prefix, readOnly = false, renderValue, required = requiredFromFormControl || false, size, suffixActionIcon, type = 'default', value: valueProp, dropdownZIndex, globalPortal = true, } = props;
18
18
  const [open, toggleOpen] = useState(false);
19
19
  const onOpen = useCallback(() => {
20
20
  // Prevent opening when readOnly is true
@@ -232,7 +232,7 @@ const Select = forwardRef(function Select(props, ref) {
232
232
  onChange,
233
233
  value,
234
234
  }), [onChange, value]);
235
- return (jsx(SelectControlContext.Provider, { value: context, children: jsx("div", { ref: nodeRef, className: cx(selectClasses.host, fullWidth && selectClasses.hostFullWidth), children: jsx(Dropdown, { disabled: readOnly || disabled, disablePortal: disablePortal, maxHeight: menuMaxHeight, mode: mode, onScroll: onScroll, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: readOnly ? false : open, options: options, sameWidth: true, type: dropdownType, value: dropdownValue, children: jsx(SelectTrigger, { ref: composedRef, active: !readOnly && open, className: className, clearable: clearable, disabled: disabled, error: error, fullWidth: fullWidth, inputRef: inputRef, mode: mode, onTagClose: onChange, onClear: onClear, onKeyDown: onKeyDownTextField, prefix: prefix, readOnly: readOnly, ...(mode === 'single' && renderValue ? { renderValue } : {}), required: required, inputProps: resolvedInputProps, size: size, suffixActionIcon: suffixActionIcon, value: value === null ? undefined : value }) }) }) }));
235
+ return (jsx(SelectControlContext.Provider, { value: context, children: jsx("div", { ref: nodeRef, className: cx(selectClasses.host, fullWidth && selectClasses.hostFullWidth, mode && selectClasses.hostMode(mode)), children: jsx(Dropdown, { disabled: readOnly || disabled, maxHeight: menuMaxHeight, mode: mode, onScroll: onScroll, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: readOnly ? false : open, options: options, sameWidth: true, type: dropdownType, value: dropdownValue, zIndex: dropdownZIndex, globalPortal: globalPortal, children: jsx(SelectTrigger, { ref: composedRef, active: !readOnly && open, className: className, clearable: clearable, disabled: disabled, error: error, fullWidth: fullWidth, inputRef: inputRef, mode: mode, onTagClose: onChange, onClear: onClear, onKeyDown: onKeyDownTextField, prefix: prefix, readOnly: readOnly, ...(mode === 'single' && renderValue ? { renderValue } : {}), required: required, inputProps: resolvedInputProps, size: size, suffixActionIcon: suffixActionIcon, value: value === null ? undefined : value }) }) }) }));
236
236
  });
237
237
 
238
238
  export { Select as default };