I'm building a Splitter component in angular, using the @angular/cdk/portals. Everything works as expected, but I'm still having trouble with the resizing of the splitter. When moving the thumb, the offset increases for no apparent reason
I've created a complete reproduction here. The project code is hosted here. And a live demo can be found here.
The splitter consists of panels inside a flexbox:
<div class="d-flex w-100" [class]="splitter-horizontal">
<ng-content></ng-content>
<ng-container *ngFor="let panel of (panels$ | async); let index = index; let last = last">
<ng-container *bsLet="(widthStyles$ | async) as widthStyles">
<div class="split-panel overflow-hidden" #splitPanel [style.width]="widthStyles | bsElementAt:index">
<ng-template [cdkPortalOutlet]="panel.portal"></ng-template>
</div>
</ng-container>
<div class="divider" (mousedown)="startResize($event, index, index + 1)" *ngIf="!last"></div>
</ng-container>
</div>
startResize
will compute the rendered widths once, and replace the width: 100%
with the computed width for each panel:
const sizes = this.splitPanels
.map((sp) => {
const styles = window.getComputedStyle(sp.nativeElement);
return styles.width;
})
.map((size) => size.slice(0, -2))
.map((size) => parseFloat(size));
this.previewSizes$.next(sizes);
And the widthStyles$
are coming from the previewSizes$
:
this.widthStyles$ = combineLatest([this.orientation$, this.previewSizes$, this.panels$])
.pipe(map(([orientation, previewSizes, panels]) => {
if (previewSizes) {
return [...Array(panels.length).keys()].map((v, i) => {
if (i < previewSizes.length) {
return previewSizes[i] + 'px';
} else {
return '100%'; // Equal width panels
}
});
} else {
return Array(panels.length).map((v, i) => '100%');
}
}));
On MouseMove, I'm updating a BehaviorSubject
:
@HostListener('document:mousemove', ['$event'])
onMouseMove(ev: MouseEvent) {
this.mousePosition$.next({ x: ev.offsetX, y: ev.offsetY });
}
And the rest of startResize
is meant to update the previewSizes$
:
this.mousePosition$.pipe(delay(5), takeUntil(this.stopResize$)).subscribe((mousePosition) => {
const deltaX = mousePosition!.x - this.operation.startPosition.x;
const sx = Array.from(this.operation.sizes);
sx[this.operation.indexBefore] = this.operation.sizes[this.operation.indexBefore] + deltaX;
sx[this.operation.indexAfter] = this.operation.sizes[this.operation.indexAfter] - deltaX;
this.previewSizes$.next(sx);
});
For some reason the splitter is still growing faster than I'm moving my mouse. I've already tried using debounce()
which is why I created the mousePosition$
BehaviorSubject in the first place.
What am I doing wrong?
Okay, I just had to use ev.clientX
instead of ev.offsetX
and the problem was fixed