diff --git a/components/splitter/Panel.tsx b/components/splitter/Panel.tsx
index adec5e6cd3..86f4c697f3 100644
--- a/components/splitter/Panel.tsx
+++ b/components/splitter/Panel.tsx
@@ -11,13 +11,24 @@ export const InternalPanel = forwardRef<
const panelClassName = classNames(
`${prefixCls}-panel`,
{
- [`${prefixCls}-panel-hidden`]: !size,
+ [`${prefixCls}-panel-hidden`]: size === 0,
},
className,
);
+ const hasSize = size !== undefined;
+
return (
-
+
{children}
);
diff --git a/components/splitter/SplitBar.tsx b/components/splitter/SplitBar.tsx
index 0884668013..c4bc289e29 100644
--- a/components/splitter/SplitBar.tsx
+++ b/components/splitter/SplitBar.tsx
@@ -22,6 +22,10 @@ export interface SplitBarProps {
ariaMax: number;
}
+function getValidNumber(num: number | undefined): number {
+ return typeof num === 'number' && !Number.isNaN(num) ? Math.round(num) : 0;
+}
+
const SplitBar: React.FC
= (props) => {
const {
prefixCls,
@@ -112,9 +116,9 @@ const SplitBar: React.FC = (props) => {
> = (props) => {
}
// ====================== Container =======================
- const [containerSize, setContainerSize] = useState
(100);
+ const [containerSize, setContainerSize] = useState();
const onContainerResize: GetProp = (size) => {
const { offsetWidth, offsetHeight } = size;
@@ -82,10 +82,8 @@ const Splitter: React.FC> = (props) => {
};
// ========================= Size =========================
- const [itemPxSizes, itemPtgSizes, itemPtgMinSizes, itemPtgMaxSizes, updateSizes] = useSizes(
- items,
- containerSize,
- );
+ const [panelSizes, itemPxSizes, itemPtgSizes, itemPtgMinSizes, itemPtgMaxSizes, updateSizes] =
+ useSizes(items, containerSize);
// ====================== Resizable =======================
const resizableInfos = useResizable(items, itemPxSizes);
@@ -157,7 +155,7 @@ const Splitter: React.FC> = (props) => {
{items.map((item, idx) => {
// Panel
- const panel =
;
+ const panel =
;
// Split Bar
let splitBar: React.ReactElement | null = null;
diff --git a/components/splitter/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/splitter/__tests__/__snapshots__/demo-extend.test.ts.snap
index a3bfd50747..9ebbbd18c7 100644
--- a/components/splitter/__tests__/__snapshots__/demo-extend.test.ts.snap
+++ b/components/splitter/__tests__/__snapshots__/demo-extend.test.ts.snap
@@ -10,7 +10,7 @@ exports[`renders components/splitter/demo/collapsible.tsx extend context correct
>
@@ -363,7 +267,7 @@ exports[`renders components/splitter/demo/debug.tsx extend context correctly 1`]
@@ -443,7 +347,7 @@ exports[`renders components/splitter/demo/debug.tsx extend context correctly 1`]
@@ -363,7 +267,7 @@ exports[`renders components/splitter/demo/debug.tsx correctly 1`] = `
@@ -449,7 +353,7 @@ exports[`renders components/splitter/demo/debug.tsx correctly 1`] = `
;
+const resizeSplitter = async () => {
+ triggerResize(document.body.querySelector('.ant-splitter')!);
+ await waitFakeTimer();
+};
+
const SplitterDemo = ({ items = [{}, {}], ...props }: { items?: PanelProps[] } & SplitterProps) => (
{items?.map((item, idx) => {
@@ -61,6 +66,8 @@ describe('Splitter', () => {
it('should correct render panel size', async () => {
const { container } = render();
+ await resizeSplitter();
+
const panels = container.querySelectorAll('.ant-splitter-panel');
expect(panels?.[0]).toHaveStyle('flex-basis: 20px');
@@ -142,6 +149,8 @@ describe('Splitter', () => {
,
);
+ await resizeSplitter();
+
// Right
mockDrag(container.querySelector('.ant-splitter-bar-dragger')!, 40);
expect(onResize).toHaveBeenCalledWith([90, 10]);
@@ -161,6 +170,8 @@ describe('Splitter', () => {
,
);
+ await resizeSplitter();
+
// Right
mockTouchDrag(container.querySelector('.ant-splitter-bar-dragger')!, 40);
expect(onResize).toHaveBeenCalledWith([90, 10]);
@@ -172,7 +183,7 @@ describe('Splitter', () => {
expect(onResizeEnd).toHaveBeenCalledWith([0, 100]);
});
- it('with min', () => {
+ it('with min', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -180,12 +191,14 @@ describe('Splitter', () => {
,
);
+ await resizeSplitter();
+
mockDrag(container.querySelector('.ant-splitter-bar-dragger')!, -100);
expect(onResize).toHaveBeenCalledWith([10, 90]);
expect(onResizeEnd).toHaveBeenCalledWith([10, 90]);
});
- it('with max', () => {
+ it('with max', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -193,12 +206,14 @@ describe('Splitter', () => {
,
);
+ await resizeSplitter();
+
mockDrag(container.querySelector('.ant-splitter-bar-dragger')!, 100);
expect(onResize).toHaveBeenCalledWith([90, 10]);
expect(onResizeEnd).toHaveBeenCalledWith([90, 10]);
});
- it('both panel has min and max', () => {
+ it('both panel has min and max', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -213,6 +228,8 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
mockDrag(container.querySelector('.ant-splitter-bar-dragger')!, -100);
expect(onResize).toHaveBeenCalledWith([20, 80]);
expect(onResizeEnd).toHaveBeenCalledWith([20, 80]);
@@ -222,7 +239,7 @@ describe('Splitter', () => {
expect(onResizeEnd).toHaveBeenCalledWith([80, 20]);
});
- it('rtl', () => {
+ it('rtl', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -232,12 +249,14 @@ describe('Splitter', () => {
,
);
+ await resizeSplitter();
+
mockDrag(container.querySelector('.ant-splitter-bar-dragger')!, -40);
expect(onResize).toHaveBeenCalledWith([90, 10]);
expect(onResizeEnd).toHaveBeenCalledWith([90, 10]);
});
- it('[true, 0, true] can be move left', () => {
+ it('[true, 0, true] can be move left', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -249,12 +268,14 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
mockDrag(container.querySelectorAll('.ant-splitter-bar-dragger')[1], -100);
expect(onResize).toHaveBeenCalledWith([0, 50, 50]);
expect(onResizeEnd).toHaveBeenCalledWith([0, 50, 50]);
});
- it('[false, 0, true] can not be move left', () => {
+ it('[false, 0, true] can not be move left', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -266,6 +287,8 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
mockDrag(container.querySelectorAll('.ant-splitter-bar-dragger')[1], -100);
expect(onResize).toHaveBeenCalledWith([50, 0, 50]);
expect(onResizeEnd).toHaveBeenCalledWith([50, 0, 50]);
@@ -277,6 +300,9 @@ describe('Splitter', () => {
return ;
};
const { container } = render();
+
+ await resizeSplitter();
+
mockDrag(container.querySelectorAll('.ant-splitter-bar-dragger')[1], -100);
triggerResize(container.querySelector('.ant-splitter')!);
await act(async () => {
@@ -295,11 +321,13 @@ describe('Splitter', () => {
// ============================= Collapsible =============================
describe('collapsible', () => {
- it('Basic', () => {
+ it('Basic', async () => {
const { container, rerender } = render(
,
);
+ await resizeSplitter();
+
expect(container.querySelectorAll('.ant-splitter-bar-collapse-icon')).toHaveLength(2);
expect(container.querySelector('.ant-splitter-bar-collapse-start')).toBeTruthy();
expect(container.querySelector('.ant-splitter-bar-collapse-end')).toBeTruthy();
@@ -324,7 +352,7 @@ describe('Splitter', () => {
expect(container.querySelectorAll('.ant-splitter-bar-collapse-end')).toHaveLength(1);
});
- it('collapsible - true', () => {
+ it('collapsible - true', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -342,12 +370,14 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
fireEvent.click(container.querySelector('.ant-splitter-bar-collapse-start')!);
expect(onResize).toHaveBeenCalledWith([0, 100]);
expect(onResizeEnd).toHaveBeenCalledWith([0, 100]);
});
- it('collapsible - start:true', () => {
+ it('collapsible - start:true', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -368,6 +398,8 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
expect(container.querySelector('.ant-splitter-bar-collapse-start')).toBeFalsy();
expect(container.querySelector('.ant-splitter-bar-collapse-end')).toBeTruthy();
@@ -376,7 +408,7 @@ describe('Splitter', () => {
expect(onResizeEnd).toHaveBeenCalledWith([60, 0, 40]);
});
- it('collapsible - end:true', () => {
+ it('collapsible - end:true', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -397,6 +429,8 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
expect(container.querySelector('.ant-splitter-bar-collapse-start')).toBeTruthy();
expect(container.querySelector('.ant-splitter-bar-collapse-end')).toBeFalsy();
@@ -405,7 +439,7 @@ describe('Splitter', () => {
expect(onResizeEnd).toHaveBeenCalledWith([40, 0, 60]);
});
- it('both collapsible', () => {
+ it('both collapsible', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -424,6 +458,8 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
function expectClick(element: HTMLElement, size: number[]) {
onResize.mockReset();
onResizeEnd.mockReset();
@@ -439,7 +475,7 @@ describe('Splitter', () => {
expectClick(container.querySelector('.ant-splitter-bar-collapse-start')!, [50, 50]);
});
- it('collapsible with min', () => {
+ it('collapsible with min', async () => {
const onResize = jest.fn();
const onResizeEnd = jest.fn();
@@ -461,6 +497,8 @@ describe('Splitter', () => {
/>,
);
+ await resizeSplitter();
+
// Collapse left
fireEvent.click(container.querySelector('.ant-splitter-bar-collapse-start')!);
expect(onResize).toHaveBeenCalledWith([0, 100]);
diff --git a/components/splitter/__tests__/ssr.test.tsx b/components/splitter/__tests__/ssr.test.tsx
new file mode 100644
index 0000000000..522b8ceb20
--- /dev/null
+++ b/components/splitter/__tests__/ssr.test.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { renderToString } from 'react-dom/server';
+
+import Splitter from '..';
+import { resetWarned } from '../../_util/warning';
+
+describe('Splitter.SSR', () => {
+ beforeEach(() => {
+ resetWarned();
+ document.body.innerHTML = '';
+ });
+
+ afterEach(() => {
+ jest.clearAllTimers();
+ jest.useRealTimers();
+ });
+
+ it('px value', () => {
+ const str = renderToString(
+
+
+
+ ,
+ );
+
+ const div = document.createElement('div');
+ div.innerHTML = str;
+ document.body.appendChild(div);
+
+ expect(div.querySelectorAll('.ant-splitter-panel')[0]).toHaveStyle({
+ flexBasis: '23px',
+ flexGrow: '0',
+ });
+ expect(div.querySelectorAll('.ant-splitter-panel')[1]).toHaveStyle({
+ flexBasis: 'auto',
+ flexGrow: '1',
+ });
+ });
+
+ it('ptg value', () => {
+ const str = renderToString(
+
+
+
+ ,
+ );
+
+ const div = document.createElement('div');
+ div.innerHTML = str;
+ document.body.appendChild(div);
+
+ expect(div.querySelectorAll('.ant-splitter-panel')[0]).toHaveStyle({
+ flexBasis: '33%',
+ flexGrow: '0',
+ });
+ expect(div.querySelectorAll('.ant-splitter-panel')[1]).toHaveStyle({
+ flexBasis: 'auto',
+ flexGrow: '1',
+ });
+ });
+});
diff --git a/components/splitter/hooks/useResize.ts b/components/splitter/hooks/useResize.ts
index c2315f6658..5d33b5f539 100644
--- a/components/splitter/hooks/useResize.ts
+++ b/components/splitter/hooks/useResize.ts
@@ -4,16 +4,20 @@ import type { ItemType } from './useItems';
import type { ResizableInfo } from './useResizable';
import { getPtg } from './useSizes';
+/**
+ * Handle user drag resize logic.
+ */
export default function useResize(
items: ItemType[],
resizableInfos: ResizableInfo[],
percentSizes: number[],
- containerSize: number,
+ containerSize: number | undefined,
updateSizes: (sizes: number[]) => void,
) {
const limitSizes = items.map((item) => [item.min, item.max]);
- const ptg2px = (ptg: number) => ptg * containerSize;
+ const mergedContainerSize = containerSize || 0;
+ const ptg2px = (ptg: number) => ptg * mergedContainerSize;
// ======================== Resize ========================
function getLimitSize(str: string | number | undefined, defaultLimit: number) {
@@ -79,8 +83,8 @@ export default function useResize(
// Get boundary
const startMinSize = getLimitSize(limitSizes[mergedIndex][0], 0);
const endMinSize = getLimitSize(limitSizes[nextIndex][0], 0);
- const startMaxSize = getLimitSize(limitSizes[mergedIndex][1], containerSize);
- const endMaxSize = getLimitSize(limitSizes[nextIndex][1], containerSize);
+ const startMaxSize = getLimitSize(limitSizes[mergedIndex][1], mergedContainerSize);
+ const endMaxSize = getLimitSize(limitSizes[nextIndex][1], mergedContainerSize);
let mergedOffset = offset;
@@ -129,9 +133,9 @@ export default function useResize(
const totalSize = currentSize + targetSize;
const currentSizeMin = getLimitSize(limitSizes[currentIndex][0], 0);
- const currentSizeMax = getLimitSize(limitSizes[currentIndex][1], containerSize);
+ const currentSizeMax = getLimitSize(limitSizes[currentIndex][1], mergedContainerSize);
const targetSizeMin = getLimitSize(limitSizes[targetIndex][0], 0);
- const targetSizeMax = getLimitSize(limitSizes[targetIndex][1], containerSize);
+ const targetSizeMax = getLimitSize(limitSizes[targetIndex][1], mergedContainerSize);
const limitStart = Math.max(currentSizeMin, totalSize - targetSizeMax);
const limitEnd = Math.min(currentSizeMax, totalSize - targetSizeMin);
diff --git a/components/splitter/hooks/useSizes.ts b/components/splitter/hooks/useSizes.ts
index 38b0dec1bc..3dab83ee96 100644
--- a/components/splitter/hooks/useSizes.ts
+++ b/components/splitter/hooks/useSizes.ts
@@ -14,12 +14,13 @@ function isPtg(itemSize: string | number | undefined): itemSize is string {
* Save the size state.
* Align the size into flex percentage base.
*/
-export default function useSizes(items: PanelProps[], containerSize: number) {
+export default function useSizes(items: PanelProps[], containerSize?: number) {
const propSizes = items.map((item) => item.size);
const itemsCount = items.length;
- const ptg2px = (ptg: number) => ptg * containerSize;
+ const mergedContainerSize = containerSize || 0;
+ const ptg2px = (ptg: number) => ptg * mergedContainerSize;
// We do not need care the size state match the `items` length in `useState`.
// It will calculate later.
@@ -53,7 +54,7 @@ export default function useSizes(items: PanelProps[], containerSize: number) {
} else if (itemSize || itemSize === 0) {
const num = Number(itemSize);
if (!Number.isNaN(num)) {
- ptgList[i] = num / containerSize;
+ ptgList[i] = num / mergedContainerSize;
}
} else {
emptyCount += 1;
@@ -74,11 +75,11 @@ export default function useSizes(items: PanelProps[], containerSize: number) {
}
return ptgList as number[];
- }, [sizes, containerSize]);
+ }, [sizes, mergedContainerSize]);
const postPxSizes = React.useMemo(
() => postPercentSizes.map(ptg2px),
- [postPercentSizes, containerSize],
+ [postPercentSizes, mergedContainerSize],
);
const postPercentMinSizes = React.useMemo(
@@ -87,9 +88,9 @@ export default function useSizes(items: PanelProps[], containerSize: number) {
if (isPtg(item.min)) {
return getPtg(item.min);
}
- return (item.min || 0) / containerSize;
+ return (item.min || 0) / mergedContainerSize;
}),
- [items, containerSize],
+ [items, mergedContainerSize],
);
const postPercentMaxSizes = React.useMemo(
@@ -98,12 +99,19 @@ export default function useSizes(items: PanelProps[], containerSize: number) {
if (isPtg(item.max)) {
return getPtg(item.max);
}
- return (item.max || containerSize) / containerSize;
+ return (item.max || mergedContainerSize) / mergedContainerSize;
}),
- [items, containerSize],
+ [items, mergedContainerSize],
+ );
+
+ // If ssr, we will use the size from developer config first.
+ const panelSizes = React.useMemo(
+ () => (containerSize ? postPxSizes : sizes),
+ [postPxSizes, containerSize],
);
return [
+ panelSizes,
postPxSizes,
postPercentSizes,
postPercentMinSizes,