@mezzanine-ui/react 1.3.1 → 1.4.1
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/Dropdown/Dropdown.d.ts +6 -2
- package/Dropdown/Dropdown.js +13 -4
- package/Navigation/NavigationOption.js +12 -7
- package/Popper/Popper.d.ts +7 -0
- package/Popper/Popper.js +6 -1
- package/Select/Select.d.ts +8 -0
- package/Select/Select.js +2 -2
- package/Select/SelectTrigger.js +2 -4
- package/package.json +1 -1
package/Dropdown/Dropdown.d.ts
CHANGED
|
@@ -146,8 +146,12 @@ export interface DropdownProps extends DropdownItemSharedProps {
|
|
|
146
146
|
sameWidth?: boolean;
|
|
147
147
|
/**
|
|
148
148
|
* Whether to enable floating-ui `flip` middleware.
|
|
149
|
-
* When `true`, the dropdown automatically flips to the opposite side
|
|
150
|
-
* (e.g. `bottom-start` → `top-start`) if it would overflow the
|
|
149
|
+
* When `true`, the dropdown automatically flips to the opposite side along
|
|
150
|
+
* the main axis (e.g. `bottom-start` → `top-start`) if it would overflow the
|
|
151
|
+
* viewport, and the enter transition slides from the resolved side. The flip
|
|
152
|
+
* is main-axis only (no `shift`/`crossAxis`), so a `sameWidth` menu stays
|
|
153
|
+
* horizontally aligned with its anchor — matching the `InputTriggerPopper`
|
|
154
|
+
* behavior used by the DatePicker/TimePicker menus.
|
|
151
155
|
* Off by default to preserve existing placement behavior across consumers.
|
|
152
156
|
* @default false
|
|
153
157
|
*/
|
package/Dropdown/Dropdown.js
CHANGED
|
@@ -180,6 +180,9 @@ function Dropdown(props) {
|
|
|
180
180
|
}
|
|
181
181
|
return 'bottom';
|
|
182
182
|
}, [inputPosition, placement]);
|
|
183
|
+
// Tracks the placement actually resolved by floating-ui. Only meaningful when
|
|
184
|
+
// `flip` is enabled, where it may differ from `popoverPlacement` after a flip.
|
|
185
|
+
const [resolvedPlacement, setResolvedPlacement] = useState(popoverPlacement);
|
|
183
186
|
const customWidthMiddleware = useMemo(() => {
|
|
184
187
|
if (!customWidth) {
|
|
185
188
|
return null;
|
|
@@ -214,8 +217,11 @@ function Dropdown(props) {
|
|
|
214
217
|
const flipMiddleware = useMemo(() => {
|
|
215
218
|
if (!flip$1)
|
|
216
219
|
return null;
|
|
220
|
+
// Main-axis flip only (bottom <-> top), aligned with `InputTriggerPopper`.
|
|
221
|
+
// No `shift`/`crossAxis` so a sameWidth menu keeps its horizontal alignment
|
|
222
|
+
// with the anchor instead of being pushed sideways.
|
|
217
223
|
return flip({
|
|
218
|
-
|
|
224
|
+
fallbackAxisSideDirection: 'end',
|
|
219
225
|
padding: 8,
|
|
220
226
|
});
|
|
221
227
|
}, [flip$1]);
|
|
@@ -244,9 +250,12 @@ function Dropdown(props) {
|
|
|
244
250
|
if (isInline) {
|
|
245
251
|
return 'bottom';
|
|
246
252
|
}
|
|
247
|
-
|
|
253
|
+
// When flip is enabled, follow the placement resolved by floating-ui so the
|
|
254
|
+
// enter transition slides from the correct side after a flip. Otherwise keep
|
|
255
|
+
// the static placement to preserve existing behavior for non-flip consumers.
|
|
256
|
+
const placementBase = (flip$1 ? resolvedPlacement : popoverPlacement).split('-')[0];
|
|
248
257
|
return placementBase === 'top' ? 'top' : 'bottom';
|
|
249
|
-
}, [isInline, popoverPlacement]);
|
|
258
|
+
}, [flip$1, isInline, popoverPlacement, resolvedPlacement]);
|
|
250
259
|
const setOpen = useCallback((next) => {
|
|
251
260
|
const nextValue = typeof next === 'function'
|
|
252
261
|
? next(isOpen)
|
|
@@ -554,7 +563,7 @@ function Dropdown(props) {
|
|
|
554
563
|
}, [isInline, isOpen, setOpen]);
|
|
555
564
|
return (jsxs("div", { id: id, ref: containerRef, className: cx(dropdownClasses.root, dropdownClasses.inputPosition(inputPosition)), children: [isInline && (jsxs(TransitionGroup, { component: null, children: [!isOpen && inlineTriggerElement && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-trigger", in: true },
|
|
556
565
|
jsx("div", { children: inlineTriggerElement }))), isOpen && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-list", in: true },
|
|
557
|
-
jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, className: dropdownClasses.popperWithPortal, controllerRef: popperControllerRef, open: isOpen, disablePortal: !globalPortal, options: {
|
|
566
|
+
jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, className: dropdownClasses.popperWithPortal, controllerRef: popperControllerRef, onPlacementChange: setResolvedPlacement, open: isOpen, disablePortal: !globalPortal, options: {
|
|
558
567
|
placement: popoverPlacement,
|
|
559
568
|
middleware: [
|
|
560
569
|
offsetMiddleware,
|
|
@@ -24,9 +24,6 @@ const NavigationOption = forwardRef((props, ref) => {
|
|
|
24
24
|
const currentKey = id || title || href || uuid;
|
|
25
25
|
const currentPath = useMemo(() => [...parentPath, currentKey], [parentPath, currentKey]);
|
|
26
26
|
const currentPathKey = currentPath.join('::');
|
|
27
|
-
const Component = href && !children
|
|
28
|
-
? ((_a = anchorComponent !== null && anchorComponent !== void 0 ? anchorComponent : optionsAnchorComponent) !== null && _a !== void 0 ? _a : 'a')
|
|
29
|
-
: 'div';
|
|
30
27
|
const flattenedChildren = useMemo(() => flattenChildren(children), [children]);
|
|
31
28
|
const { badge, items } = useMemo(() => {
|
|
32
29
|
let badgeComponent = null;
|
|
@@ -49,6 +46,12 @@ const NavigationOption = forwardRef((props, ref) => {
|
|
|
49
46
|
});
|
|
50
47
|
return { badge: badgeComponent, items };
|
|
51
48
|
}, [flattenedChildren]);
|
|
49
|
+
// Group vs leaf is decided by the presence of real sub-options, not by
|
|
50
|
+
// raw `children` — a lone `Badge` child is rendered inline on a leaf.
|
|
51
|
+
const hasSubOptions = items.length > 0;
|
|
52
|
+
const Component = href && !hasSubOptions
|
|
53
|
+
? ((_a = anchorComponent !== null && anchorComponent !== void 0 ? anchorComponent : optionsAnchorComponent) !== null && _a !== void 0 ? _a : 'a')
|
|
54
|
+
: 'div';
|
|
52
55
|
// Default open if current path is activated
|
|
53
56
|
useEffect(() => {
|
|
54
57
|
if (activatedPathKey === currentPathKey ||
|
|
@@ -83,7 +86,7 @@ const NavigationOption = forwardRef((props, ref) => {
|
|
|
83
86
|
resizeObserver.disconnect();
|
|
84
87
|
};
|
|
85
88
|
}, [title]);
|
|
86
|
-
return (jsxs("li", { ...rest, ref: ref, className: cx(navigationOptionClasses.host, open && navigationOptionClasses.open, !
|
|
89
|
+
return (jsxs("li", { ...rest, ref: ref, className: cx(navigationOptionClasses.host, open && navigationOptionClasses.open, !hasSubOptions && navigationOptionClasses.basic, (active !== null && active !== void 0 ? active : (activatedPath === null || activatedPath === void 0 ? void 0 : activatedPath[currentLevel - 1]) === currentKey) &&
|
|
87
90
|
navigationOptionClasses.active, collapsed && navigationOptionClasses.collapsed, (collapsed && collapsedHiddenKeys.has(currentKey)) ||
|
|
88
91
|
(!collapsed && !filter)
|
|
89
92
|
? navigationOptionClasses.hidden
|
|
@@ -95,16 +98,18 @@ const NavigationOption = forwardRef((props, ref) => {
|
|
|
95
98
|
if (collapsed) {
|
|
96
99
|
handleCollapseChange(false);
|
|
97
100
|
}
|
|
98
|
-
if (!
|
|
101
|
+
if (!hasSubOptions)
|
|
99
102
|
setActivatedPath(currentPath);
|
|
100
103
|
}, onKeyDown: (e) => {
|
|
101
104
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
102
105
|
e.preventDefault();
|
|
103
106
|
setOpen(!open);
|
|
104
|
-
if (!
|
|
107
|
+
if (!hasSubOptions)
|
|
105
108
|
setActivatedPath(currentPath);
|
|
106
109
|
}
|
|
107
|
-
}, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, ref: tooltipChildRef, role: "menuitem", tabIndex: 0, children: [icon && jsx(Icon, { className: navigationOptionClasses.icon, icon: icon }), jsx("span", { className: navigationOptionClasses.titleWrapper, children: jsx(Fade, { ref: titleRef, in: collapsed === false || !icon, children: jsx("span", { className: navigationOptionClasses.title, children: collapsed && !icon
|
|
110
|
+
}, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, ref: tooltipChildRef, role: "menuitem", tabIndex: 0, children: [icon && jsx(Icon, { className: navigationOptionClasses.icon, icon: icon }), jsx("span", { className: navigationOptionClasses.titleWrapper, children: jsx(Fade, { ref: titleRef, in: collapsed === false || !icon, children: jsx("span", { className: navigationOptionClasses.title, children: collapsed && !icon
|
|
111
|
+
? Array.from(title).slice(0, 2).join('')
|
|
112
|
+
: title }) }) }), badge, hasSubOptions && (jsx(Icon, { className: navigationOptionClasses.toggleIcon, icon: GroupToggleIcon }))] })) }), hasSubOptions && !collapsed && (jsx(Collapse, { lazyMount: true, className: cx(navigationOptionClasses.childrenWrapper), in: open, children: jsx(NavigationOptionLevelContext.Provider, { value: {
|
|
108
113
|
level: currentLevel,
|
|
109
114
|
path: currentPath,
|
|
110
115
|
}, children: jsx("ul", { className: navigationOptionClasses.group, children: items }) }) }))] }));
|
package/Popper/Popper.d.ts
CHANGED
|
@@ -23,6 +23,13 @@ export interface PopperProps extends Pick<PortalProps, 'container' | 'disablePor
|
|
|
23
23
|
* Provide `controllerRef` if you need access to `useFloating` results.
|
|
24
24
|
*/
|
|
25
25
|
controllerRef?: Ref<PopperController>;
|
|
26
|
+
/**
|
|
27
|
+
* Callback fired whenever the resolved placement changes, including when
|
|
28
|
+
* floating-ui middleware (e.g. `flip`) flips the popper to the opposite
|
|
29
|
+
* side. Receives the actual placement after all middleware run, which may
|
|
30
|
+
* differ from `options.placement`.
|
|
31
|
+
*/
|
|
32
|
+
onPlacementChange?: (placement: PopperPlacement) => void;
|
|
26
33
|
/**
|
|
27
34
|
* The portal element will show if open=true
|
|
28
35
|
* @default false
|
package/Popper/Popper.js
CHANGED
|
@@ -10,7 +10,7 @@ import Portal from '../Portal/Portal.js';
|
|
|
10
10
|
|
|
11
11
|
const Popper = forwardRef(function Popper(props, ref) {
|
|
12
12
|
var _a, _b, _c, _d;
|
|
13
|
-
const { anchor, arrow: arrow$1, children, container, controllerRef, disablePortal, open = false, options, style, ...rest } = props;
|
|
13
|
+
const { anchor, arrow: arrow$1, children, container, controllerRef, disablePortal, onPlacementChange, open = false, options, style, ...rest } = props;
|
|
14
14
|
const arrowRef = useRef(null);
|
|
15
15
|
const anchorEl = getElement(anchor);
|
|
16
16
|
const floatingReturn = useFloating({
|
|
@@ -42,6 +42,11 @@ const Popper = forwardRef(function Popper(props, ref) {
|
|
|
42
42
|
update();
|
|
43
43
|
}
|
|
44
44
|
}, [open, update]);
|
|
45
|
+
// Notify consumers of the resolved placement so they can react to
|
|
46
|
+
// middleware-driven flips (e.g. adjusting enter-transition direction).
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
onPlacementChange === null || onPlacementChange === void 0 ? void 0 : onPlacementChange(floatingReturn.placement);
|
|
49
|
+
}, [floatingReturn.placement, onPlacementChange]);
|
|
45
50
|
// 計算箭頭的位置和旋轉角度
|
|
46
51
|
const arrowX = (_b = floatingReturn.middlewareData.arrow) === null || _b === void 0 ? void 0 : _b.x;
|
|
47
52
|
const arrowY = (_c = floatingReturn.middlewareData.arrow) === null || _c === void 0 ? void 0 : _c.y;
|
package/Select/Select.d.ts
CHANGED
|
@@ -54,6 +54,14 @@ export interface SelectBaseProps extends Omit<SelectTriggerProps, 'active' | 'in
|
|
|
54
54
|
* The z-index of the dropdown.
|
|
55
55
|
*/
|
|
56
56
|
dropdownZIndex?: number | string;
|
|
57
|
+
/**
|
|
58
|
+
* Whether to enable floating-ui `flip` middleware for the dropdown menu.
|
|
59
|
+
* When `true`, the menu flips from below to above the input (and back) along
|
|
60
|
+
* the main axis if it would overflow the viewport, keeping its width and
|
|
61
|
+
* horizontal alignment with the input. Forwarded to the underlying `Dropdown`.
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
flip?: boolean;
|
|
57
65
|
/**
|
|
58
66
|
* Whether to enable portal for the dropdown.
|
|
59
67
|
* @default true
|
package/Select/Select.js
CHANGED
|
@@ -45,7 +45,7 @@ import cx from 'clsx';
|
|
|
45
45
|
*/
|
|
46
46
|
const Select = forwardRef(function Select(props, ref) {
|
|
47
47
|
const { disabled: disabledFromFormControl, fullWidth: fullWidthFromFormControl, required: requiredFromFormControl, severity, } = useContext(FormControlContext) || {};
|
|
48
|
-
const { className, clearable = false, defaultValue, disabled = disabledFromFormControl || false, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, inputProps, inputRef, loading = false, loadingPosition = 'bottom', loadingText, menuMaxHeight, mode = 'single', onBlur, onChange: onChangeProp, onClear: onClearProp, onFocus, onLeaveBottom, onReachBottom, onScroll, options: optionsProp, placeholder = '', prefix, readOnly = false, renderValue, required = requiredFromFormControl || false, overflowStrategy, size, suffixActionIcon, type = 'default', value: valueProp, dropdownZIndex, globalPortal = true, } = props;
|
|
48
|
+
const { className, clearable = false, defaultValue, disabled = disabledFromFormControl || false, error = severity === 'error' || false, fullWidth = fullWidthFromFormControl || false, inputProps, inputRef, loading = false, loadingPosition = 'bottom', loadingText, menuMaxHeight, mode = 'single', onBlur, onChange: onChangeProp, onClear: onClearProp, onFocus, onLeaveBottom, onReachBottom, onScroll, options: optionsProp, placeholder = '', prefix, readOnly = false, renderValue, required = requiredFromFormControl || false, overflowStrategy, size, suffixActionIcon, type = 'default', value: valueProp, dropdownZIndex, flip = false, globalPortal = true, } = props;
|
|
49
49
|
const dropdownStatus = loading
|
|
50
50
|
? 'loading'
|
|
51
51
|
: undefined;
|
|
@@ -256,7 +256,7 @@ const Select = forwardRef(function Select(props, ref) {
|
|
|
256
256
|
onChange,
|
|
257
257
|
value,
|
|
258
258
|
}), [onChange, value]);
|
|
259
|
-
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, globalPortal: globalPortal, loadingPosition: loadingPosition, loadingText: loadingText, maxHeight: menuMaxHeight, mode: mode, onLeaveBottom: onLeaveBottom, onReachBottom: onReachBottom, onScroll: onScroll, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: readOnly ? false : open, options: options, sameWidth: true, status: dropdownStatus, type: dropdownType, value: dropdownValue, zIndex: dropdownZIndex, 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, overflowStrategy: overflowStrategy, size: size, suffixActionIcon: suffixActionIcon, value: value === null ? undefined : value, placeholder: getPlaceholder() }) }) }) }));
|
|
259
|
+
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, flip: flip, globalPortal: globalPortal, loadingPosition: loadingPosition, loadingText: loadingText, maxHeight: menuMaxHeight, mode: mode, onLeaveBottom: onLeaveBottom, onReachBottom: onReachBottom, onScroll: onScroll, onSelect: handleDropdownSelect, onVisibilityChange: handleVisibilityChange, open: readOnly ? false : open, options: options, sameWidth: true, status: dropdownStatus, type: dropdownType, value: dropdownValue, zIndex: dropdownZIndex, 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, overflowStrategy: overflowStrategy, size: size, suffixActionIcon: suffixActionIcon, value: value === null ? undefined : value, placeholder: getPlaceholder() }) }) }) }));
|
|
260
260
|
});
|
|
261
261
|
|
|
262
262
|
export { Select as default };
|
package/Select/SelectTrigger.js
CHANGED
|
@@ -11,7 +11,7 @@ import cx from 'clsx';
|
|
|
11
11
|
const isMultipleSelection = (props) => props.mode === 'multiple';
|
|
12
12
|
function SelectTriggerComponent(props) {
|
|
13
13
|
var _a, _b, _c;
|
|
14
|
-
const { active, className, clearable: clearableProp = false, disabled, forceHideSuffixActionIcon, inputProps, innerRef, inputRef, isForceClearable = false, mode = 'single', onTagClose, overflowStrategy = 'counter', placeholder, readOnly, required, searchText, size = 'main', showTextInputAfterTags = false, suffixAction, suffixActionIcon: suffixActionIconProp, type = 'default', onClick, ...restTextFieldProps } = props;
|
|
14
|
+
const { active, className, clearable: clearableProp = false, disabled, error, forceHideSuffixActionIcon, inputProps, innerRef, inputRef, isForceClearable = false, mode = 'single', onTagClose, overflowStrategy = 'counter', placeholder, readOnly, required, searchText, size = 'main', showTextInputAfterTags = false, suffixAction, suffixActionIcon: suffixActionIconProp, type = 'default', onClick, ...restTextFieldProps } = props;
|
|
15
15
|
const renderValueProp = 'renderValue' in props ? props.renderValue : undefined;
|
|
16
16
|
// Exclude renderValue to avoid leaking unknown props to DOM.
|
|
17
17
|
const sanitizedTextFieldProps = (() => {
|
|
@@ -60,9 +60,7 @@ function SelectTriggerComponent(props) {
|
|
|
60
60
|
return (jsxs(TextField, { ref: innerRef, ...interactiveProps, ...sanitizedTextFieldProps, onClick: onClick, active: active, className: cx(selectClasses.trigger, selectClasses.triggerMode(mode), selectClasses.triggerSelected(Array.isArray(props.value) ? (_a = props.value) === null || _a === void 0 ? void 0 : _a.length : props.value), {
|
|
61
61
|
[selectClasses.triggerReadOnly]: readOnly,
|
|
62
62
|
[selectClasses.triggerDisabled]: disabled,
|
|
63
|
-
}, className), error: type === 'error', clearable: shouldEnableClearable, forceShowClearable: shouldEnableClearable, size: size, suffix: forceHideSuffixActionIcon ? undefined : suffixActionIcon, children: [jsx("input", { ...inputProps, ref: inputRef, "aria-autocomplete": "list", "aria-haspopup": "listbox", autoComplete: "off", className: cx(selectClasses.triggerInput, inputProps === null || inputProps === void 0 ? void 0 : inputProps.className), disabled: disabled, placeholder: placeholder, readOnly: (_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.readOnly) !== null && _b !== void 0 ? _b : true, required: required, type: "text", value: renderValue() }), isMultipleSelection(props) && ((_c = props.value) === null || _c === void 0 ? void 0 : _c.length)
|
|
64
|
-
? (jsx(SelectTriggerTags, { disabled: disabled, overflowStrategy: overflowStrategy, inputProps: inputProps, inputRef: inputRef, onTagClose: onTagClose, readOnly: readOnly, required: required, searchText: searchText, size: size, showTextInputAfterTags: showTextInputAfterTags, value: props.value }))
|
|
65
|
-
: null] }));
|
|
63
|
+
}, className), error: error || type === 'error', clearable: shouldEnableClearable, forceShowClearable: shouldEnableClearable, size: size, suffix: forceHideSuffixActionIcon ? undefined : suffixActionIcon, children: [jsx("input", { ...inputProps, ref: inputRef, "aria-autocomplete": "list", "aria-haspopup": "listbox", autoComplete: "off", className: cx(selectClasses.triggerInput, inputProps === null || inputProps === void 0 ? void 0 : inputProps.className), disabled: disabled, placeholder: placeholder, readOnly: (_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.readOnly) !== null && _b !== void 0 ? _b : true, required: required, type: "text", value: renderValue() }), isMultipleSelection(props) && ((_c = props.value) === null || _c === void 0 ? void 0 : _c.length) ? (jsx(SelectTriggerTags, { disabled: disabled, overflowStrategy: overflowStrategy, inputProps: inputProps, inputRef: inputRef, onTagClose: onTagClose, readOnly: readOnly, required: required, searchText: searchText, size: size, showTextInputAfterTags: showTextInputAfterTags, value: props.value })) : null] }));
|
|
66
64
|
}
|
|
67
65
|
const SelectTrigger = forwardRef((props, ref) => {
|
|
68
66
|
if (props.mode === 'multiple') {
|