Skip to content

Commit a94d4c0

Browse files
committed
feat(ui): optimize auto position
1 parent 7668c82 commit a94d4c0

21 files changed

Lines changed: 153 additions & 126 deletions

File tree

packages/ui/src/components/_date-input/DateInput.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
134134
setPopupPositionStyle({
135135
top,
136136
left,
137-
maxWidth: window.innerWidth - left - 20,
138137
});
139138
setTransformOrigin(transformOrigin);
140139
}

packages/ui/src/components/auto-complete/AutoComplete.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import ReactDOM from 'react-dom';
99
import { useElement, useEvent, useEventCallback, useResize } from '@react-devui/hooks';
1010
import { LoadingOutlined } from '@react-devui/icons';
1111
import { findNested, getClassName, getOriginalSize, getVerticalSidePosition } from '@react-devui/utils';
12+
import { POSITION_CONFIG } from '@react-devui/utils/position/config';
1213

1314
import { usePrefixConfig, useComponentConfig, useTranslation, useMaxIndex, useDValue, useLayout } from '../../hooks';
1415
import { registerComponentMate, TTANSITION_DURING_POPUP } from '../../utils';
@@ -104,15 +105,20 @@ function AutoComplete<T extends DAutoCompleteItem & { children?: T[] }>(
104105
if (visible) {
105106
const popupEl = popupRef.current;
106107
if (boxEl && popupEl) {
107-
const width = boxEl.getBoundingClientRect().width;
108-
const { height } = getOriginalSize(popupEl);
109-
const { top, left, transformOrigin } = getVerticalSidePosition(boxEl, { width, height }, 'bottom-left', 8);
108+
const boxWidth = boxEl.getBoundingClientRect().width;
109+
const minWidth = Math.min(boxWidth, window.innerWidth - POSITION_CONFIG.space * 2);
110+
const { width, height } = getOriginalSize(popupEl);
111+
const { top, left, transformOrigin } = getVerticalSidePosition(
112+
boxEl,
113+
{ width: Math.max(width, minWidth), height },
114+
'bottom-left',
115+
8
116+
);
110117

111118
setPopupPositionStyle({
112119
top,
113120
left,
114-
minWidth: width,
115-
maxWidth: window.innerWidth - left - 20,
121+
minWidth,
116122
});
117123
setTransformOrigin(transformOrigin);
118124
}

packages/ui/src/components/cascader/Cascader.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -259,21 +259,6 @@ function Cascader<V extends DId, T extends DCascaderItem<V> & { children?: T[] }
259259
}
260260
};
261261

262-
const updatePosition = useCallback((boxEl: HTMLElement, popupEl: HTMLElement) => {
263-
const width = boxEl.getBoundingClientRect().width;
264-
const { height } = getOriginalSize(popupEl);
265-
const { top, left, transformOrigin } = getVerticalSidePosition(boxEl, { width, height }, 'bottom-left', 8);
266-
267-
return {
268-
position: {
269-
top,
270-
left,
271-
maxWidth: window.innerWidth - left - 20,
272-
},
273-
transformOrigin,
274-
};
275-
}, []);
276-
277262
const [selectedNode, suffixNode, selectedLabel] = (() => {
278263
let selectedNode: React.ReactNode = null;
279264
let suffixNode: React.ReactNode = null;
@@ -393,7 +378,18 @@ function Cascader<V extends DId, T extends DCascaderItem<V> & { children?: T[] }
393378
},
394379
}}
395380
dInputRef={dInputRef}
396-
dUpdatePosition={updatePosition}
381+
dUpdatePosition={(boxEl, popupEl) => {
382+
const { width, height } = getOriginalSize(popupEl);
383+
const { top, left, transformOrigin } = getVerticalSidePosition(boxEl, { width, height }, 'bottom-left', 8);
384+
385+
return {
386+
position: {
387+
top,
388+
left,
389+
},
390+
transformOrigin,
391+
};
392+
}}
397393
afterVisibleChange={afterVisibleChange}
398394
onFocusVisibleChange={setFocusVisible}
399395
onClear={handleClear}

packages/ui/src/components/dropdown/Sub.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useRef, useState } from 'react';
1+
import { useRef, useState } from 'react';
22

33
import { useElement, useEventListener } from '@react-devui/hooks';
44
import { RightOutlined } from '@react-devui/icons';
@@ -68,7 +68,7 @@ export function DSub(props: DSubProps): JSX.Element | null {
6868
left: -9999,
6969
});
7070
const [transformOrigin, setTransformOrigin] = useState<string>();
71-
const updatePosition = useCallback(() => {
71+
const updatePosition = () => {
7272
if (ulRef.current && liRef.current) {
7373
const { width, height } = getOriginalSize(ulRef.current);
7474
const { top, left, transformOrigin } = getHorizontalSidePosition(liRef.current, { width, height }, 'right', 10);
@@ -78,7 +78,7 @@ export function DSub(props: DSubProps): JSX.Element | null {
7878
});
7979
setTransformOrigin(transformOrigin);
8080
}
81-
}, []);
81+
};
8282
useEventListener(dEventId, updatePosition);
8383

8484
const maxZIndex = useMaxIndex(dPopupVisible);

packages/ui/src/components/menu/Menu.tsx

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -460,46 +460,64 @@ function Menu<ID extends DId, T extends DMenuItem<ID> & { children?: T[] }>(
460460
>
461461
{(navRef, collapseStyle) => (
462462
<DFocusVisible onFocusVisibleChange={setFocusVisible}>
463-
{({ fvOnFocus, fvOnBlur, fvOnKeyDown }) => (
464-
// eslint-disable-next-line jsx-a11y/aria-activedescendant-has-tabindex
465-
<nav
466-
{...restProps}
467-
ref={navRef}
468-
className={getClassName(restProps.className, `${dPrefix}menu`, {
469-
[`${dPrefix}menu--horizontal`]: dMode === 'horizontal',
470-
})}
471-
style={{
472-
...restProps.style,
473-
...collapseStyle,
474-
}}
475-
tabIndex={restProps.tabIndex ?? 0}
476-
role={restProps.role ?? 'menubar'}
477-
aria-orientation={restProps['aria-orientation'] ?? (dMode === 'horizontal' ? 'horizontal' : 'vertical')}
478-
aria-activedescendant={restProps['aria-activedescendant'] ?? (isUndefined(focusId) ? undefined : getItemId(focusId))}
479-
onFocus={(e) => {
480-
restProps.onFocus?.(e);
481-
fvOnFocus(e);
482-
483-
setIsFocus(true);
484-
initFocus();
485-
}}
486-
onBlur={(e) => {
487-
restProps.onBlur?.(e);
488-
fvOnBlur(e);
489-
490-
setIsFocus(false);
491-
setPopupIds([]);
492-
}}
493-
onKeyDown={(e) => {
494-
restProps.onKeyDown?.(e);
495-
fvOnKeyDown(e);
496-
497-
handleKeyDown?.(e);
498-
}}
499-
>
500-
{nodes}
501-
</nav>
502-
)}
463+
{({ fvOnFocus, fvOnBlur, fvOnKeyDown }) => {
464+
const preventBlur: React.MouseEventHandler = (e) => {
465+
if (e.target !== navRef.current && e.button === 0) {
466+
e.preventDefault();
467+
}
468+
};
469+
470+
return (
471+
// eslint-disable-next-line jsx-a11y/aria-activedescendant-has-tabindex
472+
<nav
473+
{...restProps}
474+
ref={navRef}
475+
className={getClassName(restProps.className, `${dPrefix}menu`, {
476+
[`${dPrefix}menu--horizontal`]: dMode === 'horizontal',
477+
})}
478+
style={{
479+
...restProps.style,
480+
...collapseStyle,
481+
}}
482+
tabIndex={restProps.tabIndex ?? 0}
483+
role={restProps.role ?? 'menubar'}
484+
aria-orientation={restProps['aria-orientation'] ?? (dMode === 'horizontal' ? 'horizontal' : 'vertical')}
485+
aria-activedescendant={restProps['aria-activedescendant'] ?? (isUndefined(focusId) ? undefined : getItemId(focusId))}
486+
onFocus={(e) => {
487+
restProps.onFocus?.(e);
488+
fvOnFocus(e);
489+
490+
setIsFocus(true);
491+
initFocus();
492+
}}
493+
onBlur={(e) => {
494+
restProps.onBlur?.(e);
495+
fvOnBlur(e);
496+
497+
setIsFocus(false);
498+
setPopupIds([]);
499+
}}
500+
onKeyDown={(e) => {
501+
restProps.onKeyDown?.(e);
502+
fvOnKeyDown(e);
503+
504+
handleKeyDown?.(e);
505+
}}
506+
onMouseDown={(e) => {
507+
restProps.onMouseDown?.(e);
508+
509+
preventBlur(e);
510+
}}
511+
onMouseUp={(e) => {
512+
restProps.onMouseUp?.(e);
513+
514+
preventBlur(e);
515+
}}
516+
>
517+
{nodes}
518+
</nav>
519+
);
520+
}}
503521
</DFocusVisible>
504522
)}
505523
</DCollapseTransition>

packages/ui/src/components/menu/Sub.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { DMenuMode } from './Menu';
22

3-
import { useState, useRef, useCallback } from 'react';
3+
import { useState, useRef } from 'react';
44

55
import { useElement, useEventListener } from '@react-devui/hooks';
66
import { CaretDownOutlined } from '@react-devui/icons';
@@ -88,7 +88,7 @@ export function DSub(props: DSubProps): JSX.Element | null {
8888
left: -9999,
8989
});
9090
const [transformOrigin, setTransformOrigin] = useState<string>();
91-
const updatePosition = useCallback(() => {
91+
const updatePosition = () => {
9292
if (dPopupVisible) {
9393
if (popupRef.current && liRef.current) {
9494
const size = getOriginalSize(popupRef.current);
@@ -100,17 +100,17 @@ export function DSub(props: DSubProps): JSX.Element | null {
100100
}
101101

102102
const { top, left, transformOrigin } = inHorizontalNav
103-
? getVerticalSidePosition(liRef.current, { width, height }, 'bottom-left', 12)
103+
? getVerticalSidePosition(liRef.current, { width, height }, 'bottom', 12)
104104
: getHorizontalSidePosition(liRef.current, { width, height }, 'right', dInNav ? 10 : 14);
105105
setPopupPositionStyle({
106106
top,
107-
left: inHorizontalNav ? left + 16 : left,
107+
left,
108108
width: inHorizontalNav ? width : undefined,
109109
});
110110
setTransformOrigin(transformOrigin);
111111
}
112112
}
113-
}, [dInNav, dPopupVisible, inHorizontalNav]);
113+
};
114114
useEventListener(dEventId, updatePosition);
115115

116116
const maxZIndex = useMaxIndex(dPopupVisible);

packages/ui/src/components/select/Select.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,14 +446,13 @@ function Select<V extends DId, T extends DSelectItem<V> & { children?: T[] }>(
446446
dUpdatePosition={(boxEl, popupEl) => {
447447
const width = boxEl.getBoundingClientRect().width;
448448
const { height } = getOriginalSize(popupEl);
449-
const { top, left, transformOrigin } = getVerticalSidePosition(boxEl, { width, height }, 'bottom-left', 8);
449+
const { top, left, transformOrigin } = getVerticalSidePosition(boxEl, { width, height }, 'bottom', 8);
450450

451451
return {
452452
position: {
453453
top,
454454
left,
455455
width,
456-
maxWidth: window.innerWidth - left - 20,
457456
},
458457
transformOrigin,
459458
};

packages/ui/src/components/tree-select/TreeSelect.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import React, { useCallback, useState, useId, useMemo, useRef } from 'react';
1212
import { useEventNotify } from '@react-devui/hooks';
1313
import { CloseOutlined, LoadingOutlined } from '@react-devui/icons';
1414
import { findNested, getClassName, getOriginalSize, getVerticalSidePosition } from '@react-devui/utils';
15+
import { POSITION_CONFIG } from '@react-devui/utils/position/config';
1516

1617
import { usePrefixConfig, useComponentConfig, useGeneralContext, useDValue, useTranslation } from '../../hooks';
1718
import { registerComponentMate } from '../../utils';
@@ -288,26 +289,6 @@ function TreeSelect<V extends DId, T extends DTreeItem<V> & { children?: T[] }>(
288289
}
289290
};
290291

291-
const updatePosition = useCallback(
292-
(boxEl: HTMLElement, popupEl: HTMLElement) => {
293-
const width = boxEl.getBoundingClientRect().width;
294-
const { height } = getOriginalSize(popupEl);
295-
const { top, left, transformOrigin } = getVerticalSidePosition(boxEl, { width, height }, 'bottom-left', 8);
296-
297-
return {
298-
position: {
299-
top,
300-
left,
301-
width: hasSearch ? width : undefined,
302-
minWidth: hasSearch ? undefined : width,
303-
maxWidth: window.innerWidth - left - 20,
304-
},
305-
transformOrigin,
306-
};
307-
},
308-
[hasSearch]
309-
);
310-
311292
const [selectedNode, suffixNode, selectedLabel] = (() => {
312293
let selectedNode: React.ReactNode = null;
313294
let suffixNode: React.ReactNode = null;
@@ -427,7 +408,26 @@ function TreeSelect<V extends DId, T extends DTreeItem<V> & { children?: T[] }>(
427408
},
428409
}}
429410
dInputRef={dInputRef}
430-
dUpdatePosition={updatePosition}
411+
dUpdatePosition={(boxEl, popupEl) => {
412+
const boxWidth = boxEl.getBoundingClientRect().width;
413+
const minWidth = Math.min(boxWidth, window.innerWidth - POSITION_CONFIG.space * 2);
414+
const { width, height } = getOriginalSize(popupEl);
415+
const { top, left, transformOrigin } = getVerticalSidePosition(
416+
boxEl,
417+
{ width: Math.max(width, minWidth), height },
418+
'bottom-left',
419+
8
420+
);
421+
422+
return {
423+
position: {
424+
top,
425+
left,
426+
minWidth,
427+
},
428+
transformOrigin,
429+
};
430+
}}
431431
afterVisibleChange={afterVisibleChange}
432432
onFocusVisibleChange={setFocusVisible}
433433
onClear={handleClear}

packages/ui/src/components/tree/Panel.tsx

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,6 @@ export function DPanel<V extends DId, T extends DTreeItem<V>>(props: DPanelProps
122122
};
123123
useEventListener(dEventId, handleKeyDown);
124124

125-
const preventBlur: React.MouseEventHandler = (e) => {
126-
if (e.button === 0) {
127-
e.preventDefault();
128-
}
129-
};
130-
131125
const vsPerformance = useMemo<DVirtualScrollPerformance<AbstractTreeNode<V, T>>>(
132126
() => ({
133127
dList,
@@ -182,12 +176,6 @@ export function DPanel<V extends DId, T extends DTreeItem<V>>(props: DPanelProps
182176
{dFocusVisible && item.id === dFocusItem?.id && <div className={`${dPrefix}focus-outline`}></div>}
183177
<div
184178
className={`${dPrefix}tree__option-icon`}
185-
onMouseDown={(e) => {
186-
preventBlur(e);
187-
}}
188-
onMouseUp={(e) => {
189-
preventBlur(e);
190-
}}
191179
onClick={(e) => {
192180
e.stopPropagation();
193181

packages/ui/src/components/virtual-scroll/VirtualScroll.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
267267
overflow: 'hidden',
268268
[dHorizontal ? 'height' : 'width']: 0,
269269
[dHorizontal ? 'width' : 'height']: fillSize[0],
270+
flexShrink: 0,
270271
},
271272
'aria-hidden': true,
272273
})

0 commit comments

Comments
 (0)