724 lines
36 KiB
JavaScript
724 lines
36 KiB
JavaScript
import { Directionality } from '@angular/cdk/bidi';
|
|
import { coerceCssPixelValue } from '@angular/cdk/coercion';
|
|
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
|
|
import { NgTemplateOutlet } from '@angular/common';
|
|
import * as i0 from '@angular/core';
|
|
import { input, output, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, booleanAttribute, viewChild, inject, ElementRef, DOCUMENT, contentChildren, linkedSignal, signal, NgModule } from '@angular/core';
|
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
import { map, Subject, merge, takeUntil } from 'rxjs';
|
|
import { startWith, pairwise } from 'rxjs/operators';
|
|
import { NzResizeObserver } from 'ng-zorro-antd/cdk/resize-observer';
|
|
import { NzDestroyService } from 'ng-zorro-antd/core/services';
|
|
import { fromEventOutsideAngular } from 'ng-zorro-antd/core/util';
|
|
import { getEventWithPoint } from 'ng-zorro-antd/resizable';
|
|
import * as i1 from 'ng-zorro-antd/icon';
|
|
import { NzIconModule } from 'ng-zorro-antd/icon';
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
class NzSplitterBarComponent {
|
|
ariaNow = input.required(...(ngDevMode ? [{ debugName: "ariaNow" }] : []));
|
|
ariaMin = input.required(...(ngDevMode ? [{ debugName: "ariaMin" }] : []));
|
|
ariaMax = input.required(...(ngDevMode ? [{ debugName: "ariaMax" }] : []));
|
|
active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : []));
|
|
resizable = input(true, ...(ngDevMode ? [{ debugName: "resizable" }] : []));
|
|
vertical = input(...(ngDevMode ? [undefined, { debugName: "vertical" }] : []));
|
|
lazy = input(false, ...(ngDevMode ? [{ debugName: "lazy" }] : []));
|
|
collapsible = input(...(ngDevMode ? [undefined, { debugName: "collapsible" }] : []));
|
|
constrainedOffset = input(...(ngDevMode ? [undefined, { debugName: "constrainedOffset" }] : []));
|
|
offsetStart = output();
|
|
collapse = output();
|
|
previewTransform = computed(() => {
|
|
const offset = coerceCssPixelValue(this.constrainedOffset());
|
|
return this.vertical() ? `translateY(${offset})` : `translateX(${offset})`;
|
|
}, ...(ngDevMode ? [{ debugName: "previewTransform" }] : []));
|
|
resizeStartEvent(event) {
|
|
if (this.resizable()) {
|
|
const { pageX, pageY } = getEventWithPoint(event);
|
|
this.offsetStart.emit([pageX, pageY]);
|
|
}
|
|
}
|
|
collapseEvent(type) {
|
|
this.collapse.emit(type);
|
|
}
|
|
getValidNumber(num) {
|
|
return typeof num === 'number' && !isNaN(num) ? Math.round(num) : 0;
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: NzSplitterBarComponent, isStandalone: true, selector: "[nz-splitter-bar]", inputs: { ariaNow: { classPropertyName: "ariaNow", publicName: "ariaNow", isSignal: true, isRequired: true, transformFunction: null }, ariaMin: { classPropertyName: "ariaMin", publicName: "ariaMin", isSignal: true, isRequired: true, transformFunction: null }, ariaMax: { classPropertyName: "ariaMax", publicName: "ariaMax", isSignal: true, isRequired: true, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, vertical: { classPropertyName: "vertical", publicName: "vertical", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, constrainedOffset: { classPropertyName: "constrainedOffset", publicName: "constrainedOffset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { offsetStart: "offsetStart", collapse: "collapse" }, host: { attributes: { "role": "separator" }, properties: { "attr.aria-valuenow": "getValidNumber(ariaNow())", "attr.aria-valuemin": "getValidNumber(ariaMin())", "attr.aria-valuemax": "getValidNumber(ariaMax())" }, classAttribute: "ant-splitter-bar" }, ngImport: i0, template: `
|
|
@if (lazy()) {
|
|
@let preview = active() && !!this.constrainedOffset();
|
|
<div
|
|
class="ant-splitter-bar-preview"
|
|
[class.ant-splitter-bar-preview-active]="preview"
|
|
[style.transform]="preview ? previewTransform() : null"
|
|
></div>
|
|
}
|
|
|
|
<div
|
|
class="ant-splitter-bar-dragger"
|
|
[class.ant-splitter-bar-dragger-disabled]="!resizable()"
|
|
[class.ant-splitter-bar-dragger-active]="active()"
|
|
(mousedown)="resizeStartEvent($event)"
|
|
(touchstart)="resizeStartEvent($event)"
|
|
></div>
|
|
|
|
@if (collapsible()?.start) {
|
|
<div class="ant-splitter-bar-collapse-bar ant-splitter-bar-collapse-bar-start" (click)="collapseEvent('start')">
|
|
<nz-icon
|
|
[nzType]="vertical() ? 'up' : 'left'"
|
|
class="ant-splitter-bar-collapse-icon ant-splitter-bar-collapse-start"
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
@if (collapsible()?.end) {
|
|
<div class="ant-splitter-bar-collapse-bar ant-splitter-bar-collapse-bar-end" (click)="collapseEvent('end')">
|
|
<nz-icon
|
|
[nzType]="vertical() ? 'down' : 'right'"
|
|
class="ant-splitter-bar-collapse-icon ant-splitter-bar-collapse-end"
|
|
/>
|
|
</div>
|
|
}
|
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i1.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterBarComponent, decorators: [{
|
|
type: Component,
|
|
args: [{
|
|
selector: '[nz-splitter-bar]',
|
|
imports: [NzIconModule],
|
|
template: `
|
|
@if (lazy()) {
|
|
@let preview = active() && !!this.constrainedOffset();
|
|
<div
|
|
class="ant-splitter-bar-preview"
|
|
[class.ant-splitter-bar-preview-active]="preview"
|
|
[style.transform]="preview ? previewTransform() : null"
|
|
></div>
|
|
}
|
|
|
|
<div
|
|
class="ant-splitter-bar-dragger"
|
|
[class.ant-splitter-bar-dragger-disabled]="!resizable()"
|
|
[class.ant-splitter-bar-dragger-active]="active()"
|
|
(mousedown)="resizeStartEvent($event)"
|
|
(touchstart)="resizeStartEvent($event)"
|
|
></div>
|
|
|
|
@if (collapsible()?.start) {
|
|
<div class="ant-splitter-bar-collapse-bar ant-splitter-bar-collapse-bar-start" (click)="collapseEvent('start')">
|
|
<nz-icon
|
|
[nzType]="vertical() ? 'up' : 'left'"
|
|
class="ant-splitter-bar-collapse-icon ant-splitter-bar-collapse-start"
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
@if (collapsible()?.end) {
|
|
<div class="ant-splitter-bar-collapse-bar ant-splitter-bar-collapse-bar-end" (click)="collapseEvent('end')">
|
|
<nz-icon
|
|
[nzType]="vertical() ? 'down' : 'right'"
|
|
class="ant-splitter-bar-collapse-icon ant-splitter-bar-collapse-end"
|
|
/>
|
|
</div>
|
|
}
|
|
`,
|
|
host: {
|
|
role: 'separator',
|
|
class: 'ant-splitter-bar',
|
|
'[attr.aria-valuenow]': 'getValidNumber(ariaNow())',
|
|
'[attr.aria-valuemin]': 'getValidNumber(ariaMin())',
|
|
'[attr.aria-valuemax]': 'getValidNumber(ariaMax())'
|
|
},
|
|
encapsulation: ViewEncapsulation.None,
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
}]
|
|
}] });
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
class NzSplitterPanelComponent {
|
|
nzDefaultSize = input(...(ngDevMode ? [undefined, { debugName: "nzDefaultSize" }] : []));
|
|
nzMin = input(...(ngDevMode ? [undefined, { debugName: "nzMin" }] : []));
|
|
nzMax = input(...(ngDevMode ? [undefined, { debugName: "nzMax" }] : []));
|
|
nzSize = input(...(ngDevMode ? [undefined, { debugName: "nzSize" }] : []));
|
|
nzCollapsible = input(false, ...(ngDevMode ? [{ debugName: "nzCollapsible" }] : []));
|
|
nzResizable = input(true, ...(ngDevMode ? [{ debugName: "nzResizable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
contentTemplate = viewChild.required('contentTemplate');
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.0", type: NzSplitterPanelComponent, isStandalone: true, selector: "nz-splitter-panel", inputs: { nzDefaultSize: { classPropertyName: "nzDefaultSize", publicName: "nzDefaultSize", isSignal: true, isRequired: false, transformFunction: null }, nzMin: { classPropertyName: "nzMin", publicName: "nzMin", isSignal: true, isRequired: false, transformFunction: null }, nzMax: { classPropertyName: "nzMax", publicName: "nzMax", isSignal: true, isRequired: false, transformFunction: null }, nzSize: { classPropertyName: "nzSize", publicName: "nzSize", isSignal: true, isRequired: false, transformFunction: null }, nzCollapsible: { classPropertyName: "nzCollapsible", publicName: "nzCollapsible", isSignal: true, isRequired: false, transformFunction: null }, nzResizable: { classPropertyName: "nzResizable", publicName: "nzResizable", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "contentTemplate", first: true, predicate: ["contentTemplate"], descendants: true, isSignal: true }], exportAs: ["nzSplitterPanel"], ngImport: i0, template: `
|
|
<ng-template #contentTemplate>
|
|
<ng-content></ng-content>
|
|
</ng-template>
|
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterPanelComponent, decorators: [{
|
|
type: Component,
|
|
args: [{
|
|
selector: 'nz-splitter-panel',
|
|
exportAs: 'nzSplitterPanel',
|
|
template: `
|
|
<ng-template #contentTemplate>
|
|
<ng-content></ng-content>
|
|
</ng-template>
|
|
`,
|
|
encapsulation: ViewEncapsulation.None,
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
}]
|
|
}] });
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
/**
|
|
* Get the percentage value of the string. (e.g. '50%' => 0.5)
|
|
* @param str
|
|
*/
|
|
function getPercentValue(str) {
|
|
return Number(str.slice(0, -1)) / 100;
|
|
}
|
|
/**
|
|
* Check if the size is percentage.
|
|
* @param size
|
|
*/
|
|
function isPercent(size) {
|
|
return typeof size === 'string' && size.endsWith('%');
|
|
}
|
|
/**
|
|
* Coerce the panel collapsible option to the NzSplitterCollapseOption type.
|
|
*/
|
|
function coerceCollapsible(collapsible) {
|
|
if (collapsible && typeof collapsible === 'object') {
|
|
return collapsible;
|
|
}
|
|
const mergedCollapsible = !!collapsible;
|
|
return {
|
|
start: mergedCollapsible,
|
|
end: mergedCollapsible
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
const passiveEventListenerOptions = normalizePassiveListenerOptions({ passive: true });
|
|
class NzSplitterComponent {
|
|
/** ------------------- Props ------------------- */
|
|
nzLayout = input('horizontal', ...(ngDevMode ? [{ debugName: "nzLayout" }] : []));
|
|
nzLazy = input(false, ...(ngDevMode ? [{ debugName: "nzLazy", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
nzResizeStart = output();
|
|
nzResize = output();
|
|
nzResizeEnd = output();
|
|
destroy$ = inject(NzDestroyService);
|
|
elementRef = inject(ElementRef);
|
|
directionality = inject(Directionality);
|
|
resizeObserver = inject(NzResizeObserver);
|
|
document = inject(DOCUMENT);
|
|
dir = toSignal(this.directionality.change, { initialValue: this.directionality.value });
|
|
/** ------------------- Panels ------------------- */
|
|
// Get all panels from content children
|
|
panels = contentChildren(NzSplitterPanelComponent, ...(ngDevMode ? [{ debugName: "panels" }] : []));
|
|
// Subscribe to the change of properties
|
|
panelProps = computed(() => this.panels().map(panel => ({
|
|
defaultSize: panel.nzDefaultSize(),
|
|
size: panel.nzSize(),
|
|
min: panel.nzMin(),
|
|
max: panel.nzMax(),
|
|
resizable: panel.nzResizable(),
|
|
collapsible: coerceCollapsible(panel.nzCollapsible()),
|
|
contentTemplate: panel.contentTemplate()
|
|
})), ...(ngDevMode ? [{ debugName: "panelProps" }] : []));
|
|
/** ------------------- Sizes ------------------- */
|
|
/**
|
|
* Observe the size of the container.
|
|
*/
|
|
containerBox = toSignal(this.resizeObserver.observe(this.elementRef).pipe(map(([item]) => item.target), map(el => ({ width: el.clientWidth, height: el.clientHeight }))), {
|
|
initialValue: {
|
|
width: this.elementRef.nativeElement.clientWidth || 0,
|
|
height: this.elementRef.nativeElement.clientHeight || 0
|
|
}
|
|
});
|
|
/**
|
|
* The size of the container, used to calculate the percentage size and flex basis of each panel.
|
|
*/
|
|
containerSize = computed(() => this.nzLayout() === 'horizontal' ? this.containerBox().width : this.containerBox().height, ...(ngDevMode ? [{ debugName: "containerSize" }] : []));
|
|
/**
|
|
* Derived from defaultSize of each panel.
|
|
* After that it will be updated by the resize event with **real** size in pixels.
|
|
*/
|
|
innerSizes = linkedSignal({
|
|
source: this.panelProps,
|
|
computation: source => source.map(panel => panel.defaultSize)
|
|
});
|
|
/**
|
|
* Calculate the size of each panel based on the container size and the percentage size.
|
|
*/
|
|
sizes = computed(() => {
|
|
let emptyCount = 0;
|
|
const containerSize = this.containerSize();
|
|
const innerSizes = this.innerSizes();
|
|
/**
|
|
* Get the calculated size, min and max percentage of each panel
|
|
*/
|
|
const sizes = this.panelProps().map((panel, index) => {
|
|
const size = panel.size ?? innerSizes[index];
|
|
// Calculate the percentage size of each panel.
|
|
let percentage;
|
|
if (isPercent(size)) {
|
|
percentage = getPercentValue(size);
|
|
}
|
|
else if (size || size === 0) {
|
|
const num = Number(size);
|
|
if (!isNaN(num)) {
|
|
percentage = num / containerSize;
|
|
}
|
|
}
|
|
else {
|
|
percentage = undefined;
|
|
emptyCount++;
|
|
}
|
|
// Calculate the min and max percentage size of each panel.
|
|
const minSize = panel.min;
|
|
const maxSize = panel.max;
|
|
const postPercentMinSize = isPercent(minSize) ? getPercentValue(minSize) : (minSize || 0) / containerSize;
|
|
const postPercentMaxSize = isPercent(maxSize)
|
|
? getPercentValue(maxSize)
|
|
: (maxSize || containerSize) / containerSize;
|
|
return {
|
|
size,
|
|
percentage,
|
|
min: minSize,
|
|
max: maxSize,
|
|
postPercentMinSize,
|
|
postPercentMaxSize
|
|
};
|
|
});
|
|
/**
|
|
* Normalize the percentage size of each panel if the total percentage is larger than 1 or smaller than 1.
|
|
*/
|
|
const totalPercentage = sizes.reduce((acc, { percentage }) => acc + (percentage ?? 0), 0);
|
|
for (const size of sizes) {
|
|
if (totalPercentage > 1 && !emptyCount) {
|
|
// If total percentage is larger than 1, we will scale it down.
|
|
const scale = 1 / totalPercentage;
|
|
size.percentage = size.percentage === undefined ? 0 : size.percentage * scale;
|
|
}
|
|
else {
|
|
// If total percentage is smaller than 1, we will fill the rest.
|
|
const averagePercentage = (1 - totalPercentage) / emptyCount;
|
|
size.percentage = size.percentage === undefined ? averagePercentage : size.percentage;
|
|
}
|
|
size.postPxSize = size.percentage * containerSize;
|
|
size.size = containerSize ? coerceCssPixelValue(size.postPxSize) : size.size;
|
|
}
|
|
return sizes;
|
|
}, ...(ngDevMode ? [{ debugName: "sizes" }] : []));
|
|
ariaInfos = computed(() => {
|
|
const stackSizes = [];
|
|
const ariaInfos = [];
|
|
const sizes = this.sizes();
|
|
let stack = 0;
|
|
for (const size of sizes) {
|
|
stack += size.percentage;
|
|
stackSizes.push(stack);
|
|
}
|
|
for (let i = 0; i < sizes.length - 1; i += 1) {
|
|
const ariaMinStart = (stackSizes[i - 1] || 0) + sizes[i].postPercentMinSize;
|
|
const ariaMinEnd = (stackSizes[i + 1] || 100) - sizes[i + 1].postPercentMaxSize;
|
|
const ariaMaxStart = (stackSizes[i - 1] || 0) + sizes[i].postPercentMaxSize;
|
|
const ariaMaxEnd = (stackSizes[i + 1] || 100) - sizes[i + 1].postPercentMinSize;
|
|
ariaInfos.push({
|
|
ariaNow: stackSizes[i] * 100,
|
|
ariaMin: Math.max(ariaMinStart, ariaMinEnd) * 100,
|
|
ariaMax: Math.min(ariaMaxStart, ariaMaxEnd) * 100
|
|
});
|
|
}
|
|
return ariaInfos;
|
|
}, ...(ngDevMode ? [{ debugName: "ariaInfos" }] : []));
|
|
getPxSizes() {
|
|
return this.sizes().map(s => s.postPxSize);
|
|
}
|
|
/** ------------------ Resize ------------------ */
|
|
/**
|
|
* The index of the panel that is being resized.
|
|
* @note Mark the moving splitter bar as activated to show the dragging effect even if the mouse is outside the
|
|
* splitter container.
|
|
*/
|
|
movingIndex = signal(null, ...(ngDevMode ? [{ debugName: "movingIndex" }] : []));
|
|
/**
|
|
* The offset of preview position (lazy mode) when dragging the splitter bar.
|
|
* Constrained by the min and max size of the target panel.
|
|
*/
|
|
constrainedOffset = signal(0, ...(ngDevMode ? [{ debugName: "constrainedOffset" }] : []));
|
|
/**
|
|
* The resizable information of each splitter bar.
|
|
*/
|
|
resizableInfos = computed(() => {
|
|
const items = this.panelProps();
|
|
const pxSizes = this.getPxSizes();
|
|
const resizeInfos = [];
|
|
for (let i = 0; i < items.length - 1; i += 1) {
|
|
const prevItem = items[i];
|
|
const nextItem = items[i + 1];
|
|
const prevSize = pxSizes[i];
|
|
const nextSize = pxSizes[i + 1];
|
|
const { resizable: prevResizable = true, min: prevMin, collapsible: prevCollapsible } = prevItem;
|
|
const { resizable: nextResizable = true, min: nextMin, collapsible: nextCollapsible } = nextItem;
|
|
const mergedResizable =
|
|
// Both need to be resizable
|
|
prevResizable &&
|
|
nextResizable &&
|
|
// Prev is not collapsed and limit min size
|
|
(prevSize !== 0 || !prevMin) &&
|
|
// Next is not collapsed and limit min size
|
|
(nextSize !== 0 || !nextMin);
|
|
const startCollapsible =
|
|
// Self is collapsible
|
|
(prevCollapsible.end && prevSize > 0) ||
|
|
// Collapsed and can be collapsed
|
|
(nextCollapsible.start && nextSize === 0 && prevSize > 0);
|
|
const endCollapsible =
|
|
// Self is collapsible
|
|
(nextCollapsible.start && nextSize > 0) ||
|
|
// Collapsed and can be collapsed
|
|
(prevCollapsible.end && prevSize === 0 && nextSize > 0);
|
|
resizeInfos[i] = {
|
|
resizable: mergedResizable,
|
|
collapsible: {
|
|
start: !!(this.dir() === 'rtl' ? endCollapsible : startCollapsible),
|
|
end: !!(this.dir() === 'rtl' ? startCollapsible : endCollapsible)
|
|
}
|
|
};
|
|
}
|
|
return resizeInfos;
|
|
}, ...(ngDevMode ? [{ debugName: "resizableInfos" }] : []));
|
|
/**
|
|
* Handle the resize start event for the specified panel.
|
|
* @param index The index of the panel.
|
|
* @param startPos The start position of the resize event.
|
|
*/
|
|
startResize(index, startPos) {
|
|
this.movingIndex.set({ index, confirmed: false });
|
|
this.nzResizeStart.emit(this.getPxSizes());
|
|
const end$ = new Subject();
|
|
// Updated constraint calculation
|
|
const getConstrainedOffset = (rawOffset) => {
|
|
const { percentage, postPercentMinSize, postPercentMaxSize } = this.sizes()[index];
|
|
const [ariaNow, ariaMin, ariaMax] = [percentage, postPercentMinSize, postPercentMaxSize].map(p => p * 100);
|
|
const containerSize = this.containerSize();
|
|
const currentPos = (containerSize * ariaNow) / 100;
|
|
const newPos = currentPos + rawOffset;
|
|
// Calculate available space
|
|
const minAllowed = Math.max(0, (containerSize * ariaMin) / 100);
|
|
const maxAllowed = Math.min(containerSize, (containerSize * ariaMax) / 100);
|
|
// Constrain new position within bounds
|
|
const clampedPos = Math.max(minAllowed, Math.min(maxAllowed, newPos));
|
|
return clampedPos - currentPos;
|
|
};
|
|
const handleLazyMove = (offset) => {
|
|
this.constrainedOffset.set(getConstrainedOffset(offset));
|
|
};
|
|
const handleLazyEnd = () => {
|
|
this.updateOffset(index, this.constrainedOffset());
|
|
this.constrainedOffset.set(0);
|
|
};
|
|
// resizing
|
|
merge(fromEventOutsideAngular(this.document, 'mousemove', passiveEventListenerOptions), fromEventOutsideAngular(this.document, 'touchmove', passiveEventListenerOptions))
|
|
.pipe(map(event => getEventWithPoint(event)), map(({ pageX, pageY }) => (this.nzLayout() === 'horizontal' ? pageX - startPos[0] : pageY - startPos[1])),
|
|
// flip the offset if the direction is rtl
|
|
map(offset => (this.nzLayout() === 'horizontal' && this.dir() === 'rtl' ? -offset : offset)), startWith(0), pairwise(), takeUntil(merge(end$, this.destroy$)))
|
|
.subscribe(([prev, next]) => {
|
|
if (this.nzLazy() && next !== 0) {
|
|
handleLazyMove(next);
|
|
}
|
|
else {
|
|
const deltaOffset = next - prev;
|
|
// filter out the 0 delta offset
|
|
if (deltaOffset !== 0) {
|
|
this.updateOffset(index, deltaOffset);
|
|
}
|
|
}
|
|
});
|
|
// resize end
|
|
merge(fromEventOutsideAngular(this.document, 'mouseup'), fromEventOutsideAngular(this.document, 'touchend'))
|
|
.pipe(takeUntil(merge(end$, this.destroy$)))
|
|
.subscribe(() => {
|
|
if (this.nzLazy()) {
|
|
handleLazyEnd();
|
|
}
|
|
this.movingIndex.set(null);
|
|
this.nzResizeEnd.emit(this.getPxSizes());
|
|
end$.next();
|
|
});
|
|
}
|
|
/**
|
|
* Update the sizes of specified panels based on the move offset.
|
|
* @param index The index of the panel.
|
|
* @param offset The move offset in pixels.
|
|
*/
|
|
updateOffset(index, offset) {
|
|
const containerSize = this.containerSize();
|
|
const limitSizes = this.sizes().map(p => [p.min, p.max]);
|
|
const pxSizes = this.sizes().map(p => p.percentage * containerSize);
|
|
const getLimitSize = (size, defaultLimit) => {
|
|
if (typeof size === 'string') {
|
|
return getPercentValue(size) * containerSize;
|
|
}
|
|
return size ?? defaultLimit;
|
|
};
|
|
// First time trigger move index update is not sync in the state
|
|
let confirmedIndex = null;
|
|
const movingIndex = this.movingIndex();
|
|
// we need to know what the real index is
|
|
if ((!movingIndex || !movingIndex.confirmed) && offset !== 0) {
|
|
// search for the real index
|
|
if (offset > 0) {
|
|
confirmedIndex = index;
|
|
this.movingIndex.set({ index, confirmed: true });
|
|
}
|
|
else {
|
|
for (let i = index; i >= 0; i -= 1) {
|
|
if (pxSizes[i] > 0 && this.resizableInfos()[i].resizable) {
|
|
confirmedIndex = i;
|
|
this.movingIndex.set({ index: i, confirmed: true });
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const mergedIndex = confirmedIndex ?? index;
|
|
const nextIndex = mergedIndex + 1;
|
|
// Get boundary
|
|
const startMinSize = getLimitSize(limitSizes[mergedIndex][0], 0);
|
|
const endMinSize = getLimitSize(limitSizes[nextIndex][0], 0);
|
|
const startMaxSize = getLimitSize(limitSizes[mergedIndex][1], containerSize);
|
|
const endMaxSize = getLimitSize(limitSizes[nextIndex][1], containerSize);
|
|
let mergedOffset = offset;
|
|
// Align with the boundary
|
|
if (pxSizes[mergedIndex] + mergedOffset < startMinSize) {
|
|
mergedOffset = startMinSize - pxSizes[mergedIndex];
|
|
}
|
|
if (pxSizes[nextIndex] - mergedOffset < endMinSize) {
|
|
mergedOffset = pxSizes[nextIndex] - endMinSize;
|
|
}
|
|
if (pxSizes[mergedIndex] + mergedOffset > startMaxSize) {
|
|
mergedOffset = startMaxSize - pxSizes[mergedIndex];
|
|
}
|
|
if (pxSizes[nextIndex] - mergedOffset > endMaxSize) {
|
|
mergedOffset = pxSizes[nextIndex] - endMaxSize;
|
|
}
|
|
// Do offset
|
|
pxSizes[mergedIndex] += mergedOffset;
|
|
pxSizes[nextIndex] -= mergedOffset;
|
|
this.innerSizes.set(pxSizes);
|
|
this.nzResize.emit(pxSizes);
|
|
}
|
|
/** ------------------ Resize ------------------ */
|
|
/**
|
|
* Record the original size of the collapsed panel.
|
|
* Used to restore the size when the panel is expanded back.
|
|
*/
|
|
cacheCollapsedSize = [];
|
|
/**
|
|
* Collapse the specified panel.
|
|
* @param index The index of the panel to collapse.
|
|
* @param type The type of collapse, either `start` or `end`.
|
|
*/
|
|
collapse(index, type) {
|
|
const containerSize = this.containerSize();
|
|
const limitSizes = this.sizes().map(p => [p.min, p.max]);
|
|
const currentSizes = this.sizes().map(p => p.percentage * containerSize);
|
|
const adjustedType = this.dir() === 'rtl' ? (type === 'start' ? 'end' : 'start') : type;
|
|
const currentIndex = adjustedType === 'start' ? index : index + 1;
|
|
const targetIndex = adjustedType == 'start' ? index + 1 : index;
|
|
const currentSize = currentSizes[currentIndex];
|
|
const targetSize = currentSizes[targetIndex];
|
|
const getLimitSize = (size, defaultLimit) => {
|
|
if (typeof size === 'string') {
|
|
return getPercentValue(size) * containerSize;
|
|
}
|
|
return size ?? defaultLimit;
|
|
};
|
|
if (currentSize !== 0 && targetSize !== 0) {
|
|
// Collapse directly
|
|
currentSizes[currentIndex] = 0;
|
|
currentSizes[targetIndex] += currentSize;
|
|
this.cacheCollapsedSize[index] = currentSize;
|
|
}
|
|
else {
|
|
const totalSize = currentSize + targetSize;
|
|
const currentSizeMin = getLimitSize(limitSizes[currentIndex][0], 0);
|
|
const currentSizeMax = getLimitSize(limitSizes[currentIndex][1], containerSize);
|
|
const targetSizeMin = getLimitSize(limitSizes[targetIndex][0], 0);
|
|
const targetSizeMax = getLimitSize(limitSizes[targetIndex][1], containerSize);
|
|
const limitStart = Math.max(currentSizeMin, totalSize - targetSizeMax);
|
|
const limitEnd = Math.min(currentSizeMax, totalSize - targetSizeMin);
|
|
const halfOffset = (limitEnd - limitStart) / 2;
|
|
const targetCacheCollapsedSize = this.cacheCollapsedSize[index];
|
|
const currentCacheCollapsedSize = totalSize - targetCacheCollapsedSize;
|
|
const shouldUseCache = targetCacheCollapsedSize &&
|
|
targetCacheCollapsedSize <= targetSizeMax &&
|
|
targetCacheCollapsedSize >= targetSizeMin &&
|
|
currentCacheCollapsedSize <= currentSizeMax &&
|
|
currentCacheCollapsedSize >= currentSizeMin;
|
|
if (shouldUseCache) {
|
|
currentSizes[targetIndex] = targetCacheCollapsedSize;
|
|
currentSizes[currentIndex] = currentCacheCollapsedSize;
|
|
}
|
|
else {
|
|
currentSizes[currentIndex] -= halfOffset;
|
|
currentSizes[targetIndex] += halfOffset;
|
|
}
|
|
}
|
|
this.innerSizes.set(currentSizes);
|
|
this.nzResize.emit(currentSizes);
|
|
this.nzResizeEnd.emit(currentSizes);
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: NzSplitterComponent, isStandalone: true, selector: "nz-splitter", inputs: { nzLayout: { classPropertyName: "nzLayout", publicName: "nzLayout", isSignal: true, isRequired: false, transformFunction: null }, nzLazy: { classPropertyName: "nzLazy", publicName: "nzLazy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nzResizeStart: "nzResizeStart", nzResize: "nzResize", nzResizeEnd: "nzResizeEnd" }, host: { properties: { "class.ant-splitter-horizontal": "nzLayout() === \"horizontal\"", "class.ant-splitter-vertical": "nzLayout() === \"vertical\"", "class.ant-splitter-rtl": "dir() === \"rtl\"" }, classAttribute: "ant-splitter" }, providers: [NzDestroyService], queries: [{ propertyName: "panels", predicate: NzSplitterPanelComponent, isSignal: true }], exportAs: ["nzSplitter"], ngImport: i0, template: `
|
|
@for (panel of panelProps(); let i = $index; track i; let last = $last) {
|
|
@let size = sizes()[i];
|
|
@let flexBasis = !!size.size ? size.size : 'auto';
|
|
@let flexGrow = !!size.size ? 0 : 1;
|
|
<div
|
|
class="ant-splitter-panel"
|
|
[class.ant-splitter-panel-hidden]="size.postPxSize === 0"
|
|
[style.flex-basis]="flexBasis"
|
|
[style.flex-grow]="flexGrow"
|
|
>
|
|
<ng-container *ngTemplateOutlet="panel.contentTemplate"></ng-container>
|
|
</div>
|
|
|
|
@if (!last) {
|
|
@let resizableInfo = resizableInfos()[i];
|
|
@let ariaInfo = ariaInfos()[i];
|
|
<div
|
|
nz-splitter-bar
|
|
[ariaNow]="ariaInfo.ariaNow"
|
|
[ariaMin]="ariaInfo.ariaMin"
|
|
[ariaMax]="ariaInfo.ariaMax"
|
|
[resizable]="resizableInfo.resizable"
|
|
[collapsible]="resizableInfo.collapsible"
|
|
[active]="movingIndex()?.index === i"
|
|
[vertical]="nzLayout() === 'vertical'"
|
|
[lazy]="nzLazy()"
|
|
[constrainedOffset]="constrainedOffset()"
|
|
(offsetStart)="startResize(i, $event)"
|
|
(collapse)="collapse(i, $event)"
|
|
></div>
|
|
}
|
|
}
|
|
|
|
<!-- Fake mask for cursor -->
|
|
@if (movingIndex() !== null) {
|
|
<div
|
|
aria-hidden
|
|
class="ant-splitter-mask"
|
|
[class.ant-splitter-mask-horizontal]="nzLayout() === 'horizontal'"
|
|
[class.ant-splitter-mask-vertical]="nzLayout() === 'vertical'"
|
|
></div>
|
|
}
|
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NzSplitterBarComponent, selector: "[nz-splitter-bar]", inputs: ["ariaNow", "ariaMin", "ariaMax", "active", "resizable", "vertical", "lazy", "collapsible", "constrainedOffset"], outputs: ["offsetStart", "collapse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterComponent, decorators: [{
|
|
type: Component,
|
|
args: [{
|
|
selector: 'nz-splitter',
|
|
exportAs: 'nzSplitter',
|
|
template: `
|
|
@for (panel of panelProps(); let i = $index; track i; let last = $last) {
|
|
@let size = sizes()[i];
|
|
@let flexBasis = !!size.size ? size.size : 'auto';
|
|
@let flexGrow = !!size.size ? 0 : 1;
|
|
<div
|
|
class="ant-splitter-panel"
|
|
[class.ant-splitter-panel-hidden]="size.postPxSize === 0"
|
|
[style.flex-basis]="flexBasis"
|
|
[style.flex-grow]="flexGrow"
|
|
>
|
|
<ng-container *ngTemplateOutlet="panel.contentTemplate"></ng-container>
|
|
</div>
|
|
|
|
@if (!last) {
|
|
@let resizableInfo = resizableInfos()[i];
|
|
@let ariaInfo = ariaInfos()[i];
|
|
<div
|
|
nz-splitter-bar
|
|
[ariaNow]="ariaInfo.ariaNow"
|
|
[ariaMin]="ariaInfo.ariaMin"
|
|
[ariaMax]="ariaInfo.ariaMax"
|
|
[resizable]="resizableInfo.resizable"
|
|
[collapsible]="resizableInfo.collapsible"
|
|
[active]="movingIndex()?.index === i"
|
|
[vertical]="nzLayout() === 'vertical'"
|
|
[lazy]="nzLazy()"
|
|
[constrainedOffset]="constrainedOffset()"
|
|
(offsetStart)="startResize(i, $event)"
|
|
(collapse)="collapse(i, $event)"
|
|
></div>
|
|
}
|
|
}
|
|
|
|
<!-- Fake mask for cursor -->
|
|
@if (movingIndex() !== null) {
|
|
<div
|
|
aria-hidden
|
|
class="ant-splitter-mask"
|
|
[class.ant-splitter-mask-horizontal]="nzLayout() === 'horizontal'"
|
|
[class.ant-splitter-mask-vertical]="nzLayout() === 'vertical'"
|
|
></div>
|
|
}
|
|
`,
|
|
imports: [NgTemplateOutlet, NzSplitterBarComponent],
|
|
providers: [NzDestroyService],
|
|
host: {
|
|
class: 'ant-splitter',
|
|
'[class.ant-splitter-horizontal]': 'nzLayout() === "horizontal"',
|
|
'[class.ant-splitter-vertical]': 'nzLayout() === "vertical"',
|
|
'[class.ant-splitter-rtl]': 'dir() === "rtl"'
|
|
},
|
|
encapsulation: ViewEncapsulation.None,
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
}]
|
|
}] });
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
class NzSplitterModule {
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterModule, imports: [NzSplitterComponent, NzSplitterPanelComponent], exports: [NzSplitterComponent, NzSplitterPanelComponent] });
|
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterModule, imports: [NzSplitterComponent] });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NzSplitterModule, decorators: [{
|
|
type: NgModule,
|
|
args: [{
|
|
imports: [NzSplitterComponent, NzSplitterPanelComponent],
|
|
exports: [NzSplitterComponent, NzSplitterPanelComponent]
|
|
}]
|
|
}] });
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
|
|
/**
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
|
|
*/
|
|
|
|
/**
|
|
* Generated bundle index. Do not edit.
|
|
*/
|
|
|
|
export { NzSplitterComponent, NzSplitterModule, NzSplitterPanelComponent };
|
|
//# sourceMappingURL=ng-zorro-antd-splitter.mjs.map
|