Skip to content

Commit

Permalink
feat(Splitter): add lazy mode (#51557)
Browse files Browse the repository at this point in the history
Co-authored-by: afc163 <[email protected]>
Co-authored-by: lijianan <[email protected]>
  • Loading branch information
3 people authored Nov 14, 2024
1 parent 390e346 commit 6773815
Show file tree
Hide file tree
Showing 11 changed files with 608 additions and 9 deletions.
66 changes: 63 additions & 3 deletions components/splitter/SplitBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LeftOutlined from '@ant-design/icons/LeftOutlined';
import RightOutlined from '@ant-design/icons/RightOutlined';
import UpOutlined from '@ant-design/icons/UpOutlined';
import classNames from 'classnames';
import useEvent from 'rc-util/lib/hooks/useEvent';

export interface SplitBarProps {
index: number;
Expand All @@ -20,6 +21,8 @@ export interface SplitBarProps {
ariaNow: number;
ariaMin: number;
ariaMax: number;
lazy?: boolean;
containerSize: number;
}

function getValidNumber(num: number | undefined): number {
Expand All @@ -42,12 +45,18 @@ const SplitBar: React.FC<SplitBarProps> = (props) => {
onOffsetUpdate,
onOffsetEnd,
onCollapse,
lazy,
containerSize,
} = props;

const splitBarPrefixCls = `${prefixCls}-bar`;

// ======================== Resize ========================
const [startPos, setStartPos] = useState<[x: number, y: number] | null>(null);
const [constrainedOffset, setConstrainedOffset] = useState<number>(0);

const constrainedOffsetX = vertical ? 0 : constrainedOffset;
const constrainedOffsetY = vertical ? constrainedOffset : 0;

const onMouseDown: React.MouseEventHandler<HTMLDivElement> = (e) => {
if (resizable && e.currentTarget) {
Expand All @@ -64,17 +73,48 @@ const SplitBar: React.FC<SplitBarProps> = (props) => {
}
};

// Updated constraint calculation
const getConstrainedOffset = (rawOffset: number) => {
const currentPos = (containerSize * ariaNow) / 100;
const newPos = currentPos + rawOffset;

// Calculate available space
const minAllowed = Math.max(0, (containerSize * ariaMin) / 100);
const maxAllowed = Math.min(containerSize, (containerSize * ariaMax) / 100);

// Constrain new position within bounds
const clampedPos = Math.max(minAllowed, Math.min(maxAllowed, newPos));
return clampedPos - currentPos;
};

const handleLazyMove = useEvent((offsetX: number, offsetY: number) => {
const constrainedOffsetValue = getConstrainedOffset(vertical ? offsetY : offsetX);
setConstrainedOffset(constrainedOffsetValue);
});

const handleLazyEnd = useEvent(() => {
onOffsetUpdate(index, constrainedOffsetX, constrainedOffsetY);
setConstrainedOffset(0);
});

React.useEffect(() => {
if (startPos) {
const onMouseMove = (e: MouseEvent) => {
const { pageX, pageY } = e;
const offsetX = pageX - startPos[0];
const offsetY = pageY - startPos[1];

onOffsetUpdate(index, offsetX, offsetY);
if (lazy) {
handleLazyMove(offsetX, offsetY);
} else {
onOffsetUpdate(index, offsetX, offsetY);
}
};

const onMouseUp = () => {
if (lazy) {
handleLazyEnd();
}
setStartPos(null);
onOffsetEnd();
};
Expand All @@ -85,11 +125,18 @@ const SplitBar: React.FC<SplitBarProps> = (props) => {
const offsetX = touch.pageX - startPos[0];
const offsetY = touch.pageY - startPos[1];

onOffsetUpdate(index, offsetX, offsetY);
if (lazy) {
handleLazyMove(offsetX, offsetY);
} else {
onOffsetUpdate(index, offsetX, offsetY);
}
}
};

const handleTouchEnd = () => {
if (lazy) {
handleLazyEnd();
}
setStartPos(null);
onOffsetEnd();
};
Expand All @@ -106,7 +153,11 @@ const SplitBar: React.FC<SplitBarProps> = (props) => {
window.removeEventListener('touchend', handleTouchEnd);
};
}
}, [startPos]);
}, [startPos, lazy, vertical, index, containerSize, ariaNow, ariaMin, ariaMax]);

const transformStyle = {
[`--${splitBarPrefixCls}-preview-offset`]: `${constrainedOffset}px`,
};

// ======================== Render ========================
const StartIcon = vertical ? UpOutlined : LeftOutlined;
Expand All @@ -120,6 +171,15 @@ const SplitBar: React.FC<SplitBarProps> = (props) => {
aria-valuemin={getValidNumber(ariaMin)}
aria-valuemax={getValidNumber(ariaMax)}
>
{lazy && (
<div
className={classNames(`${splitBarPrefixCls}-preview`, {
[`${splitBarPrefixCls}-preview-active`]: !!constrainedOffset,
})}
style={transformStyle}
/>
)}

<div
className={classNames(`${splitBarPrefixCls}-dragger`, {
[`${splitBarPrefixCls}-dragger-disabled`]: !resizable,
Expand Down
3 changes: 3 additions & 0 deletions components/splitter/Splitter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const Splitter: React.FC<React.PropsWithChildren<SplitterProps>> = (props) => {
onResizeStart,
onResize,
onResizeEnd,
lazy,
} = props;

const { getPrefixCls, direction, splitter } = React.useContext(ConfigContext);
Expand Down Expand Up @@ -170,6 +171,7 @@ const Splitter: React.FC<React.PropsWithChildren<SplitterProps>> = (props) => {

splitBar = (
<SplitBar
lazy={lazy}
index={idx}
active={movingIndex === idx}
prefixCls={prefixCls}
Expand All @@ -190,6 +192,7 @@ const Splitter: React.FC<React.PropsWithChildren<SplitterProps>> = (props) => {
}}
onOffsetEnd={onInternalResizeEnd}
onCollapse={onInternalCollapse}
containerSize={containerSize || 0}
/>
);
}
Expand Down
122 changes: 122 additions & 0 deletions components/splitter/__tests__/__snapshots__/demo-extend.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,128 @@ exports[`renders components/splitter/demo/group.tsx extend context correctly 1`]

exports[`renders components/splitter/demo/group.tsx extend context correctly 2`] = `[]`;

exports[`renders components/splitter/demo/lazy.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
style="width: 100%;"
>
<div
class="ant-space-item"
>
<div
class="ant-splitter ant-splitter-horizontal"
style="height: 200px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);"
>
<div
class="ant-splitter-panel"
style="flex-basis: 40%; flex-grow: 0;"
>
<div
class="ant-flex ant-flex-align-center ant-flex-justify-center"
style="height: 100%;"
>
<h5
class="ant-typography ant-typography-secondary"
style="white-space: nowrap;"
>
First
</h5>
</div>
</div>
<div
aria-valuemax="0"
aria-valuemin="0"
aria-valuenow="40"
class="ant-splitter-bar"
role="separator"
>
<div
class="ant-splitter-bar-preview"
style="--ant-splitter-bar-preview-offset: 0px;"
/>
<div
class="ant-splitter-bar-dragger ant-splitter-bar-dragger-disabled"
/>
</div>
<div
class="ant-splitter-panel"
style="flex-basis: auto; flex-grow: 1;"
>
<div
class="ant-flex ant-flex-align-center ant-flex-justify-center"
style="height: 100%;"
>
<h5
class="ant-typography ant-typography-secondary"
style="white-space: nowrap;"
>
Second
</h5>
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-splitter ant-splitter-vertical"
style="height: 200px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);"
>
<div
class="ant-splitter-panel"
style="flex-basis: 40%; flex-grow: 0;"
>
<div
class="ant-flex ant-flex-align-center ant-flex-justify-center"
style="height: 100%;"
>
<h5
class="ant-typography ant-typography-secondary"
style="white-space: nowrap;"
>
First
</h5>
</div>
</div>
<div
aria-valuemax="0"
aria-valuemin="0"
aria-valuenow="40"
class="ant-splitter-bar"
role="separator"
>
<div
class="ant-splitter-bar-preview"
style="--ant-splitter-bar-preview-offset: 0px;"
/>
<div
class="ant-splitter-bar-dragger ant-splitter-bar-dragger-disabled"
/>
</div>
<div
class="ant-splitter-panel"
style="flex-basis: auto; flex-grow: 1;"
>
<div
class="ant-flex ant-flex-align-center ant-flex-justify-center"
style="height: 100%;"
>
<h5
class="ant-typography ant-typography-secondary"
style="white-space: nowrap;"
>
Second
</h5>
</div>
</div>
</div>
</div>
</div>
`;

exports[`renders components/splitter/demo/lazy.tsx extend context correctly 2`] = `[]`;

exports[`renders components/splitter/demo/multiple.tsx extend context correctly 1`] = `
<div
class="ant-splitter ant-splitter-horizontal"
Expand Down
Loading

0 comments on commit 6773815

Please sign in to comment.