Skip to content

fix: use Rect for logic #906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
&-invalid {
box-shadow: 0 0 2px red;
}

&-panels {
display: flex;
flex-wrap: nowrap;
}

&-panel {
display: inline-block;
vertical-align: top;
Expand Down
46 changes: 26 additions & 20 deletions src/PickerInput/Popup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
ValueDate,
} from '../../interface';
import { toArray } from '../../utils/miscUtil';
import { getRealPlacement, getoffsetUnit } from '../../utils/uiUtil';
import PickerContext from '../context';
import Footer, { type FooterProps } from './Footer';
import PopupPanel, { type PopupPanelProps } from './PopupPanel';
Expand All @@ -32,8 +31,7 @@
onPresetSubmit: (presetValue: PresetValue) => void;

// Range
activeOffset?: number;
placement?: string;
activeInfo?: [activeInputLeft: number, activeInputRight: number, selectorWidth: number];
// Direction
direction?: 'ltr' | 'rtl';

Expand All @@ -59,8 +57,7 @@
// Range
range,
multiple,
activeOffset = 0,
placement,
activeInfo = [0, 0, 0],

// Presets
presets,
Expand Down Expand Up @@ -96,28 +93,43 @@
// ======================== Offset ========================
const [containerWidth, setContainerWidth] = React.useState<number>(0);
const [containerOffset, setContainerOffset] = React.useState<number>(0);
const [arrowOffset, setArrowOffset] = React.useState<number>(0);

const onResize: ResizeObserverProps['onResize'] = (info) => {
if (info.offsetWidth) {
setContainerWidth(info.offsetWidth);
if (info.width) {
setContainerWidth(info.width);
}
};

const [activeInputLeft, activeInputRight, selectorWidth] = activeInfo;

React.useEffect(() => {
// `activeOffset` is always align with the active input element
// So we need only check container contains the `activeOffset`
if (range) {
if (range && wrapperRef.current) {
// Offset in case container has border radius
const arrowWidth = arrowRef.current?.offsetWidth || 0;

const maxOffset = containerWidth - arrowWidth;
if (activeOffset <= maxOffset) {
setContainerOffset(0);
// Arrow Offset
const wrapperRect = wrapperRef.current.getBoundingClientRect();
const nextArrowOffset = rtl
? activeInputRight - arrowWidth

Check warning on line 116 in src/PickerInput/Popup/index.tsx

View check run for this annotation

Codecov / codecov/patch

src/PickerInput/Popup/index.tsx#L116

Added line #L116 was not covered by tests
: activeInputLeft - wrapperRect.left;
setArrowOffset(nextArrowOffset);

// Container Offset
if (containerWidth < selectorWidth) {
const offset = rtl
? wrapperRect.right - (activeInputRight - arrowWidth + containerWidth)

Check warning on line 123 in src/PickerInput/Popup/index.tsx

View check run for this annotation

Codecov / codecov/patch

src/PickerInput/Popup/index.tsx#L123

Added line #L123 was not covered by tests
: activeInputLeft + arrowWidth - wrapperRect.left - containerWidth;

const safeOffset = Math.max(0, offset);
setContainerOffset(safeOffset);
} else {
setContainerOffset(activeOffset + arrowWidth - containerWidth);
setContainerOffset(0);
}
}
}, [containerWidth, activeOffset, range]);
}, [rtl, containerWidth, activeInputLeft, activeInputRight, selectorWidth, range]);

// ======================== Custom ========================
function filterEmpty<T>(list: T[]) {
Expand Down Expand Up @@ -213,19 +225,13 @@
);

if (range) {
const realPlacement = getRealPlacement(placement, rtl);
const offsetUnit = getoffsetUnit(realPlacement, rtl);
renderNode = (
<div
onMouseDown={onPanelMouseDown}
ref={wrapperRef}
className={classNames(`${prefixCls}-range-wrapper`, `${prefixCls}-${picker}-range-wrapper`)}
>
<div
ref={arrowRef}
className={`${prefixCls}-range-arrow`}
style={{ [offsetUnit]: activeOffset }}
/>
<div ref={arrowRef} className={`${prefixCls}-range-arrow`} style={{ left: arrowOffset }} />

{/* Watch for container size */}
<ResizeObserver onResize={onResize}>{renderNode}</ResizeObserver>
Expand Down
11 changes: 6 additions & 5 deletions src/PickerInput/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ function RangePicker<DateType extends object = any>(
prefixCls,
styles,
classNames,
placement,

// Value
defaultValue,
Expand Down Expand Up @@ -473,7 +472,10 @@ function RangePicker<DateType extends object = any>(
// == Panels ==
// ========================================================
// Save the offset with active bar position
const [activeOffset, setActiveOffset] = React.useState(0);
// const [activeOffset, setActiveOffset] = React.useState(0);
const [activeInfo, setActiveInfo] = React.useState<
[activeInputLeft: number, activeInputRight: number, selectorWidth: number]
>([0, 0, 0]);

// ======================= Presets ========================
const presetList = usePresets(presets, ranges);
Expand Down Expand Up @@ -574,8 +576,7 @@ function RangePicker<DateType extends object = any>(
// Range
range
multiplePanel={multiplePanel}
activeOffset={activeOffset}
placement={placement}
activeInfo={activeInfo}
// Disabled
disabledDate={mergedDisabledDate}
// Focus
Expand Down Expand Up @@ -796,7 +797,7 @@ function RangePicker<DateType extends object = any>(
invalid={submitInvalidates}
onInvalid={onSelectorInvalid}
// Offset
onActiveOffset={setActiveOffset}
onActiveInfo={setActiveInfo}
/>
</PickerTrigger>
</PickerContext.Provider>
Expand Down
27 changes: 13 additions & 14 deletions src/PickerInput/Selector/RangeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import useInputProps from './hooks/useInputProps';
import useRootProps from './hooks/useRootProps';
import Icon, { ClearIcon } from './Icon';
import Input, { type InputRef } from './Input';
import { getoffsetUnit, getRealPlacement } from '../../utils/uiUtil';

export type SelectorIdType =
| string
Expand Down Expand Up @@ -42,7 +41,9 @@ export interface RangeSelectorProps<DateType = any> extends SelectorProps<DateTy
* Trigger when the active bar offset position changed.
* This is used for popup panel offset.
*/
onActiveOffset: (offset: number) => void;
onActiveInfo: (
info: [activeInputLeft: number, activeInputRight: number, selectorWidth: number],
) => void;
}

function RangeSelector<DateType extends object = any>(
Expand Down Expand Up @@ -102,7 +103,7 @@ function RangeSelector<DateType extends object = any>(
onOpenChange,

// Offset
onActiveOffset,
onActiveInfo,
placement,

// Native
Expand Down Expand Up @@ -173,9 +174,6 @@ function RangeSelector<DateType extends object = any>(
});

// ====================== ActiveBar =======================
const realPlacement = getRealPlacement(placement, rtl);
const offsetUnit = getoffsetUnit(realPlacement, rtl);
const placementRight = realPlacement?.toLowerCase().endsWith('right');
const [activeBarStyle, setActiveBarStyle] = React.useState<React.CSSProperties>({
position: 'absolute',
width: 0,
Expand All @@ -184,15 +182,16 @@ function RangeSelector<DateType extends object = any>(
const syncActiveOffset = useEvent(() => {
const input = getInput(activeIndex);
if (input) {
const { offsetWidth, offsetLeft, offsetParent } = input.nativeElement;
const parentWidth = (offsetParent as HTMLElement)?.offsetWidth || 0;
const activeOffset = placementRight ? (parentWidth - offsetWidth - offsetLeft) : offsetLeft;
setActiveBarStyle(({ insetInlineStart, insetInlineEnd, ...rest }) => ({
...rest,
width: offsetWidth,
[offsetUnit]: activeOffset
const inputRect = input.nativeElement.getBoundingClientRect();
const parentRect = rootRef.current.getBoundingClientRect();

const rectOffset = inputRect.left - parentRect.left;
setActiveBarStyle((ori) => ({
...ori,
width: inputRect.width,
left: rectOffset,
}));
onActiveOffset(activeOffset);
onActiveInfo([inputRect.left, inputRect.right, parentRect.width]);
}
});

Expand Down
Loading