Skip to content

Commit 47e54b4

Browse files
dankeboy36bendera
authored andcommitted
fix: add missing minStart/minEnd to split-layout
Closes: #552 Signed-off-by: dankeboy36 <dankeboy36@gmail.com>
1 parent eda2fab commit 47e54b4

3 files changed

Lines changed: 351 additions & 17 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Split Layout - Min Size</title>
7+
<link
8+
rel="stylesheet"
9+
href="/node_modules/@vscode/codicons/dist/codicon.css"
10+
id="vscode-codicon-stylesheet"
11+
>
12+
<script
13+
type="module"
14+
src="/node_modules/@vscode-elements/webview-playground/dist/index.js"
15+
></script>
16+
<script type="module" src="/dist/vscode-split-layout/index.js"></script>
17+
<script type="module" src="/dist/vscode-scrollable/index.js"></script>
18+
<style>
19+
body {
20+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
21+
line-height: 1.4;
22+
margin: 24px;
23+
}
24+
25+
vscode-demo {
26+
display: block;
27+
margin-bottom: 32px;
28+
}
29+
30+
.pane {
31+
border: 1px solid #555;
32+
box-sizing: border-box;
33+
padding: 12px;
34+
}
35+
36+
.pane.start {
37+
background: #252526;
38+
color: #f0f0f0;
39+
}
40+
41+
.pane.end {
42+
background: #1e1e1e;
43+
color: #d0d0d0;
44+
}
45+
</style>
46+
</head>
47+
48+
<body>
49+
<h1>Split Layout - Min Size Examples</h1>
50+
<p>
51+
These demos show how <code>min-start</code> and <code>min-end</code> clamp
52+
the sash so panes never collapse below sensible limits. Pixels and
53+
percentages can be combined to match your layout.
54+
</p>
55+
56+
<h2>Vertical mix: 240px start, 25% end</h2>
57+
<p>
58+
Drag the handle and notice how the master pane refuses to shrink past
59+
<strong>240px</strong> while the detail pane keeps <strong>25%</strong> of
60+
the width.
61+
</p>
62+
<vscode-demo>
63+
<vscode-split-layout
64+
style="width: 520px; height: 280px"
65+
min-start="240px"
66+
min-end="25%"
67+
>
68+
<div slot="start" class="pane start">
69+
<h3>Sources</h3>
70+
<p>This slot refuses to shrink past 240px.</p>
71+
<p>
72+
Try dragging the handle all the way left—the clamp keeps the list
73+
readable.
74+
</p>
75+
</div>
76+
<div slot="end" class="pane end">
77+
<h3>Details</h3>
78+
<p>
79+
This pane holds onto 25% of the available width for preview content.
80+
</p>
81+
</div>
82+
</vscode-split-layout>
83+
</vscode-demo>
84+
85+
<h2>Horizontal ratio clamp (30%)</h2>
86+
<p>
87+
Here both panes use percentage-based minimums to keep toolbars usable in a
88+
horizontal split.
89+
</p>
90+
<vscode-demo>
91+
<vscode-split-layout
92+
split="horizontal"
93+
style="width: 520px; height: 320px"
94+
initial-handle-position="40%"
95+
min-start="30%"
96+
min-end="30%"
97+
>
98+
<div slot="start" class="pane start">
99+
<h3>Console</h3>
100+
<p>The console keeps at least 30% height so filters don’t vanish.</p>
101+
</div>
102+
<div slot="end" class="pane end">
103+
<h3>Inspector</h3>
104+
<p>The inspector pane also clamps at 30% to preserve actions.</p>
105+
</div>
106+
</vscode-split-layout>
107+
</vscode-demo>
108+
</body>
109+
</html>

‎src/vscode-split-layout/vscode-split-layout.test.ts‎

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,142 @@ describe('vscode-split-layout', () => {
389389
});
390390
});
391391

392+
describe('min size constraints', () => {
393+
it('prevents dragging the start pane below the configured pixel minimum', async () => {
394+
const el = await fixture<VscodeSplitLayout>(
395+
html`<vscode-split-layout
396+
style="width: 500px; height: 300px;"
397+
initial-handle-position="400px"
398+
min-start="240px"
399+
></vscode-split-layout>`
400+
);
401+
402+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
403+
const startPane = el.shadowRoot?.querySelector(
404+
'.start'
405+
) as HTMLDivElement;
406+
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;
407+
408+
await dragElement(handle, -300);
409+
410+
expect(startPane.offsetWidth).to.eq(240);
411+
expect(endPane.offsetWidth).to.eq(260);
412+
});
413+
414+
it('prevents dragging the end pane below the configured pixel minimum', async () => {
415+
const el = await fixture<VscodeSplitLayout>(
416+
html`<vscode-split-layout
417+
style="width: 500px; height: 300px;"
418+
initial-handle-position="200px"
419+
min-end="180px"
420+
></vscode-split-layout>`
421+
);
422+
423+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
424+
const startPane = el.shadowRoot?.querySelector(
425+
'.start'
426+
) as HTMLDivElement;
427+
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;
428+
429+
await dragElement(handle, 300);
430+
431+
expect(startPane.offsetWidth).to.eq(320);
432+
expect(endPane.offsetWidth).to.eq(180);
433+
});
434+
435+
it('applies percentage-based minimums when handlePosition changes programmatically', async () => {
436+
const el = await fixture<VscodeSplitLayout>(
437+
html`<vscode-split-layout
438+
style="width: 600px; height: 300px;"
439+
initial-handle-position="50%"
440+
min-start="30%"
441+
min-end="25%"
442+
></vscode-split-layout>`
443+
);
444+
445+
const startPane = el.shadowRoot?.querySelector(
446+
'.start'
447+
) as HTMLDivElement;
448+
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;
449+
450+
el.handlePosition = '0px';
451+
await el.updateComplete;
452+
453+
expect(startPane.offsetWidth).to.eq(180);
454+
expect(endPane.offsetWidth).to.eq(420);
455+
456+
el.handlePosition = '100%';
457+
await el.updateComplete;
458+
459+
expect(startPane.offsetWidth).to.eq(450);
460+
expect(endPane.offsetWidth).to.eq(150);
461+
});
462+
463+
it('respects minimum sizes in horizontal layouts', async () => {
464+
const el = await fixture<VscodeSplitLayout>(
465+
html`<vscode-split-layout
466+
style="width: 400px; height: 400px;"
467+
split="horizontal"
468+
initial-handle-position="300px"
469+
min-start="150px"
470+
></vscode-split-layout>`
471+
);
472+
473+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
474+
const startPane = el.shadowRoot?.querySelector(
475+
'.start'
476+
) as HTMLDivElement;
477+
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;
478+
479+
await dragElement(handle, 0, -250);
480+
481+
expect(startPane.offsetHeight).to.eq(150);
482+
expect(endPane.offsetHeight).to.eq(250);
483+
});
484+
485+
it('handles overlapping minimums without crashing', async () => {
486+
const el = await fixture<VscodeSplitLayout>(
487+
html`<vscode-split-layout
488+
style="width: 400px; height: 300px;"
489+
min-start="110%"
490+
min-end="110%"
491+
></vscode-split-layout>`
492+
);
493+
494+
const startPane = el.shadowRoot?.querySelector(
495+
'.start'
496+
) as HTMLDivElement;
497+
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;
498+
499+
expect(startPane.offsetWidth).to.be.closeTo(400, 1);
500+
expect(endPane.offsetWidth).to.be.closeTo(0, 1);
501+
});
502+
503+
it('accepts zero minimum values', async () => {
504+
const el = await fixture<VscodeSplitLayout>(
505+
html`<vscode-split-layout
506+
style="width: 400px; height: 300px;"
507+
min-start="0px"
508+
min-end="0%"
509+
></vscode-split-layout>`
510+
);
511+
512+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLDivElement;
513+
const startPane = el.shadowRoot?.querySelector(
514+
'.start'
515+
) as HTMLDivElement;
516+
const endPane = el.shadowRoot?.querySelector('.end') as HTMLDivElement;
517+
518+
await dragElement(handle, -400);
519+
expect(startPane.offsetWidth).to.be.closeTo(0, 1);
520+
expect(endPane.offsetWidth).to.be.closeTo(400, 1);
521+
522+
await dragElement(handle, 400);
523+
expect(startPane.offsetWidth).to.be.closeTo(400, 1);
524+
expect(endPane.offsetWidth).to.be.closeTo(0, 1);
525+
});
526+
});
527+
392528
describe('interactions', () => {
393529
it('should panes resize in vertical mode', async () => {
394530
const el = await fixture<VscodeSplitLayout>(

0 commit comments

Comments
 (0)