Add support for resizing panes in the playground (#2581)

fix #2422 


![Kapture 2023-10-17 at 15 12
37](https://github.com/microsoft/typespec/assets/1031227/e2eea00b-3d2f-454a-9c89-76c678c6dedb)
This commit is contained in:
Timothee Guerin 2023-10-18 08:59:03 -07:00 коммит произвёл GitHub
Родитель f39f3a7a99
Коммит 9d2cf852e7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 515 добавлений и 45 удалений

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/playground",
"comment": "Add resizable panes for the editor and output",
"type": "none"
}
],
"packageName": "@typespec/playground"
}

52
common/config/rush/pnpm-lock.yaml сгенерированный
Просмотреть файл

@ -882,6 +882,9 @@ importers:
c8:
specifier: ~8.0.1
version: 8.0.1
copyfiles:
specifier: ~2.4.1
version: 2.4.1
cross-env:
specifier: ~7.0.3
version: 7.0.3
@ -8710,6 +8713,19 @@ packages:
webpack: 5.88.2(@swc/core@1.3.62)
dev: false
/copyfiles@2.4.1:
resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==}
hasBin: true
dependencies:
glob: 7.2.3
minimatch: 3.1.2
mkdirp: 1.0.4
noms: 0.0.0
through2: 2.0.5
untildify: 4.0.0
yargs: 16.2.0
dev: true
/core-js-compat@3.32.2:
resolution: {integrity: sha512-+GjlguTDINOijtVRUxrQOv3kfu9rl+qPNdX2LTbJ/ZyVTuxK+ksVSAGX1nHstu4hrv1En/uPTtWgq2gI5wt4AQ==}
dependencies:
@ -8728,7 +8744,6 @@ packages:
/core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: false
/cose-base@1.0.3:
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
@ -11711,7 +11726,6 @@ packages:
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: false
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@ -12900,7 +12914,6 @@ packages:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
dev: false
/mkdirp@3.0.1:
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
@ -13145,6 +13158,13 @@ packages:
engines: {node: '>=6'}
dev: false
/noms@0.0.0:
resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==}
dependencies:
inherits: 2.0.4
readable-stream: 1.0.34
dev: true
/non-layered-tidy-tree-layout@2.0.2:
resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==}
@ -14144,7 +14164,6 @@ packages:
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: false
/process@0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
@ -14709,6 +14728,15 @@ packages:
mute-stream: 0.0.8
dev: true
/readable-stream@1.0.34:
resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==}
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 0.0.1
string_decoder: 0.10.31
dev: true
/readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
dependencies:
@ -14719,7 +14747,6 @@ packages:
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
dev: false
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
@ -15109,7 +15136,6 @@ packages:
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
dev: false
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@ -15605,11 +15631,14 @@ packages:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
/string_decoder@0.10.31:
resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==}
dev: true
/string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
safe-buffer: 5.1.2
dev: false
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@ -15941,6 +15970,13 @@ packages:
/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
/through2@2.0.5:
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
dependencies:
readable-stream: 2.3.8
xtend: 4.0.2
dev: true
/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
requiresBuild: true
@ -16429,7 +16465,6 @@ packages:
/untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
dev: false
/update-browserslist-db@1.0.12(browserslist@4.21.10):
resolution: {integrity: sha512-tE1smlR58jxbFMtrMpFNRmsrOXlpNXss965T1CrpwuZUzUAg/TBQc94SpyhDLSzrqrJS9xTRBthnZAGcE1oaxg==}
@ -17249,7 +17284,6 @@ packages:
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
dev: false
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}

1
packages/playground/definitions/index.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
declare module "*.module.css";

Просмотреть файл

@ -30,8 +30,9 @@
},
"scripts": {
"clean": "rimraf ./dist ./dist-dev ./temp ./typespecContents.json",
"build": "tsc -p .",
"watch": "tsc -p . --watch",
"build": "tsc -p . && npm run copy-css",
"watch": "npm run copy-css && tsc -p . --watch",
"copy-css": "copyfiles -u 1 src/**/*.module.css dist/src",
"preview": "npm run build && vite preview",
"start": "vite",
"e2e": "cross-env PW_EXPERIMENTAL_TS_ESM=1 playwright test -c e2e ",
@ -89,6 +90,7 @@
"rimraf": "~5.0.1",
"rollup-plugin-visualizer": "~5.9.2",
"typescript": "~5.2.2",
"vite": "^4.4.9"
"vite": "^4.4.9",
"copyfiles": "~2.4.1"
}
}

Просмотреть файл

@ -2,5 +2,5 @@ export { createBrowserHost } from "./browser-host.js";
export { registerMonacoDefaultWorkers } from "./monaco-worker.js";
export { registerMonacoLanguage } from "./services.js";
export { createUrlStateStorage } from "./state-storage.js";
export { PlaygroundSample } from "./types.js";
export type { PlaygroundSample } from "./types.js";
export { resolveLibraries as filterEmitters } from "./utils.js";

Просмотреть файл

@ -1,3 +1,4 @@
export { Playground, PlaygroundProps } from "./playground.js";
export { Playground } from "./playground.js";
export type { PlaygroundProps } from "./playground.js";
export { createReactPlayground, renderReactPlayground } from "./standalone.js";
export * from "./types.js";

Просмотреть файл

@ -14,6 +14,8 @@ import { useMonacoModel } from "./editor.js";
import { Footer } from "./footer.js";
import { useAsyncMemo, useControllableValue } from "./hooks.js";
import { OutputView } from "./output-view.js";
import Pane from "./split-pane/pane.js";
import { SplitPane } from "./split-pane/split-pane.js";
import { CompilationState, FileOutputViewer } from "./types.js";
import { TypeSpecEditor } from "./typespec-editor.js";
@ -208,45 +210,42 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
<div
css={{
display: "grid",
gridTemplateColumns: "repeat(2, 1fr)",
gridTemplateColumns: "1",
gridTemplateRows: "1fr auto",
gridTemplateAreas: '"typespeceditor output"\n "footer footer"',
gridTemplateAreas: '"typespeceditor"\n "footer"',
width: "100%",
height: "100%",
overflow: "hidden",
fontFamily: `"Segoe UI", Tahoma, Geneva, Verdana, sans-serif`,
}}
>
<div css={{ gridArea: "typespeceditor", width: "100%", height: "100%", overflow: "hidden" }}>
<EditorCommandBar
libraries={libraries}
selectedEmitter={selectedEmitter}
onSelectedEmitterChange={onSelectedEmitterChange}
compilerOptions={compilerOptions}
onCompilerOptionsChange={onCompilerOptionsChange}
samples={props.samples}
selectedSampleName={selectedSampleName}
onSelectedSampleNameChange={onSelectedSampleNameChange}
saveCode={saveCode}
newIssue={props?.links?.githubIssueUrl ? newIssue : undefined}
documentationUrl={props.links?.documentationUrl}
/>
<TypeSpecEditor model={typespecModel} actions={typespecEditorActions} />
</div>
<div
css={{
gridArea: "output",
overflow: "hidden",
display: "flex",
flexDirection: "column",
borderLeft: "1px solid #c5c5c5",
}}
<SplitPane
initialSizes={["50%", "50%"]}
css={{ gridArea: "typespeceditor", width: "100%", height: "100%", overflow: "hidden" }}
>
<OutputView
compilationState={compilationState}
viewers={props.emitterViewers?.[selectedEmitter]}
/>
</div>
<Pane>
<EditorCommandBar
libraries={libraries}
selectedEmitter={selectedEmitter}
onSelectedEmitterChange={onSelectedEmitterChange}
compilerOptions={compilerOptions}
onCompilerOptionsChange={onCompilerOptionsChange}
samples={props.samples}
selectedSampleName={selectedSampleName}
onSelectedSampleNameChange={onSelectedSampleNameChange}
saveCode={saveCode}
newIssue={props?.links?.githubIssueUrl ? newIssue : undefined}
documentationUrl={props.links?.documentationUrl}
/>
<TypeSpecEditor model={typespecModel} actions={typespecEditorActions} />
</Pane>
<Pane>
<OutputView
compilationState={compilationState}
viewers={props.emitterViewers?.[selectedEmitter]}
/>
</Pane>
</SplitPane>
<Footer />
</div>
);

Просмотреть файл

@ -0,0 +1 @@
export { SplitPane, SplitPaneProps } from "./split-pane.js";

Просмотреть файл

@ -0,0 +1,13 @@
import { HTMLAttributes, PropsWithChildren } from "react";
export interface PaneProps {
maxSize?: number | string;
minSize?: number | string;
}
export default function Pane({
children,
...props
}: PropsWithChildren<HTMLAttributes<HTMLDivElement> & PaneProps>) {
return <div {...props}>{children}</div>;
}

Просмотреть файл

@ -0,0 +1,29 @@
import { mergeClasses } from "@fluentui/react-components";
import { ReactNode } from "react";
import style from "./split-pane.module.css";
export interface SashContentProps {
className?: string;
dragging?: boolean;
children?: ReactNode;
}
export const SashContent: React.FunctionComponent<SashContentProps> = ({
className,
children,
dragging,
...others
}: SashContentProps) => {
return (
<div
className={mergeClasses(
style["sash-content"],
dragging && style["sash-content-dragging"],
className
)}
{...others}
>
{children}
</div>
);
};

Просмотреть файл

@ -0,0 +1,53 @@
import { mergeClasses } from "@fluentui/react-components";
import { ReactNode, useState } from "react";
import style from "./split-pane.module.css";
export interface SashProps {
className?: string;
style: React.CSSProperties;
render: (dragging: boolean) => ReactNode;
onReset: () => void;
onDragStart: React.MouseEventHandler<HTMLDivElement>;
onDragging: React.MouseEventHandler<HTMLDivElement>;
onDragEnd: React.MouseEventHandler<HTMLDivElement>;
}
export const Sash = ({
className,
render,
onDragStart,
onDragging,
onDragEnd,
onReset,
...others
}: SashProps) => {
const [draging, setDrag] = useState(false);
const handleMouseMove = (e: any) => {
onDragging(e);
};
const handleMouseUp = (e: any) => {
setDrag(false);
onDragEnd(e);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
return (
<div
className={mergeClasses(style["sash"], className)}
onMouseDown={(e) => {
setDrag(true);
onDragStart(e);
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
}}
onDoubleClick={onReset}
{...others}
>
{render(draging)}
</div>
);
};

Просмотреть файл

@ -0,0 +1,58 @@
.split-pane {
flex: 1;
height: 100%;
position: relative;
width: 100%;
}
.split-disabled {
user-select: none;
}
.split-pane-dragging.react-split-vertical {
cursor: col-resize;
}
.split-pane-dragging.react-split-horizontal {
cursor: row-resize;
}
.sash {
height: 100%;
position: absolute;
top: 0;
transition: background-color 0.1s;
width: 100%;
z-index: 2;
}
.sash-disabled {
pointer-events: none;
}
.sash-vertical {
cursor: col-resize;
}
.sash-horizontal {
cursor: row-resize;
}
.sash-content {
width: 100%;
height: 100%;
background-color: #e5e5e5;
}
.sash-content-dragging,
.sash-content:hover {
background-color: #c3c3c3;
}
.pane {
height: 100%;
position: absolute;
white-space: normal;
width: 100%;
overflow: hidden;
}

Просмотреть файл

@ -0,0 +1,269 @@
import { mergeClasses } from "@fluentui/react-components";
import {
FunctionComponent,
JSX,
MouseEvent,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useControllableValue } from "../hooks.js";
import Pane, { PaneProps } from "./pane.js";
import { SashContent } from "./sash-content.js";
import { Sash } from "./sash.js";
import style from "./split-pane.module.css";
export interface SplitPaneProps {
children: JSX.Element[];
allowResize?: boolean;
split?: "vertical" | "horizontal";
initialSizes: (string | number)[];
sizes?: (string | number)[];
sashRender?: (index: number, active: boolean) => React.ReactNode;
onChange?: (sizes: number[]) => void;
onDragStart?: (e: MouseEvent) => void;
onDragEnd?: (e: MouseEvent) => void;
className?: string;
sashClassName?: string;
performanceMode?: boolean;
resizerSize?: number;
}
interface Axis {
x: number;
y: number;
}
interface CacheSizes {
sizes: (string | number)[];
sashPosSizes: (string | number)[];
}
export const SplitPane: FunctionComponent<SplitPaneProps> = ({
children,
sizes: propSizes,
initialSizes: defaultSizes,
allowResize = true,
split = "vertical",
className: wrapClassName,
sashRender = (_, active) => <SashContent dragging={active} />,
resizerSize = 4,
performanceMode = false,
onChange = () => null,
onDragStart = () => null,
onDragEnd = () => null,
...others
}: SplitPaneProps) => {
const [resolvedPropSize, updateSizes] = useControllableValue<(string | number)[]>(
propSizes,
defaultSizes,
onChange as any
);
const axis = useRef<Axis>({ x: 0, y: 0 });
const wrapper = useRef<HTMLDivElement>(null);
const cacheSizes = useRef<CacheSizes>({ sizes: [], sashPosSizes: [] });
const [wrapperRect, setWrapperRect] = useState<Record<string, number>>({});
const [isDragging, setDragging] = useState<boolean>(false);
useEffect(() => {
const resizeObserver = new ResizeObserver(() => {
setWrapperRect(wrapper?.current?.getBoundingClientRect() ?? ({} as any));
});
resizeObserver.observe(wrapper.current!);
return () => {
resizeObserver.disconnect();
};
}, []);
const { sizeName, splitPos, splitAxis } = useMemo(
() =>
({
sizeName: split === "vertical" ? "width" : "height",
splitPos: split === "vertical" ? "left" : "top",
splitAxis: split === "vertical" ? "x" : "y",
}) as const,
[split]
);
const wrapSize: number = wrapperRect[sizeName] ?? 0;
// Get limit sizes via children
const paneLimitSizes = useMemo(
() =>
children.map((childNode) => {
const limits = [0, Infinity];
if (childNode?.type === Pane) {
const { minSize, maxSize } = childNode.props as PaneProps;
limits[0] = assertsSize(minSize, wrapSize, 0);
limits[1] = assertsSize(maxSize, wrapSize);
}
return limits;
}),
[children, wrapSize]
);
const sizes = useMemo(
function () {
let count = 0;
let curSum = 0;
const res = children.map((_, index) => {
const size = assertsSize(resolvedPropSize[index], wrapSize);
size === Infinity ? count++ : (curSum += size);
return size;
});
// resize or illegal size input,recalculate pane sizes
if (curSum > wrapSize || (!count && curSum < wrapSize)) {
const cacheNum = (curSum - wrapSize) / curSum;
return res.map((size) => {
return size === Infinity ? 0 : size - size * cacheNum;
});
}
if (count > 0) {
const average = (wrapSize - curSum) / count;
return res.map((size) => {
return size === Infinity ? average : size;
});
}
return res;
},
[...resolvedPropSize, children.length, wrapSize]
);
const sashPosSizes = useMemo(
() => sizes.reduce((a, b) => [...a, a[a.length - 1] + b], [0]),
[...sizes]
);
const dragStart = useCallback(
(e: any) => {
document?.body?.classList?.add(style["split-disabled"]);
axis.current = { x: e.pageX, y: e.pageY };
cacheSizes.current = { sizes, sashPosSizes };
setDragging(true);
onDragStart(e);
},
[onDragStart, sizes, sashPosSizes]
);
const resetPosition = useCallback(() => {
updateSizes(defaultSizes);
}, [defaultSizes, updateSizes]);
const dragEnd = useCallback(
(e: any) => {
document?.body?.classList?.remove(style["split-disabled"]);
axis.current = { x: e.pageX, y: e.pageY };
cacheSizes.current = { sizes, sashPosSizes };
setDragging(false);
onDragEnd(e);
},
[onDragEnd, sizes, sashPosSizes]
);
const onDragging = useCallback(
function (e: MouseEvent<HTMLDivElement>, i: number) {
const curAxis = { x: e.pageX, y: e.pageY };
let distanceX = curAxis[splitAxis] - axis.current[splitAxis];
const leftBorder = -Math.min(
sizes[i] - paneLimitSizes[i][0],
paneLimitSizes[i + 1][1] - sizes[i + 1]
);
const rightBorder = Math.min(
sizes[i + 1] - paneLimitSizes[i + 1][0],
paneLimitSizes[i][1] - sizes[i]
);
if (distanceX < leftBorder) {
distanceX = leftBorder;
}
if (distanceX > rightBorder) {
distanceX = rightBorder;
}
const nextSizes = [...sizes];
nextSizes[i] += distanceX;
nextSizes[i + 1] -= distanceX;
updateSizes(nextSizes);
},
[paneLimitSizes, onChange]
);
const paneFollow = !(performanceMode && isDragging);
const paneSizes = paneFollow ? sizes : cacheSizes.current.sizes;
const panePoses = paneFollow ? sashPosSizes : cacheSizes.current.sashPosSizes;
return (
<div
className={mergeClasses(
style["split-pane"],
split === "vertical" && style["split-pane-vertical"],
split === "horizontal" && style["split-pane-horizontal"],
isDragging && style["split-pane-dragging"],
wrapClassName
)}
ref={wrapper}
{...others}
>
{children.map((childNode, childIndex) => {
const isPane = childNode.type === Pane;
const paneProps = isPane ? childNode.props : {};
return (
<Pane
key={childIndex}
className={mergeClasses(style["pane"], paneProps.className)}
style={{
...paneProps.style,
[sizeName]: paneSizes[childIndex],
[splitPos]: panePoses[childIndex],
}}
>
{isPane ? paneProps.children : childNode}
</Pane>
);
})}
{sashPosSizes.slice(1, -1).map((posSize, index) => (
<Sash
key={index}
className={mergeClasses(
!allowResize && style["sash-disabled"],
split === "vertical" ? style["sash-vertical"] : style["sash-horizontal"]
)}
style={{
[sizeName]: resizerSize,
[splitPos]: posSize - resizerSize / 2,
}}
render={sashRender.bind(null, index)}
onReset={resetPosition}
onDragStart={dragStart}
onDragging={(e) => onDragging(e, index)}
onDragEnd={dragEnd}
/>
))}
</div>
);
};
/**
* Convert size to absolute number or Infinity
* SplitPane allows sizes in string and number, but the state sizes only support number,
* so convert string and number to number in here
* 'auto' -> divide the remaining space equally
* 'xxx px' -> xxx
* 'xxx%' -> wrapper.size * xxx/100
* xxx -> xxx
*/
function assertsSize(size: string | number | undefined, sum: number, defaultValue = Infinity) {
if (typeof size === "undefined") return defaultValue;
if (typeof size === "number") return size;
if (size.endsWith("%")) return sum * (+size.replace("%", "") / 100);
if (size.endsWith("px")) return +size.replace("px", "");
return defaultValue;
}