Skip to content
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
109 changes: 109 additions & 0 deletions dev/vscode-split-layout/min-size.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Split Layout - Min Size</title>
<link
rel="stylesheet"
href="/node_modules/@vscode/codicons/dist/codicon.css"
id="vscode-codicon-stylesheet"
>
<script
type="module"
src="/node_modules/@vscode-elements/webview-playground/dist/index.js"
></script>
<script type="module" src="/dist/vscode-split-layout/index.js"></script>
<script type="module" src="/dist/vscode-scrollable/index.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.4;
margin: 24px;
}

vscode-demo {
display: block;
margin-bottom: 32px;
}

.pane {
border: 1px solid #555;
box-sizing: border-box;
padding: 12px;
}

.pane.start {
background: #252526;
color: #f0f0f0;
}

.pane.end {
background: #1e1e1e;
color: #d0d0d0;
}
</style>
</head>

<body>
<h1>Split Layout - Min Size Examples</h1>
<p>
These demos show how <code>min-start</code> and <code>min-end</code> clamp
the sash so panes never collapse below sensible limits. Pixels and
percentages can be combined to match your layout.
</p>

<h2>Vertical mix: 240px start, 25% end</h2>
<p>
Drag the handle and notice how the master pane refuses to shrink past
<strong>240px</strong> while the detail pane keeps <strong>25%</strong> of
the width.
</p>
<vscode-demo>
<vscode-split-layout
style="width: 520px; height: 280px"
min-start="240px"
min-end="25%"
>
<div slot="start" class="pane start">
<h3>Sources</h3>
<p>This slot refuses to shrink past 240px.</p>
<p>
Try dragging the handle all the way left—the clamp keeps the list
readable.
</p>
</div>
<div slot="end" class="pane end">
<h3>Details</h3>
<p>
This pane holds onto 25% of the available width for preview content.
</p>
</div>
</vscode-split-layout>
</vscode-demo>

<h2>Horizontal ratio clamp (30%)</h2>
<p>
Here both panes use percentage-based minimums to keep toolbars usable in a
horizontal split.
</p>
<vscode-demo>
<vscode-split-layout
split="horizontal"
style="width: 520px; height: 320px"
initial-handle-position="40%"
min-start="30%"
min-end="30%"
>
<div slot="start" class="pane start">
<h3>Console</h3>
<p>The console keeps at least 30% height so filters don’t vanish.</p>
</div>
<div slot="end" class="pane end">
<h3>Inspector</h3>
<p>The inspector pane also clamps at 30% to preserve actions.</p>
</div>
</vscode-split-layout>
</vscode-demo>
</body>
</html>
136 changes: 136 additions & 0 deletions src/vscode-split-layout/vscode-split-layout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,142 @@ describe('vscode-split-layout', () => {
});
});

describe('min size constraints', () => {
it('prevents dragging the start pane below the configured pixel minimum', async () => {
const el = await fixture<VscodeSplitLayout>(
html`<vscode-split-layout
style="width: 500px; height: 300px;"
initial-handle-position="400px"
min-start="240px"
></vscode-split-layout>`
);

const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
const startPane = el.shadowRoot?.querySelector(
'.start'
) as HTMLDivElement;
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;

await dragElement(handle, -300);

expect(startPane.offsetWidth).to.eq(240);
expect(endPane.offsetWidth).to.eq(260);
});

it('prevents dragging the end pane below the configured pixel minimum', async () => {
const el = await fixture<VscodeSplitLayout>(
html`<vscode-split-layout
style="width: 500px; height: 300px;"
initial-handle-position="200px"
min-end="180px"
></vscode-split-layout>`
);

const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
const startPane = el.shadowRoot?.querySelector(
'.start'
) as HTMLDivElement;
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;

await dragElement(handle, 300);

expect(startPane.offsetWidth).to.eq(320);
expect(endPane.offsetWidth).to.eq(180);
});

it('applies percentage-based minimums when handlePosition changes programmatically', async () => {
const el = await fixture<VscodeSplitLayout>(
html`<vscode-split-layout
style="width: 600px; height: 300px;"
initial-handle-position="50%"
min-start="30%"
min-end="25%"
></vscode-split-layout>`
);

const startPane = el.shadowRoot?.querySelector(
'.start'
) as HTMLDivElement;
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;

el.handlePosition = '0px';
await el.updateComplete;

expect(startPane.offsetWidth).to.eq(180);
expect(endPane.offsetWidth).to.eq(420);

el.handlePosition = '100%';
await el.updateComplete;

expect(startPane.offsetWidth).to.eq(450);
expect(endPane.offsetWidth).to.eq(150);
});

it('respects minimum sizes in horizontal layouts', async () => {
const el = await fixture<VscodeSplitLayout>(
html`<vscode-split-layout
style="width: 400px; height: 400px;"
split="horizontal"
initial-handle-position="300px"
min-start="150px"
></vscode-split-layout>`
);

const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
const startPane = el.shadowRoot?.querySelector(
'.start'
) as HTMLDivElement;
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;

await dragElement(handle, 0, -250);

expect(startPane.offsetHeight).to.eq(150);
expect(endPane.offsetHeight).to.eq(250);
});

it('handles overlapping minimums without crashing', async () => {
const el = await fixture<VscodeSplitLayout>(
html`<vscode-split-layout
style="width: 400px; height: 300px;"
min-start="110%"
min-end="110%"
></vscode-split-layout>`
);

const startPane = el.shadowRoot?.querySelector(
'.start'
) as HTMLDivElement;
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;

expect(startPane.offsetWidth).to.be.closeTo(400, 1);
expect(endPane.offsetWidth).to.be.closeTo(0, 1);
});

it('accepts zero minimum values', async () => {
const el = await fixture<VscodeSplitLayout>(
html`<vscode-split-layout
style="width: 400px; height: 300px;"
min-start="0px"
min-end="0%"
></vscode-split-layout>`
);

const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
const startPane = el.shadowRoot?.querySelector(
'.start'
) as HTMLDivElement;
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;

await dragElement(handle, -400);
expect(startPane.offsetWidth).to.be.closeTo(0, 1);
expect(endPane.offsetWidth).to.be.closeTo(400, 1);

await dragElement(handle, 400);
expect(startPane.offsetWidth).to.be.closeTo(400, 1);
expect(endPane.offsetWidth).to.be.closeTo(0, 1);
});
});

describe('interactions', () => {
it('should panes resize in vertical mode', async () => {
const el = await fixture<VscodeSplitLayout>(
Expand Down
Loading