Skip to content

Commit 0faa738

Browse files
MayaKirovaMKirovadkamburovzdrawkuDiyanDimitrov
authored
Add support for splitter % resize. (#8578)
* feat(igxSplitter): Support resize in % for panes that have % size or no size. Co-authored-by: MKirova <MKirova@DEV-MKIROVA> Co-authored-by: Deyan Kamburov <[email protected]> Co-authored-by: Zdravko Kolev <[email protected]> Co-authored-by: Diyan Dimitrov <[email protected]> Co-authored-by: Radoslav Karaivanov <[email protected]>
1 parent 81e9c9d commit 0faa738

File tree

7 files changed

+152
-40
lines changed

7 files changed

+152
-40
lines changed

projects/igniteui-angular/src/lib/splitter/splitter-bar/splitter-bar.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
[dragDirection]='dragDir'
77
(dragStart)='onDragStart($event)'
88
(dragMove)="onDragMove($event)"
9+
(dragEnd)="onDragEnd($event)"
910
>
1011
<div class="igx-splitter-bar__expander--start" igxDragIgnore (click)='onCollapsing(false)' [hidden]='prevButtonHidden'></div>
1112
<div class="igx-splitter-bar__handle" ></div>

projects/igniteui-angular/src/lib/splitter/splitter-bar/splitter-bar.component.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ export class IgxSplitBarComponent {
8888
@Output()
8989
public moving = new EventEmitter<number>();
9090

91+
@Output()
92+
public movingEnd = new EventEmitter<number>();
93+
9194
/**
9295
* A temporary holder for the pointer coordinates.
9396
*/
@@ -213,6 +216,15 @@ export class IgxSplitBarComponent {
213216
}
214217
}
215218

219+
public onDragEnd(event: any) {
220+
const isHorizontal = this.type === SplitterType.Horizontal;
221+
const curr = isHorizontal ? event.pageX : event.pageY;
222+
const delta = this.startPoint - curr;
223+
if (delta !== 0) {
224+
this.movingEnd.emit(delta);
225+
}
226+
}
227+
216228
protected get resizeDisallowed() {
217229
const relatedTabs = this.siblings;
218230
return !!relatedTabs.find(x => x.resizable === false || x.collapsed === true);

projects/igniteui-angular/src/lib/splitter/splitter-pane/splitter-pane.component.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Component, HostBinding, Input, ElementRef, Output, EventEmitter } from
2020
export class IgxSplitterPaneComponent {
2121

2222
private _size = 'auto';
23+
private _dragSize;
2324
private _collapsed = false;
2425

2526
/** @hidden @internal */
@@ -44,6 +45,20 @@ export class IgxSplitterPaneComponent {
4445
this.el.nativeElement.style.flex = this.flex;
4546
}
4647

48+
/** @hidden @internal */
49+
get isPercentageSize() {
50+
return this.size === 'auto' || this.size.indexOf('%') !== -1;
51+
}
52+
53+
/** @hidden @internal */
54+
get dragSize() {
55+
return this._dragSize;
56+
}
57+
set dragSize(val) {
58+
this._dragSize = val;
59+
this.el.nativeElement.style.flex = this.flex;
60+
}
61+
4762
/**
4863
* Gets/Sets the minimum allowed size of the current pane.
4964
* @example
@@ -137,10 +152,10 @@ export class IgxSplitterPaneComponent {
137152
*/
138153
@HostBinding('style.flex')
139154
public get flex() {
140-
const grow = this.size !== 'auto' ? 0 : 1;
141-
const shrink = this.size !== 'auto' ? 0 : 1;
142-
143-
return `${grow} ${shrink} ${this.size}`;
155+
const isAuto = this.size === 'auto' && !this.dragSize;
156+
const grow = !isAuto ? 0 : 1;
157+
const size = this.dragSize || this.size;
158+
return `${grow} ${grow} ${size}`;
144159
}
145160

146161
/**

projects/igniteui-angular/src/lib/splitter/splitter.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[pane]="pane"
66
[siblings]='getPaneSiblingsByOrder(pane.order + 1, index)'
77
(moveStart)="onMoveStart($event)"
8-
(moving)="onMoving($event)">
8+
(moving)="onMoving($event)"
9+
(movingEnd)='onMoveEnd($event)'>
910
</igx-splitter-bar>
1011
</ng-container>

projects/igniteui-angular/src/lib/splitter/splitter.component.spec.ts

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ describe('IgxSplitter', () => {
6969
splitterBarComponent.moveStart.emit(pane1);
7070
splitterBarComponent.moving.emit(-100);
7171
fixture.detectChanges();
72-
expect(pane1.size).toBe(pane1_originalSize + 100 + 'px');
73-
expect(pane2.size).toBe(pane2_originalSize - 100 + 'px');
72+
expect(pane1.dragSize).toBe(pane1_originalSize + 100 + 'px');
73+
expect(pane2.dragSize).toBe(pane2_originalSize - 100 + 'px');
7474

7575
splitterBarComponent.moving.emit(100);
7676
fixture.detectChanges();
77-
expect(pane1.size).toBe(pane1_originalSize - 100 + 'px');
78-
expect(pane2.size).toBe(pane2_originalSize + 100 + 'px');
77+
expect(pane1.dragSize).toBe(pane1_originalSize - 100 + 'px');
78+
expect(pane2.dragSize).toBe(pane2_originalSize + 100 + 'px');
7979
});
8080
it('should allow resizing horizontal splitter', () => {
8181
const pane1 = splitter.panes.toArray()[0];
@@ -89,13 +89,13 @@ describe('IgxSplitter', () => {
8989
splitterBarComponent.moving.emit(-100);
9090
fixture.detectChanges();
9191

92-
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize + 100, 0);
93-
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize - 100, 0);
92+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize + 100, 0);
93+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize - 100, 0);
9494

9595
splitterBarComponent.moving.emit(100);
9696
fixture.detectChanges();
97-
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize - 100, 0);
98-
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize + 100, 0);
97+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize - 100, 0);
98+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize + 100, 0);
9999
});
100100
it('should honor minSize/maxSize when resizing.', () => {
101101
fixture.componentInstance.type = SplitterType.Vertical;
@@ -112,16 +112,16 @@ describe('IgxSplitter', () => {
112112
splitterBarComponent.moveStart.emit(pane1);
113113
splitterBarComponent.moving.emit(100);
114114
fixture.detectChanges();
115-
expect(pane1.size).toBe('100px');
116-
expect(pane2.size).toBe('300px');
115+
expect(pane1.dragSize).toBe('100px');
116+
expect(pane2.dragSize).toBe('300px');
117117

118118
splitterBarComponent.moveStart.emit(pane1);
119119
splitterBarComponent.moving.emit(-200);
120120
splitterBarComponent.moveStart.emit(pane1);
121121
splitterBarComponent.moving.emit(-50);
122122
fixture.detectChanges();
123-
expect(pane1.size).toBe('300px');
124-
expect(pane2.size).toBe('100px');
123+
expect(pane1.dragSize).toBe('300px');
124+
expect(pane2.dragSize).toBe('100px');
125125
});
126126

127127
it('should not allow drag resize if resizable is set to false.', () => {
@@ -149,20 +149,20 @@ describe('IgxSplitter', () => {
149149
splitterBarComponent.nativeElement.focus();
150150
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', splitterBarComponent);
151151
fixture.detectChanges();
152-
expect(pane1.size).toBe(pane1_originalSize - 10 + 'px');
153-
expect(pane2.size).toBe(pane2_originalSize + 10 + 'px');
152+
expect(pane1.dragSize).toBe(pane1_originalSize - 10 + 'px');
153+
expect(pane2.dragSize).toBe(pane2_originalSize + 10 + 'px');
154154

155155
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent);
156156
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent);
157157
fixture.detectChanges();
158-
expect(pane1.size).toBe(pane1_originalSize + 10 + 'px');
159-
expect(pane2.size).toBe(pane2_originalSize - 10 + 'px');
158+
expect(pane1.dragSize).toBe(pane1_originalSize + 10 + 'px');
159+
expect(pane2.dragSize).toBe(pane2_originalSize - 10 + 'px');
160160

161161
pane2.resizable = false;
162162
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent);
163163
fixture.detectChanges();
164-
expect(pane1.size).toBe(pane1_originalSize + 10 + 'px');
165-
expect(pane2.size).toBe(pane2_originalSize - 10 + 'px');
164+
expect(pane1.dragSize).toBe(pane1_originalSize + 10 + 'px');
165+
expect(pane2.dragSize).toBe(pane2_originalSize - 10 + 'px');
166166
});
167167

168168
it('should allow resizing with left/right arrow keys', () => {
@@ -178,20 +178,20 @@ describe('IgxSplitter', () => {
178178
splitterBarComponent.nativeElement.focus();
179179
UIInteractions.triggerEventHandlerKeyDown('ArrowLeft', splitterBarComponent);
180180
fixture.detectChanges();
181-
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize - 10, 0);
182-
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize + 10, 0);
181+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize - 10, 0);
182+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize + 10, 0);
183183

184184
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent);
185185
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent);
186186
fixture.detectChanges();
187-
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize + 10, 0);
188-
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize - 10, 0);
187+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize + 10, 0);
188+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize - 10, 0);
189189

190190
pane1.resizable = false;
191191
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent);
192192
fixture.detectChanges();
193-
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize + 10, 0);
194-
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize - 10, 0);
193+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize + 10, 0);
194+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize - 10, 0);
195195
});
196196

197197
it('should allow expand/collapse with Ctrl + up/down arrow keys', () => {
@@ -240,6 +240,60 @@ describe('IgxSplitter', () => {
240240
expect(pane2.collapsed).toBeFalsy();
241241
});
242242

243+
it('should allow resize in % when pane size is auto.', () => {
244+
const pane1 = splitter.panes.toArray()[0];
245+
const pane2 = splitter.panes.toArray()[1];
246+
expect(pane1.size).toBe('auto');
247+
expect(pane2.size).toBe('auto');
248+
const pane1_originalSize = pane1.element.offsetWidth;
249+
const pane2_originalSize = pane2.element.offsetWidth;
250+
const splitterBarComponent = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS)).context;
251+
splitterBarComponent.moveStart.emit(pane1);
252+
splitterBarComponent.moving.emit(-100);
253+
fixture.detectChanges();
254+
255+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize + 100, 0);
256+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize - 100, 0);
257+
258+
// on move end convert to % value and apply to size.
259+
splitterBarComponent.movingEnd.emit(-100);
260+
fixture.detectChanges();
261+
262+
expect(pane1.size.indexOf('%') !== -1).toBeTrue();
263+
expect(pane2.size.indexOf('%') !== -1).toBeTrue();
264+
265+
expect(pane1.element.offsetWidth).toBeCloseTo(pane1_originalSize + 100);
266+
expect(pane2.element.offsetWidth).toBeCloseTo(pane2_originalSize - 100);
267+
});
268+
269+
it('should allow mixing % and px sizes.', () => {
270+
const pane1 = splitter.panes.toArray()[0];
271+
const pane2 = splitter.panes.toArray()[1];
272+
pane1.size = '200px';
273+
fixture.detectChanges();
274+
275+
const pane1_originalSize = pane1.element.offsetWidth;
276+
const pane2_originalSize = pane2.element.offsetWidth;
277+
const splitterBarComponent = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS)).context;
278+
splitterBarComponent.moveStart.emit(pane1);
279+
splitterBarComponent.moving.emit(-100);
280+
fixture.detectChanges();
281+
282+
expect(parseFloat(pane1.dragSize)).toBeCloseTo(pane1_originalSize + 100, 0);
283+
expect(parseFloat(pane2.dragSize)).toBeCloseTo(pane2_originalSize - 100, 0);
284+
285+
// on move end convert to % value and apply to size.
286+
splitterBarComponent.movingEnd.emit(-100);
287+
fixture.detectChanges();
288+
289+
// fist pane should remain in px
290+
expect(pane1.size).toBe('300px');
291+
expect(pane2.size.indexOf('%') !== -1).toBeTrue();
292+
293+
expect(pane1.element.offsetWidth).toBeCloseTo(pane1_originalSize + 100);
294+
expect(pane2.element.offsetWidth).toBeCloseTo(pane2_originalSize - 100);
295+
});
296+
243297
});
244298

245299
describe('IgxSplitter pane toggle', () => {

projects/igniteui-angular/src/lib/splitter/splitter.component.ts

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Component, QueryList, Input, ContentChildren, AfterContentInit, HostBinding, Output, EventEmitter } from '@angular/core';
1+
import { Component, QueryList, Input, ContentChildren, AfterContentInit, HostBinding, Output, EventEmitter, Inject, ElementRef } from '@angular/core';
22
import { IgxSplitterPaneComponent } from './splitter-pane/splitter-pane.component';
3+
import { DOCUMENT } from '@angular/common';
34

45
/**
56
* An enumeration that defines the `SplitterComponent` panes orientation.
@@ -40,6 +41,8 @@ export enum SplitterType {
4041
})
4142
export class IgxSplitterComponent implements AfterContentInit {
4243
private _type: SplitterType = SplitterType.Horizontal;
44+
45+
constructor(@Inject(DOCUMENT) public document, private elementRef: ElementRef) {}
4346
/**
4447
* Gets/Sets the splitter orientation.
4548
* @example
@@ -138,15 +141,9 @@ export class IgxSplitterComponent implements AfterContentInit {
138141

139142
const paneRect = this.pane.element.getBoundingClientRect();
140143
this.initialPaneSize = this.type === SplitterType.Horizontal ? paneRect.width : paneRect.height;
141-
if (this.pane.size === 'auto') {
142-
this.pane.size = this.type === SplitterType.Horizontal ? paneRect.width : paneRect.height;
143-
}
144144

145145
const siblingRect = this.sibling.element.getBoundingClientRect();
146146
this.initialSiblingSize = this.type === SplitterType.Horizontal ? siblingRect.width : siblingRect.height;
147-
if (this.sibling.size === 'auto') {
148-
this.sibling.size = this.type === SplitterType.Horizontal ? siblingRect.width : siblingRect.height;
149-
}
150147
}
151148

152149
/**
@@ -165,11 +162,43 @@ export class IgxSplitterComponent implements AfterContentInit {
165162
if (paneSize < min || paneSize > max || siblingSize < minSibling || siblingSize > maxSibling) {
166163
return;
167164
}
165+
this.pane.dragSize = paneSize + 'px';
166+
this.sibling.dragSize = siblingSize + 'px';
167+
}
168+
169+
public onMoveEnd(delta: number) {
170+
const paneSize = this.initialPaneSize - delta;
171+
const siblingSize = this.initialSiblingSize + delta;
172+
if (this.pane.isPercentageSize) {
173+
// handle % resizes
174+
const totalSize = this.getTotalSize();
175+
const percentPaneSize = (paneSize / totalSize) * 100;
176+
this.pane.size = percentPaneSize + '%';
177+
} else {
178+
// px resize
179+
this.pane.size = paneSize + 'px';
180+
}
181+
182+
if (this.sibling.isPercentageSize) {
183+
// handle % resizes
184+
const totalSize = this.getTotalSize();
185+
const percentSiblingPaneSize = (siblingSize / totalSize) * 100;
186+
this.sibling.size = percentSiblingPaneSize + '%';
187+
} else {
188+
// px resize
189+
this.sibling.size = siblingSize + 'px';
190+
}
191+
this.pane.dragSize = null;
192+
this.sibling.dragSize = null;
193+
}
168194

169-
this.pane.size = paneSize + 'px';
170-
this.sibling.size = siblingSize + 'px';
195+
private getTotalSize() {
196+
const computed = this.document.defaultView.getComputedStyle(this.elementRef.nativeElement);
197+
const totalSize = this.type === SplitterType.Horizontal ? computed.getPropertyValue('width') : computed.getPropertyValue('height');
198+
return parseFloat(totalSize);
171199
}
172200

201+
173202
/**
174203
* @hidden @internal
175204
* This method assigns the order of each pane.

src/app/splitter/splitter.sample.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<igx-switch (change)='changeType()' style="padding-left: 10px">Toggle Splitter Direction</igx-switch>
22
<div>Simple sample</div>
33
<igx-splitter style='height: 30vh;'>
4-
<igx-splitter-pane [resizable]='false'>
4+
<igx-splitter-pane size='20%'>
55
<div style='width:100%;'>
66
Pane 1
77
</div>
88
</igx-splitter-pane>
9-
<igx-splitter-pane>
9+
<igx-splitter-pane size='200px'>
1010
<div style='width:100%;'>
1111
Pane 2
1212
</div>

0 commit comments

Comments
 (0)