@mezzanine-ui/react 1.0.0-rc.0 → 1.0.0-rc.2
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/Badge/Badge.d.ts +4 -0
- package/Badge/Badge.js +2 -2
- package/Badge/typings.d.ts +13 -1
- package/Calendar/CalendarDays.js +4 -3
- package/Calendar/CalendarWeeks.js +4 -3
- package/Cascader/Cascader.d.ts +3 -0
- package/Cascader/Cascader.js +241 -0
- package/Cascader/CascaderPanel.d.ts +29 -0
- package/Cascader/CascaderPanel.js +29 -0
- package/Cascader/index.d.ts +5 -0
- package/Cascader/index.js +2 -0
- package/Cascader/typings.d.ts +92 -0
- package/Description/Description.d.ts +6 -1
- package/Description/Description.js +11 -4
- package/Description/DescriptionContent.d.ts +9 -3
- package/Description/DescriptionContent.js +4 -1
- package/Description/DescriptionContext.d.ts +6 -0
- package/Description/DescriptionContext.js +9 -0
- package/Description/index.d.ts +2 -0
- package/Description/index.js +1 -0
- package/Form/FormField.d.ts +6 -0
- package/Form/FormField.js +2 -2
- package/Form/FormHintText.d.ts +12 -0
- package/Form/FormHintText.js +3 -2
- package/Layout/Layout.d.ts +21 -5
- package/Layout/Layout.js +23 -19
- package/Layout/LayoutContext.d.ts +6 -6
- package/Layout/LayoutContext.js +2 -2
- package/Layout/LayoutHost.d.ts +0 -4
- package/Layout/LayoutHost.js +16 -13
- package/Layout/LayoutLeftPanel.d.ts +19 -0
- package/Layout/LayoutLeftPanel.js +86 -0
- package/Layout/LayoutMain.d.ts +10 -1
- package/Layout/LayoutMain.js +12 -3
- package/Layout/LayoutRightPanel.d.ts +19 -0
- package/Layout/{LayoutSidePanel.js → LayoutRightPanel.js} +32 -36
- package/Layout/index.d.ts +2 -2
- package/Layout/index.js +2 -1
- package/Modal/MediaPreviewModal.d.ts +4 -0
- package/Modal/MediaPreviewModal.js +2 -2
- package/Modal/Modal.d.ts +4 -0
- package/Modal/Modal.js +2 -2
- package/Modal/useModalContainer.js +0 -1
- package/Navigation/Navigation.d.ts +4 -0
- package/Navigation/Navigation.js +39 -3
- package/Navigation/NavigationFooter.js +19 -2
- package/Navigation/NavigationOption.d.ts +4 -0
- package/Navigation/NavigationOption.js +40 -19
- package/Navigation/NavigationOverflowMenuOption.js +11 -7
- package/Navigation/NavigationUserMenu.d.ts +1 -0
- package/Navigation/NavigationUserMenu.js +24 -5
- package/Navigation/context.d.ts +2 -0
- package/Navigation/context.js +4 -1
- package/Picker/RangePickerTrigger.js +1 -1
- package/Section/Section.js +6 -6
- package/Transition/Collapse.d.ts +2 -1
- package/Transition/Collapse.js +2 -1
- package/Upload/Upload.js +63 -9
- package/Upload/UploadPictureCard.d.ts +25 -15
- package/Upload/UploadPictureCard.js +14 -6
- package/index.d.ts +4 -2
- package/index.js +4 -1
- package/package.json +4 -4
- package/Layout/LayoutSidePanel.d.ts +0 -14
package/Badge/Badge.d.ts
CHANGED
|
@@ -6,24 +6,28 @@ declare const Badge: import("react").ForwardRefExoticComponent<(Omit<Omit<Native
|
|
|
6
6
|
children?: never;
|
|
7
7
|
count: number;
|
|
8
8
|
overflowCount?: number;
|
|
9
|
+
size?: never;
|
|
9
10
|
text?: never;
|
|
10
11
|
variant: BadgeCountVariant;
|
|
11
12
|
}, "ref"> | Omit<Omit<NativeElementPropsWithoutKeyAndRef<"span">, "children"> & {
|
|
12
13
|
children?: never;
|
|
13
14
|
count?: never;
|
|
14
15
|
overflowCount?: never;
|
|
16
|
+
size?: import("@mezzanine-ui/core/badge").BadgeTextSize;
|
|
15
17
|
text?: string;
|
|
16
18
|
variant: import("@mezzanine-ui/core/badge").BadgeDotVariant;
|
|
17
19
|
}, "ref"> | Omit<Omit<NativeElementPropsWithoutKeyAndRef<"span">, "children"> & {
|
|
18
20
|
children?: import("react").ReactNode;
|
|
19
21
|
count?: never;
|
|
20
22
|
overflowCount?: never;
|
|
23
|
+
size?: never;
|
|
21
24
|
text?: never;
|
|
22
25
|
variant: import("@mezzanine-ui/core/badge").BadgeDotVariant;
|
|
23
26
|
}, "ref"> | Omit<Omit<NativeElementPropsWithoutKeyAndRef<"span">, "children"> & {
|
|
24
27
|
children?: never;
|
|
25
28
|
count?: never;
|
|
26
29
|
overflowCount?: never;
|
|
30
|
+
size?: import("@mezzanine-ui/core/badge").BadgeTextSize;
|
|
27
31
|
text: string;
|
|
28
32
|
variant: import("@mezzanine-ui/core/badge").BadgeTextVariant;
|
|
29
33
|
}, "ref">) & import("react").RefAttributes<HTMLSpanElement>>;
|
package/Badge/Badge.js
CHANGED
|
@@ -14,8 +14,8 @@ const isCountVariant = (variant) => [
|
|
|
14
14
|
* The react component for `mezzanine` badge.
|
|
15
15
|
*/
|
|
16
16
|
const Badge = forwardRef(function Badge(props, ref) {
|
|
17
|
-
const { children, count, className, overflowCount, text, variant, ...rest } = props;
|
|
18
|
-
return (jsxs("div", { className: badgeClasses.container(!!children), children: [children, jsx("span", { ...rest, ref: ref, className: cx(badgeClasses.host, badgeClasses.variant(variant), { [badgeClasses.hide]: isCountVariant(variant) && count === 0 }, className), children: isCountVariant(variant)
|
|
17
|
+
const { children, count, className, overflowCount, size, text, variant, ...rest } = props;
|
|
18
|
+
return (jsxs("div", { className: badgeClasses.container(!!children), children: [children, jsx("span", { ...rest, ref: ref, className: cx(badgeClasses.host, badgeClasses.variant(variant), { [badgeClasses.hide]: isCountVariant(variant) && count === 0 }, size && badgeClasses.size(size), className), children: isCountVariant(variant)
|
|
19
19
|
? overflowCount && count > overflowCount
|
|
20
20
|
? `${overflowCount}+`
|
|
21
21
|
: count
|
package/Badge/typings.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadgeCountVariant, BadgeDotVariant, BadgeTextVariant } from '@mezzanine-ui/core/badge';
|
|
1
|
+
import { BadgeCountVariant, BadgeDotVariant, BadgeTextSize, BadgeTextVariant } from '@mezzanine-ui/core/badge';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { NativeElementPropsWithoutKeyAndRef } from 'react/src/utils/jsx-types';
|
|
4
4
|
export type BadgeProps = Omit<NativeElementPropsWithoutKeyAndRef<'span'>, 'children'> & BadgeVariantProps;
|
|
@@ -21,6 +21,11 @@ type BadgeCountProps = {
|
|
|
21
21
|
* it will show overflowCount suffixed with a "+".
|
|
22
22
|
*/
|
|
23
23
|
overflowCount?: number;
|
|
24
|
+
/**
|
|
25
|
+
* ONLY AVAILABLE FOR DOT WITH TEXT BADGE.
|
|
26
|
+
* Controls the size of the dot and text.
|
|
27
|
+
*/
|
|
28
|
+
size?: never;
|
|
24
29
|
/**
|
|
25
30
|
* ONLY AVAILABLE FOR DOT WITH TEXT BADGE.
|
|
26
31
|
* String displayed next to the dot badge.
|
|
@@ -35,6 +40,7 @@ type BadgeTextProps = {
|
|
|
35
40
|
children?: never;
|
|
36
41
|
count?: never;
|
|
37
42
|
overflowCount?: never;
|
|
43
|
+
size?: BadgeTextSize;
|
|
38
44
|
text: string;
|
|
39
45
|
variant: BadgeTextVariant;
|
|
40
46
|
};
|
|
@@ -42,6 +48,11 @@ type BadgeDotWithTextProps = {
|
|
|
42
48
|
children?: never;
|
|
43
49
|
count?: never;
|
|
44
50
|
overflowCount?: never;
|
|
51
|
+
/**
|
|
52
|
+
* Controls the size of the text.
|
|
53
|
+
* @default 'main'
|
|
54
|
+
*/
|
|
55
|
+
size?: BadgeTextSize;
|
|
45
56
|
text?: string;
|
|
46
57
|
variant: BadgeDotVariant;
|
|
47
58
|
};
|
|
@@ -49,6 +60,7 @@ type BadgeDotProps = {
|
|
|
49
60
|
children?: ReactNode;
|
|
50
61
|
count?: never;
|
|
51
62
|
overflowCount?: never;
|
|
63
|
+
size?: never;
|
|
52
64
|
text?: never;
|
|
53
65
|
variant: BadgeDotVariant;
|
|
54
66
|
};
|
package/Calendar/CalendarDays.js
CHANGED
|
@@ -14,10 +14,11 @@ import cx from 'clsx';
|
|
|
14
14
|
* You may use it to compose your own calendar.
|
|
15
15
|
*/
|
|
16
16
|
function CalendarDays(props) {
|
|
17
|
-
const { locale, getCalendarGrid, getDate, getMonth, getNow, isDateIncluded, isSameDate, setDate, setMonth, setHour, setMinute, setSecond, setMillisecond, } = useCalendarContext();
|
|
17
|
+
const { locale, getCalendarGrid, getDate, getMonth, getNow, getWeekends, isDateIncluded, isSameDate, setDate, setMonth, setHour, setMinute, setSecond, setMillisecond, } = useCalendarContext();
|
|
18
18
|
const { className, displayWeekDayLocale = locale, isYearDisabled, isMonthDisabled, isDateDisabled, isDateInRange, onClick: onClickProp, onDateHover, renderAnnotations, referenceDate, value, ...rest } = props;
|
|
19
|
+
const weekends = useMemo(() => getWeekends(displayWeekDayLocale), [getWeekends, displayWeekDayLocale]);
|
|
19
20
|
const daysGrid = useMemo(() => getCalendarGrid(referenceDate, displayWeekDayLocale), [getCalendarGrid, displayWeekDayLocale, referenceDate]);
|
|
20
|
-
return (jsx("div", { ...rest, className: cx(calendarClasses.board, className), children: jsxs("div", { className: calendarClasses.daysGrid, children: [jsx(CalendarDayOfWeek, { displayWeekDayLocale: displayWeekDayLocale }), daysGrid.map((week, index) => (jsx("div", { className: calendarClasses.row, children: week.map((dateNum) => {
|
|
21
|
+
return (jsx("div", { ...rest, className: cx(calendarClasses.board, className), children: jsxs("div", { className: calendarClasses.daysGrid, children: [jsx(CalendarDayOfWeek, { displayWeekDayLocale: displayWeekDayLocale }), daysGrid.map((week, index) => (jsx("div", { className: calendarClasses.row, children: week.map((dateNum, dayIndex) => {
|
|
21
22
|
const isPrevMonth = index === 0 && dateNum > 7;
|
|
22
23
|
const isNextMonth = index > 3 && dateNum <= 14;
|
|
23
24
|
const thisMonth = getMonth(referenceDate);
|
|
@@ -66,7 +67,7 @@ function CalendarDays(props) {
|
|
|
66
67
|
]
|
|
67
68
|
.filter(Boolean)
|
|
68
69
|
.join(', ');
|
|
69
|
-
return (jsx(CalendarCell, { mode: "day", today: isSameDate(date, getNow()),
|
|
70
|
+
return (jsx(CalendarCell, { active: active, disabled: isPrevMonth || isNextMonth, isRangeEnd: inRangeEnd, isRangeStart: inRangeStart, isWeekend: weekends[dayIndex], mode: "day", today: isSameDate(date, getNow()), withAnnotation: Boolean(renderAnnotations), children: jsxs("button", { type: "button", "aria-disabled": disabled, disabled: disabled, "aria-label": ariaLabel, "aria-pressed": active, "aria-current": isToday ? 'date' : undefined, onMouseEnter: onMouseEnter, className: cx(calendarClasses.button, {
|
|
70
71
|
[calendarClasses.buttonInRange]: inRange,
|
|
71
72
|
[calendarClasses.buttonActive]: active,
|
|
72
73
|
[calendarClasses.buttonDisabled]: disabled,
|
|
@@ -13,8 +13,9 @@ import cx from 'clsx';
|
|
|
13
13
|
* You may use it to compose your own calendar.
|
|
14
14
|
*/
|
|
15
15
|
function CalendarWeeks(props) {
|
|
16
|
-
const { locale, getCalendarGrid, getWeek, getDate, getMonth, getNow, isInMonth, isSameDate, isWeekIncluded, setDate, setMonth, setHour, setMinute, setSecond, setMillisecond, getCurrentWeekFirstDate, } = useCalendarContext();
|
|
16
|
+
const { locale, getCalendarGrid, getWeek, getDate, getMonth, getNow, getWeekends, isInMonth, isSameDate, isWeekIncluded, setDate, setMonth, setHour, setMinute, setSecond, setMillisecond, getCurrentWeekFirstDate, } = useCalendarContext();
|
|
17
17
|
const { className, displayWeekDayLocale = locale, isYearDisabled, isMonthDisabled, isWeekDisabled, isWeekInRange, onClick: onClickProp, onWeekHover, referenceDate, value, ...rest } = props;
|
|
18
|
+
const weekends = useMemo(() => getWeekends(displayWeekDayLocale), [getWeekends, displayWeekDayLocale]);
|
|
18
19
|
const daysGrid = useMemo(() => getCalendarGrid(referenceDate, displayWeekDayLocale), [getCalendarGrid, referenceDate, displayWeekDayLocale]);
|
|
19
20
|
// Pre-calculate all weeks data including dates and week first dates
|
|
20
21
|
const weeksData = useMemo(() => {
|
|
@@ -143,8 +144,8 @@ function CalendarWeeks(props) {
|
|
|
143
144
|
isSameDate(currentDate, lastWeekDatesMap.lastWeekDates[6]);
|
|
144
145
|
cellActive = isFirstWeekFirstDate || isLastWeekLastDate;
|
|
145
146
|
}
|
|
146
|
-
return (jsx(CalendarCell, {
|
|
147
|
-
!isInMonth(dates[dateIndex], getMonth(referenceDate)),
|
|
147
|
+
return (jsx(CalendarCell, { active: cellActive, disabled: disabled ||
|
|
148
|
+
!isInMonth(dates[dateIndex], getMonth(referenceDate)), isRangeEnd: isLastWeekLastDate, isRangeStart: isFirstWeekFirstDate, isWeekend: weekends[dateIndex], mode: "week", today: isSameDate(dates[dateIndex], getNow()), children: jsx("div", { className: cx(calendarClasses.button, {
|
|
148
149
|
[calendarClasses.buttonInRange]: weekIncluded,
|
|
149
150
|
[calendarClasses.buttonActive]: cellActive,
|
|
150
151
|
}), style: {
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, useContext, useState, useRef, useCallback, useMemo, createElement } from 'react';
|
|
4
|
+
import { cascaderClasses } from '@mezzanine-ui/core/cascader';
|
|
5
|
+
import { offset } from '@floating-ui/react-dom';
|
|
6
|
+
import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
|
|
7
|
+
import { TransitionGroup } from 'react-transition-group';
|
|
8
|
+
import { useControlValueState } from '../Form/useControlValueState.js';
|
|
9
|
+
import { useDocumentEvents } from '../hooks/useDocumentEvents.js';
|
|
10
|
+
import SelectTrigger from '../Select/SelectTrigger.js';
|
|
11
|
+
import Translate from '../Transition/Translate.js';
|
|
12
|
+
import CascaderPanel from './CascaderPanel.js';
|
|
13
|
+
import { FormControlContext } from '../Form/FormControlContext.js';
|
|
14
|
+
import Popper from '../Popper/Popper.js';
|
|
15
|
+
import cx from 'clsx';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Walks the options tree using ids from `value` and returns a new activePath
|
|
19
|
+
* whose items carry proper `children` references from the tree.
|
|
20
|
+
* Items in `value` may omit `children`, so we cannot rely on them directly
|
|
21
|
+
* for panel expansion.
|
|
22
|
+
*/
|
|
23
|
+
function resolveActivePath(options, value) {
|
|
24
|
+
const result = [];
|
|
25
|
+
let currentOptions = options;
|
|
26
|
+
for (const selectedItem of value) {
|
|
27
|
+
const found = currentOptions.find((o) => o.id === selectedItem.id);
|
|
28
|
+
if (!found)
|
|
29
|
+
break;
|
|
30
|
+
result.push(found);
|
|
31
|
+
if (found.children && found.children.length > 0) {
|
|
32
|
+
currentOptions = found.children;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const Cascader = forwardRef(function Cascader(props, ref) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const { disabled: disabledFromFormControl, fullWidth: fullWidthFromFormControl, required: requiredFromFormControl, severity, } = useContext(FormControlContext) || {};
|
|
43
|
+
const { className, clearable = false, defaultValue, disabled = disabledFromFormControl || false, dropdownZIndex, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, globalPortal = true, menuMaxHeight, onBlur, onChange: onChangeProp, onFocus, options, placeholder = '', readOnly = false, required = requiredFromFormControl || false, size, value: valueProp, } = props;
|
|
44
|
+
const [open, setOpen] = useState(false);
|
|
45
|
+
const [activePath, setActivePath] = useState([]);
|
|
46
|
+
const [keyboardFocusedIndex, setKeyboardFocusedIndex] = useState(-1);
|
|
47
|
+
const [value, setValue] = useControlValueState({
|
|
48
|
+
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : [],
|
|
49
|
+
value: valueProp,
|
|
50
|
+
});
|
|
51
|
+
const anchorRef = useRef(null);
|
|
52
|
+
const dropdownRef = useRef(null);
|
|
53
|
+
const handleOpen = useCallback(() => {
|
|
54
|
+
if (readOnly || disabled)
|
|
55
|
+
return;
|
|
56
|
+
setActivePath(resolveActivePath(options, value));
|
|
57
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus();
|
|
58
|
+
setOpen(true);
|
|
59
|
+
}, [disabled, onFocus, options, readOnly, value]);
|
|
60
|
+
const handleClose = useCallback(() => {
|
|
61
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur();
|
|
62
|
+
setOpen(false);
|
|
63
|
+
setKeyboardFocusedIndex(-1);
|
|
64
|
+
}, [onBlur]);
|
|
65
|
+
const handleClear = useCallback((event) => {
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
setValue([]);
|
|
68
|
+
onChangeProp === null || onChangeProp === void 0 ? void 0 : onChangeProp([]);
|
|
69
|
+
if (open) {
|
|
70
|
+
handleClose();
|
|
71
|
+
}
|
|
72
|
+
}, [handleClose, onChangeProp, open, setValue]);
|
|
73
|
+
const handleItemSelect = useCallback((panelIndex, option, isLeaf) => {
|
|
74
|
+
const newActivePath = [...activePath.slice(0, panelIndex), option];
|
|
75
|
+
setActivePath(newActivePath);
|
|
76
|
+
if (isLeaf) {
|
|
77
|
+
setValue(newActivePath);
|
|
78
|
+
onChangeProp === null || onChangeProp === void 0 ? void 0 : onChangeProp(newActivePath);
|
|
79
|
+
handleClose();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
setKeyboardFocusedIndex(-1);
|
|
83
|
+
}
|
|
84
|
+
}, [activePath, handleClose, onChangeProp, setValue]);
|
|
85
|
+
const panels = useMemo(() => {
|
|
86
|
+
const result = [options];
|
|
87
|
+
for (const activeOption of activePath) {
|
|
88
|
+
if (activeOption.children && activeOption.children.length > 0) {
|
|
89
|
+
result.push(activeOption.children);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}, [activePath, options]);
|
|
97
|
+
useDocumentEvents(() => {
|
|
98
|
+
if (!open)
|
|
99
|
+
return;
|
|
100
|
+
const handleClickAway = (event) => {
|
|
101
|
+
const target = event.target;
|
|
102
|
+
if (!target)
|
|
103
|
+
return;
|
|
104
|
+
const anchor = anchorRef.current;
|
|
105
|
+
const dropdown = dropdownRef.current;
|
|
106
|
+
if ((anchor === null || anchor === void 0 ? void 0 : anchor.contains(target)) || (dropdown === null || dropdown === void 0 ? void 0 : dropdown.contains(target)))
|
|
107
|
+
return;
|
|
108
|
+
handleClose();
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
click: handleClickAway,
|
|
112
|
+
touchend: handleClickAway,
|
|
113
|
+
};
|
|
114
|
+
}, [handleClose, open]);
|
|
115
|
+
useDocumentEvents(() => {
|
|
116
|
+
if (!open)
|
|
117
|
+
return;
|
|
118
|
+
const currentPanelOptions = panels[panels.length - 1];
|
|
119
|
+
return {
|
|
120
|
+
keydown(event) {
|
|
121
|
+
switch (event.key) {
|
|
122
|
+
case 'Escape': {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
handleClose();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case 'ArrowDown': {
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
let next = keyboardFocusedIndex + 1;
|
|
130
|
+
while (next < currentPanelOptions.length &&
|
|
131
|
+
currentPanelOptions[next].disabled) {
|
|
132
|
+
next++;
|
|
133
|
+
}
|
|
134
|
+
if (next < currentPanelOptions.length) {
|
|
135
|
+
setKeyboardFocusedIndex(next);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case 'ArrowUp': {
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
let prev = keyboardFocusedIndex === -1
|
|
142
|
+
? currentPanelOptions.length - 1
|
|
143
|
+
: keyboardFocusedIndex - 1;
|
|
144
|
+
while (prev >= 0 && currentPanelOptions[prev].disabled) {
|
|
145
|
+
prev--;
|
|
146
|
+
}
|
|
147
|
+
if (prev >= 0) {
|
|
148
|
+
setKeyboardFocusedIndex(prev);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'ArrowRight':
|
|
153
|
+
case 'Enter':
|
|
154
|
+
case ' ': {
|
|
155
|
+
if (keyboardFocusedIndex === -1)
|
|
156
|
+
return;
|
|
157
|
+
const focusedOption = currentPanelOptions[keyboardFocusedIndex];
|
|
158
|
+
if (!focusedOption || focusedOption.disabled)
|
|
159
|
+
return;
|
|
160
|
+
const isLeaf = !focusedOption.children || focusedOption.children.length === 0;
|
|
161
|
+
if (event.key === 'ArrowRight' && isLeaf)
|
|
162
|
+
return;
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
handleItemSelect(panels.length - 1, focusedOption, isLeaf);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case 'ArrowLeft': {
|
|
168
|
+
event.preventDefault();
|
|
169
|
+
if (activePath.length === 0)
|
|
170
|
+
return;
|
|
171
|
+
const removedItem = activePath[activePath.length - 1];
|
|
172
|
+
const parentPanel = panels[activePath.length - 1];
|
|
173
|
+
const idx = parentPanel.findIndex((o) => o.id === removedItem.id);
|
|
174
|
+
setActivePath(activePath.slice(0, -1));
|
|
175
|
+
setKeyboardFocusedIndex(idx >= 0 ? idx : -1);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}, [
|
|
182
|
+
activePath,
|
|
183
|
+
handleClose,
|
|
184
|
+
handleItemSelect,
|
|
185
|
+
keyboardFocusedIndex,
|
|
186
|
+
open,
|
|
187
|
+
panels,
|
|
188
|
+
]);
|
|
189
|
+
const displayPath = open ? activePath : value;
|
|
190
|
+
const displayString = displayPath.map((o) => o.name).join(' / ');
|
|
191
|
+
const triggerValue = useMemo(() => {
|
|
192
|
+
if (displayPath.length === 0)
|
|
193
|
+
return undefined;
|
|
194
|
+
return { id: 'cascader-value', name: displayString };
|
|
195
|
+
}, [displayPath.length, displayString]);
|
|
196
|
+
const isPartial = open &&
|
|
197
|
+
activePath.length > 0 &&
|
|
198
|
+
!!((_b = (_a = activePath[activePath.length - 1]) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b.length);
|
|
199
|
+
const zIndexMiddleware = useMemo(() => ({
|
|
200
|
+
name: 'zIndex',
|
|
201
|
+
fn: ({ elements }) => {
|
|
202
|
+
const val = dropdownZIndex !== null && dropdownZIndex !== void 0 ? dropdownZIndex : 1;
|
|
203
|
+
const num = typeof val === 'number' ? val : parseInt(val, 10) || val;
|
|
204
|
+
Object.assign(elements.floating.style, { zIndex: num });
|
|
205
|
+
return {};
|
|
206
|
+
},
|
|
207
|
+
}), [dropdownZIndex]);
|
|
208
|
+
const offsetMiddleware = useMemo(() => offset({ mainAxis: 4 }), []);
|
|
209
|
+
const translateProps = useMemo(() => ({
|
|
210
|
+
duration: {
|
|
211
|
+
enter: MOTION_DURATION.moderate,
|
|
212
|
+
exit: MOTION_DURATION.moderate,
|
|
213
|
+
},
|
|
214
|
+
easing: {
|
|
215
|
+
enter: MOTION_EASING.standard,
|
|
216
|
+
exit: MOTION_EASING.standard,
|
|
217
|
+
},
|
|
218
|
+
}), []);
|
|
219
|
+
return (jsxs("div", { ref: ref, className: cx(cascaderClasses.host, fullWidth && cascaderClasses.hostFullWidth), children: [jsx(Popper, { anchor: anchorRef, open: open, disablePortal: !globalPortal, options: {
|
|
220
|
+
placement: 'bottom-start',
|
|
221
|
+
middleware: [offsetMiddleware, zIndexMiddleware],
|
|
222
|
+
}, children: jsx(TransitionGroup, { component: null, children: open && (createElement(Translate, { ...translateProps, from: "bottom", key: "cascader-dropdown", in: true },
|
|
223
|
+
jsx("div", { children: jsx("div", { ref: dropdownRef, className: cascaderClasses.dropdownPanels, children: panels.map((panelOptions, panelIndex) => {
|
|
224
|
+
var _a, _b, _c;
|
|
225
|
+
return (jsx(CascaderPanel, { activeId: (_a = activePath[panelIndex]) === null || _a === void 0 ? void 0 : _a.id, focusedId: panelIndex === panels.length - 1 &&
|
|
226
|
+
keyboardFocusedIndex >= 0
|
|
227
|
+
? (_b = panels[panelIndex][keyboardFocusedIndex]) === null || _b === void 0 ? void 0 : _b.id
|
|
228
|
+
: undefined, maxHeight: menuMaxHeight, onSelect: (option, isLeaf) => handleItemSelect(panelIndex, option, isLeaf), options: panelOptions, selectedId: value.length > 0 && panelIndex === value.length - 1
|
|
229
|
+
? (_c = value[panelIndex]) === null || _c === void 0 ? void 0 : _c.id
|
|
230
|
+
: undefined }, panelIndex));
|
|
231
|
+
}) }) }))) }) }), jsx(SelectTrigger, { ref: anchorRef, active: open, className: cx(className, isPartial && cascaderClasses.triggerPartial), disabled: disabled, fullWidth: fullWidth, inputProps: {
|
|
232
|
+
onKeyDown: (e) => {
|
|
233
|
+
if (!open && (e.key === ' ' || e.key === 'Enter')) {
|
|
234
|
+
e.preventDefault();
|
|
235
|
+
handleOpen();
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
}, isForceClearable: clearable && !disabled && !readOnly && value.length > 0, mode: "single", onClick: open ? handleClose : handleOpen, onClear: handleClear, placeholder: placeholder, readOnly: readOnly, required: required, size: size, type: error ? 'error' : 'default', value: triggerValue })] }));
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
export { Cascader as default };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CascaderOption } from './typings';
|
|
2
|
+
export interface CascaderPanelProps {
|
|
3
|
+
/**
|
|
4
|
+
* The id of the currently active (navigating) option.
|
|
5
|
+
*/
|
|
6
|
+
activeId?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The id of the keyboard-focused option in this panel.
|
|
9
|
+
*/
|
|
10
|
+
focusedId?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The max height for the panel.
|
|
13
|
+
*/
|
|
14
|
+
maxHeight?: number | string;
|
|
15
|
+
/**
|
|
16
|
+
* Called when an option is clicked.
|
|
17
|
+
* `isLeaf` is true if the option has no children.
|
|
18
|
+
*/
|
|
19
|
+
onSelect: (option: CascaderOption, isLeaf: boolean) => void;
|
|
20
|
+
/**
|
|
21
|
+
* The options to render in this panel.
|
|
22
|
+
*/
|
|
23
|
+
options: CascaderOption[];
|
|
24
|
+
/**
|
|
25
|
+
* The id of the confirmed selected option at this level.
|
|
26
|
+
*/
|
|
27
|
+
selectedId?: string;
|
|
28
|
+
}
|
|
29
|
+
export default function CascaderPanel({ activeId, focusedId, maxHeight, onSelect, options, selectedId, }: CascaderPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import { cascaderClasses } from '@mezzanine-ui/core/cascader';
|
|
4
|
+
import { CheckedIcon, CaretRightIcon } from '@mezzanine-ui/icons';
|
|
5
|
+
import Icon from '../Icon/Icon.js';
|
|
6
|
+
import cx from 'clsx';
|
|
7
|
+
|
|
8
|
+
function CascaderPanel({ activeId, focusedId, maxHeight, onSelect, options, selectedId, }) {
|
|
9
|
+
const toItemId = (optionId) => `mzn-cascader-option-${optionId}`;
|
|
10
|
+
return (jsx("div", { className: cascaderClasses.panel, style: maxHeight ? { maxHeight } : undefined, children: jsx("ul", { "aria-activedescendant": focusedId ? toItemId(focusedId) : undefined, "aria-label": "Options", role: "listbox", style: { listStyle: 'none', margin: 0, padding: 0 }, tabIndex: -1, children: options.map((option) => {
|
|
11
|
+
const isLeaf = !option.children || option.children.length === 0;
|
|
12
|
+
const isActive = option.id === activeId;
|
|
13
|
+
const isSelected = option.id === selectedId;
|
|
14
|
+
return (jsxs("li", { "aria-disabled": option.disabled || undefined, "aria-expanded": !isLeaf ? isActive : undefined, "aria-selected": isSelected, className: cx(cascaderClasses.item, isActive && cascaderClasses.itemActive, option.disabled && cascaderClasses.itemDisabled, option.id === focusedId && cascaderClasses.itemFocused, isSelected && cascaderClasses.itemSelected), id: toItemId(option.id), onClick: () => {
|
|
15
|
+
if (!option.disabled) {
|
|
16
|
+
onSelect(option, isLeaf);
|
|
17
|
+
}
|
|
18
|
+
}, onKeyDown: (e) => {
|
|
19
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
if (!option.disabled) {
|
|
22
|
+
onSelect(option, isLeaf);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}, role: "option", children: [jsx("span", { className: cascaderClasses.itemLabel, children: option.name }), jsx("span", { className: cascaderClasses.itemAppend, children: isLeaf ? (isSelected && jsx(Icon, { icon: CheckedIcon })) : (jsx(Icon, { icon: CaretRightIcon })) })] }, option.id));
|
|
26
|
+
}) }) }));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { CascaderPanel as default };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default } from './Cascader';
|
|
2
|
+
export type { CascaderProps } from './typings';
|
|
3
|
+
export type { CascaderOption, CascaderSize } from './typings';
|
|
4
|
+
export { default as CascaderPanel } from './CascaderPanel';
|
|
5
|
+
export type { CascaderPanelProps } from './CascaderPanel';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { CascaderSize } from '@mezzanine-ui/core/cascader';
|
|
2
|
+
export type { CascaderSize };
|
|
3
|
+
export interface CascaderOption {
|
|
4
|
+
children?: CascaderOption[];
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CascaderBaseProps {
|
|
10
|
+
/**
|
|
11
|
+
* Additional class name.
|
|
12
|
+
*/
|
|
13
|
+
className?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether to show the clear button when a value is selected.
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
clearable?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the cascader is disabled.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to show in error state.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
error?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the cascader takes full width.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
fullWidth?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The z-index of the dropdown.
|
|
36
|
+
*/
|
|
37
|
+
dropdownZIndex?: number | string;
|
|
38
|
+
/**
|
|
39
|
+
* Whether to enable portal for the dropdown.
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
globalPortal?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The max height of each cascader panel column.
|
|
45
|
+
*/
|
|
46
|
+
menuMaxHeight?: number | string;
|
|
47
|
+
/**
|
|
48
|
+
* Callback fired when the dropdown is closed.
|
|
49
|
+
*/
|
|
50
|
+
onBlur?: () => void;
|
|
51
|
+
/**
|
|
52
|
+
* Callback fired when the dropdown is opened.
|
|
53
|
+
*/
|
|
54
|
+
onFocus?: () => void;
|
|
55
|
+
/**
|
|
56
|
+
* The tree options for the cascader.
|
|
57
|
+
*/
|
|
58
|
+
options: CascaderOption[];
|
|
59
|
+
/**
|
|
60
|
+
* Placeholder text for the trigger input.
|
|
61
|
+
*/
|
|
62
|
+
placeholder?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Whether the cascader is read-only.
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
readOnly?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Whether the cascader is required.
|
|
70
|
+
* @default false
|
|
71
|
+
*/
|
|
72
|
+
required?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* The size of the trigger input.
|
|
75
|
+
* @default 'main'
|
|
76
|
+
*/
|
|
77
|
+
size?: CascaderSize;
|
|
78
|
+
}
|
|
79
|
+
export interface CascaderProps extends CascaderBaseProps {
|
|
80
|
+
/**
|
|
81
|
+
* Uncontrolled default value (array from root to leaf).
|
|
82
|
+
*/
|
|
83
|
+
defaultValue?: CascaderOption[];
|
|
84
|
+
/**
|
|
85
|
+
* Callback fired when the final leaf option is selected.
|
|
86
|
+
*/
|
|
87
|
+
onChange?: (value: CascaderOption[]) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Controlled value (array from root to leaf).
|
|
90
|
+
*/
|
|
91
|
+
value?: CascaderOption[];
|
|
92
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
-
import { DescriptionOrientation } from '@mezzanine-ui/core/description';
|
|
2
|
+
import { DescriptionOrientation, DescriptionSize } from '@mezzanine-ui/core/description';
|
|
3
3
|
import { DescriptionTitleProps } from './DescriptionTitle';
|
|
4
4
|
import { DescriptionContentProps } from './DescriptionContent';
|
|
5
5
|
import { BadgeProps } from '../Badge/typings';
|
|
@@ -21,6 +21,11 @@ export type DescriptionProps = DistributiveOmit<DescriptionTitleProps, 'classNam
|
|
|
21
21
|
* @default 'horizontal'
|
|
22
22
|
*/
|
|
23
23
|
orientation?: DescriptionOrientation;
|
|
24
|
+
/**
|
|
25
|
+
* Controls the text size of the description content
|
|
26
|
+
* @default 'main'
|
|
27
|
+
*/
|
|
28
|
+
size?: DescriptionSize;
|
|
24
29
|
/**
|
|
25
30
|
* title text for description
|
|
26
31
|
*/
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
3
|
-
import { forwardRef } from 'react';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, isValidElement, cloneElement } from 'react';
|
|
4
4
|
import { descriptionClasses } from '@mezzanine-ui/core/description';
|
|
5
5
|
import DescriptionTitle from './DescriptionTitle.js';
|
|
6
|
+
import { DescriptionContext } from './DescriptionContext.js';
|
|
6
7
|
import cx from 'clsx';
|
|
7
8
|
|
|
8
9
|
const Description = forwardRef(function Description(props, ref) {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
var _a;
|
|
11
|
+
const { children, className, orientation = 'horizontal', size = 'main', title, ...rest } = props;
|
|
12
|
+
const injectedChildren = isValidElement(children)
|
|
13
|
+
? cloneElement(children, {
|
|
14
|
+
size: (_a = children.props.size) !== null && _a !== void 0 ? _a : size,
|
|
15
|
+
})
|
|
16
|
+
: children;
|
|
17
|
+
return (jsx(DescriptionContext.Provider, { value: { size }, children: jsxs("div", { className: cx(descriptionClasses.host, descriptionClasses.orientation(orientation), className), ref: ref, children: [jsx(DescriptionTitle, { ...rest, children: title }), injectedChildren] }) }));
|
|
11
18
|
});
|
|
12
19
|
|
|
13
20
|
export { Description as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IconDefinition } from '@mezzanine-ui/icons';
|
|
2
|
-
import {
|
|
2
|
+
import { DescriptionContentVariant, DescriptionSize } from '@mezzanine-ui/core/description';
|
|
3
3
|
interface DescriptionContentBaseProps {
|
|
4
4
|
/**
|
|
5
5
|
* Custom class name for content
|
|
@@ -10,8 +10,9 @@ interface DescriptionContentBaseProps {
|
|
|
10
10
|
*/
|
|
11
11
|
children: string;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Controls the text size of the content. When provided, overrides the size
|
|
14
|
+
* inherited from a parent <Description> component.
|
|
15
|
+
* @default context value or 'main'
|
|
15
16
|
*/
|
|
16
17
|
size?: DescriptionSize;
|
|
17
18
|
/**
|
|
@@ -31,6 +32,11 @@ interface DescriptionContentBaseProps {
|
|
|
31
32
|
interface DescriptionContentWithClickableIcon {
|
|
32
33
|
className?: string;
|
|
33
34
|
children: string;
|
|
35
|
+
/**
|
|
36
|
+
* Controls the text size of the content. When provided, overrides the size
|
|
37
|
+
* inherited from a parent <Description> component.
|
|
38
|
+
* @default context value or 'main'
|
|
39
|
+
*/
|
|
34
40
|
size?: DescriptionSize;
|
|
35
41
|
variant: Extract<DescriptionContentVariant, 'with-icon'>;
|
|
36
42
|
icon: IconDefinition;
|
|
@@ -3,11 +3,14 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { CaretUpIcon, CaretDownIcon } from '@mezzanine-ui/icons';
|
|
5
5
|
import { descriptionClasses } from '@mezzanine-ui/core/description';
|
|
6
|
+
import { useDescriptionContext } from './DescriptionContext.js';
|
|
6
7
|
import Icon from '../Icon/Icon.js';
|
|
7
8
|
import cx from 'clsx';
|
|
8
9
|
|
|
9
10
|
const DescriptionContent = forwardRef(function DescriptionContent(props, ref) {
|
|
10
|
-
const { className, children, icon, onClickIcon, size
|
|
11
|
+
const { className, children, icon, onClickIcon, size: sizeProp, variant = 'normal', } = props;
|
|
12
|
+
const { size: contextSize } = useDescriptionContext();
|
|
13
|
+
const size = sizeProp !== null && sizeProp !== void 0 ? sizeProp : contextSize;
|
|
11
14
|
return (jsxs("span", { className: cx(descriptionClasses.contentHost, descriptionClasses.contentVariant(variant), descriptionClasses.contentSize(size), className), ref: ref, children: [variant === 'trend-up' && (jsx(Icon, { className: descriptionClasses.contentTrendUp, icon: CaretUpIcon, size: 16 })), variant === 'trend-down' && (jsx(Icon, { className: descriptionClasses.contentTrendDown, icon: CaretDownIcon, size: 16 })), children, variant === 'with-icon' && icon && (jsx(Icon, { className: descriptionClasses.contentIcon, icon: icon, size: 16, onClick: onClickIcon }))] }));
|
|
12
15
|
});
|
|
13
16
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DescriptionSize } from '@mezzanine-ui/core/description';
|
|
2
|
+
export interface DescriptionContextValue {
|
|
3
|
+
size: DescriptionSize;
|
|
4
|
+
}
|
|
5
|
+
export declare const DescriptionContext: import("react").Context<DescriptionContextValue>;
|
|
6
|
+
export declare const useDescriptionContext: () => DescriptionContextValue;
|