UNPKG

ng-cw-v12

Version:

Angular UI Component Library

395 lines (389 loc) 21.8 kB
import * as i0 from '@angular/core'; import { Component, Input, ViewChild, EventEmitter, ChangeDetectionStrategy, ContentChildren, Output, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; class SplitterPanelComponent { } SplitterPanelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: SplitterPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); SplitterPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: SplitterPanelComponent, selector: "nc-splitter-panel", inputs: { ncPanelSize: "ncPanelSize", ncMinSize: "ncMinSize" }, viewQueries: [{ propertyName: "contentTemplate", first: true, predicate: ["content"], descendants: true }], ngImport: i0, template: ` <ng-template #content> <ng-content></ng-content> </ng-template> `, isInline: true, styles: ["\n :host {\n display: flex;\n overflow: auto;\n width: 100%;\n height: 100%;\n }\n "] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: SplitterPanelComponent, decorators: [{ type: Component, args: [{ selector: 'nc-splitter-panel', template: ` <ng-template #content> <ng-content></ng-content> </ng-template> `, styles: [` :host { display: flex; overflow: auto; width: 100%; height: 100%; } `] }] }], propDecorators: { ncPanelSize: [{ type: Input }], ncMinSize: [{ type: Input }], contentTemplate: [{ type: ViewChild, args: ['content'] }] } }); class SplitterComponent { constructor(cdr, el) { this.cdr = cdr; this.el = el; /** 布局方向 */ this.ncLayout = 'horizontal'; /** 分割线大小 */ this.ncGutterSize = 2; /** 分割线热区大小(可点击区域),默认为分割线大小的3倍 */ this.ncGutterHitSize = this.ncGutterSize * 3; /** 分割线颜色 */ this.ncGutterColor = 'rgba(0, 0, 0, 0.04)'; /** 分割线悬停颜色 */ this.ncGutterHoverColor = 'rgb(230, 244, 255)'; /** 分割线移动颜色 */ this.ncGutterMoveColor = 'rgb(186, 224, 255)'; /** 分割线图标颜色 */ this.ncGutterIconColor = 'rgba(0, 0, 0, 0.15)'; /** 延迟渲染模式 */ this._lazy = false; /** 分割线移动事件 */ this.ncResize = new EventEmitter(); /** 分割线开始移动事件 */ this.ncResizeStart = new EventEmitter(); /** 分割线结束移动事件 */ this.ncResizeEnd = new EventEmitter(); this.panels = []; this.isResizing = false; this.resizingIndex = -1; this.startPosition = 0; this.startSizes = []; this.curGutterDom = null; // 懒加载模式相关属性 this.lazySizes = []; // 懒加载模式下计算的临时尺寸 this.lazyOffset = 0; // 分割线的偏移量 this.previewLine = null; // 预览线元素 this.onMouseMove = (event) => { this.handleMove(event, 'mouse'); }; this.onTouchMove = (event) => { // 防止页面滚动 event.preventDefault(); this.handleMove(event, 'touch'); }; this.onMouseUp = (event) => { this.endResize(event, 'mouse'); }; this.onTouchEnd = (event) => { this.endResize(event, 'touch'); }; } set ncLazy(val) { this._lazy = val !== null && val !== undefined && val !== false && val !== 'false'; } get ncLazy() { return this._lazy; } ngAfterViewInit() { this.panels = this.panelComponents.toArray(); this.normalizeSizes(); this.cdr.detectChanges(); } normalizeSizes() { // 计算已设置尺寸的面板总和 const configuredPanels = this.panels.filter(panel => panel.ncPanelSize); const configuredSize = configuredPanels.reduce((sum, panel) => sum + panel.ncPanelSize, 0); // 计算未设置尺寸的面板数量 const unconfiguredCount = this.panels.length - configuredPanels.length; // 为未设置尺寸的面板平均分配剩余空间 const remainingSize = 100 - configuredSize; const defaultSize = unconfiguredCount > 0 ? remainingSize / unconfiguredCount : 0; // 设置每个面板的最终尺寸 this.panels.forEach(panel => { if (!panel.ncPanelSize) { panel.ncPanelSize = defaultSize; } }); // 若面板的最小尺寸大于面板的尺寸,则将面板的尺寸设置为最小尺寸,即无法再缩小 this.panels.forEach(panel => { if (panel.ncMinSize && panel.ncMinSize > panel.ncPanelSize) { panel.ncMinSize = panel.ncPanelSize; } }); } onMouseDown(event, index) { this.startResize(event, index, 'mouse'); } onTouchStart(event, index) { // 防止默认的滚动行为 event.preventDefault(); this.startResize(event, index, 'touch'); } startResize(event, index, type) { event.preventDefault(); // 防止默认行为 this.curGutterDom = event.target; this.curGutterDom.style.backgroundColor = this.ncGutterMoveColor; this.isResizing = true; this.resizingIndex = index; // 获取初始位置(兼容鼠标和触摸事件) const clientX = type === 'mouse' ? event.clientX : event.touches[0].clientX; const clientY = type === 'mouse' ? event.clientY : event.touches[0].clientY; this.startPosition = this.ncLayout === 'horizontal' ? clientX : clientY; this.startSizes = this.panels.map(panel => panel.ncPanelSize || 0); // 禁用文本选择 document.body.style.userSelect = 'none'; document.body.style.webkitUserSelect = 'none'; // 设置鼠标指针样式(仅对鼠标事件有效) if (type === 'mouse') { document.body.style.cursor = this.ncLayout === 'horizontal' ? 'col-resize' : 'row-resize'; } // 对于触摸事件,防止页面滚动和缩放 if (type === 'touch') { // 禁用触摸滚动 document.body.style.overflow = 'hidden'; document.body.style.touchAction = 'none'; } // 添加对应的事件监听器 if (type === 'mouse') { document.addEventListener('mousemove', this.onMouseMove); document.addEventListener('mouseup', this.onMouseUp); } else { document.addEventListener('touchmove', this.onTouchMove, { passive: false }); document.addEventListener('touchend', this.onTouchEnd); document.addEventListener('touchcancel', this.onTouchEnd); } this.ncResizeStart.emit({ gutterIndex: index, sizes: this.panels.map(panel => panel.ncPanelSize || 0), mouseEvent: event, containerSize: [this.el.nativeElement.offsetWidth, this.el.nativeElement.offsetHeight] }); } handleMove(event, type) { if (!this.isResizing) return; // 获取当前位置(兼容鼠标和触摸事件) const clientX = type === 'mouse' ? event.clientX : event.touches[0].clientX; const clientY = type === 'mouse' ? event.clientY : event.touches[0].clientY; const currentPosition = this.ncLayout === 'horizontal' ? clientX : clientY; let delta = ((currentPosition - this.startPosition) / this.getContainerSize()) * 100; const newSizes = [...this.startSizes]; const leftPanelMinSize = this.panels[this.resizingIndex].ncMinSize || 0; const rightPanelMinSize = this.panels[this.resizingIndex + 1].ncMinSize || 0; // 计算实际可移动的范围,限制delta //左面板最多能扩展多少(受右面板最小尺寸限制) const maxLeftExpansion = this.startSizes[this.resizingIndex + 1] - rightPanelMinSize; //右面板最多能扩展多少(受左面板最小尺寸限制) const maxRightExpansion = this.startSizes[this.resizingIndex] - leftPanelMinSize; // 限制delta在有效范围内 if (delta > maxLeftExpansion) { //如果delta > maxLeftExpansion,说明左面板想要扩展得太多,会导致右面板小于最小尺寸,所以限制delta = maxLeftExpansion delta = maxLeftExpansion; } else if (delta < -maxRightExpansion) { //如果delta < -maxRightExpansion,说明右面板想要扩展得太多,会导致左面板小于最小尺寸,所以限制delta = -maxRightExpansion delta = -maxRightExpansion; } newSizes[this.resizingIndex] = this.startSizes[this.resizingIndex] + delta; newSizes[this.resizingIndex + 1] = this.startSizes[this.resizingIndex + 1] - delta; if (this.ncLazy) { // 懒加载模式:显示预览线,不改变面板尺寸 this.lazySizes = [...newSizes]; this.lazyOffset = (delta / 100) * this.getContainerSize(); // 创建或更新预览线 if (!this.previewLine) { this.previewLine = this.createPreviewLine(); this.el.nativeElement.appendChild(this.previewLine); } // 计算预览线的位置 const gutterRect = this.curGutterDom.getBoundingClientRect(); const containerRect = this.el.nativeElement.getBoundingClientRect(); if (this.ncLayout === 'horizontal') { const originalLeft = gutterRect.left - containerRect.left; this.previewLine.style.left = `${originalLeft + this.lazyOffset}px`; } else { const originalTop = gutterRect.top - containerRect.top; this.previewLine.style.top = `${originalTop + this.lazyOffset}px`; } } else { // 正常模式:实时更新面板尺寸 const totalSize = newSizes.reduce((sum, size) => sum + size, 0); this.panels.forEach((panel, i) => panel.ncPanelSize = (newSizes[i] / totalSize) * 100); this.cdr.detectChanges(); } this.ncResize.emit({ gutterIndex: this.resizingIndex, sizes: this.ncLazy ? this.lazySizes : this.panels.map(panel => panel.ncPanelSize || 0), delta: delta, mouseEvent: event, containerSize: [this.el.nativeElement.offsetWidth, this.el.nativeElement.offsetHeight] }); } endResize(event, type) { if (this.curGutterDom) { this.curGutterDom.style.backgroundColor = ''; // 清空内联样式,让CSS样式生效 if (this.ncLazy) { // 懒加载模式:移除预览线,应用最终面板尺寸 this.removePreviewLine(); if (this.lazySizes.length > 0) { const totalSize = this.lazySizes.reduce((sum, size) => sum + size, 0); this.panels.forEach((panel, i) => panel.ncPanelSize = (this.lazySizes[i] / totalSize) * 100); this.cdr.detectChanges(); } // 清空懒加载状态 this.lazySizes = []; this.lazyOffset = 0; } } // 恢复文本选择和鼠标指针 document.body.style.userSelect = ''; document.body.style.webkitUserSelect = ''; document.body.style.cursor = ''; // 对于触摸事件,恢复页面滚动和缩放 if (type === 'touch') { document.body.style.overflow = ''; document.body.style.touchAction = ''; } this.isResizing = false; // 移除对应的事件监听器 if (type === 'mouse') { document.removeEventListener('mousemove', this.onMouseMove); document.removeEventListener('mouseup', this.onMouseUp); } else { document.removeEventListener('touchmove', this.onTouchMove); document.removeEventListener('touchend', this.onTouchEnd); document.removeEventListener('touchcancel', this.onTouchEnd); } this.ncResizeEnd.emit({ gutterIndex: this.resizingIndex, sizes: this.panels.map(panel => panel.ncPanelSize || 0), mouseEvent: event, containerSize: [this.el.nativeElement.offsetWidth, this.el.nativeElement.offsetHeight] }); } getContainerSize() { return this.ncLayout === 'horizontal' ? this.el.nativeElement.offsetWidth : this.el.nativeElement.offsetHeight; } // 创建预览线 createPreviewLine() { const previewLine = document.createElement('div'); previewLine.className = `nc-splitter-preview-line nc-splitter-preview-line-${this.ncLayout}`; previewLine.style.position = 'absolute'; previewLine.style.backgroundColor = this.ncGutterHoverColor; previewLine.style.pointerEvents = 'none'; previewLine.style.zIndex = '1000'; if (this.ncLayout === 'horizontal') { previewLine.style.width = `${this.ncGutterSize}px`; previewLine.style.height = '100%'; previewLine.style.top = '0'; } else { previewLine.style.height = `${this.ncGutterSize}px`; previewLine.style.width = '100%'; previewLine.style.left = '0'; } return previewLine; } // 移除预览线 removePreviewLine() { if (this.previewLine && this.previewLine.parentNode) { this.previewLine.parentNode.removeChild(this.previewLine); this.previewLine = null; } } ngOnDestroy() { // 清理预览线 this.removePreviewLine(); // 清理所有事件监听器 document.removeEventListener('mousemove', this.onMouseMove); document.removeEventListener('mouseup', this.onMouseUp); document.removeEventListener('touchmove', this.onTouchMove); document.removeEventListener('touchend', this.onTouchEnd); document.removeEventListener('touchcancel', this.onTouchEnd); // 恢复所有样式 document.body.style.userSelect = ''; document.body.style.webkitUserSelect = ''; document.body.style.cursor = ''; document.body.style.overflow = ''; document.body.style.touchAction = ''; } } SplitterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: SplitterComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); SplitterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: SplitterComponent, selector: "nc-splitter", inputs: { ncLayout: "ncLayout", ncGutterSize: "ncGutterSize", ncGutterHitSize: "ncGutterHitSize", ncGutterColor: "ncGutterColor", ncGutterHoverColor: "ncGutterHoverColor", ncGutterMoveColor: "ncGutterMoveColor", ncGutterIconColor: "ncGutterIconColor", ncLazy: "ncLazy" }, outputs: { ncResize: "ncResize", ncResizeStart: "ncResizeStart", ncResizeEnd: "ncResizeEnd" }, queries: [{ propertyName: "panelComponents", predicate: SplitterPanelComponent }], ngImport: i0, template: "<div class=\"nc-splitter\" [ngClass]=\"'nc-splitter-' + ncLayout\" style=\"--ncGutterSize: {{ncGutterSize + 'px'}};\r\n --ncGutterColor: {{ncGutterColor}};\r\n --ncGutterHoverColor: {{ncGutterHoverColor}};\r\n --ncGutterIconColor: {{ncGutterIconColor}};\r\n --ncGutterHitSize: {{ncGutterHitSize + 'px'}};\">\r\n <ng-container *ngFor=\"let panel of panels; let i = index; let last = last\">\r\n <div class=\"panel\" [style.flexBasis.%]=\"panel.ncPanelSize\">\r\n <ng-container *ngTemplateOutlet=\"panel.contentTemplate\"></ng-container>\r\n </div>\r\n <div *ngIf=\"!last\" class=\"nc-splitter-gutter\" [ngClass]=\"'nc-splitter-gutter-' + ncLayout\"\r\n (mousedown)=\"onMouseDown($event, i)\" (touchstart)=\"onTouchStart($event, i)\"></div>\r\n </ng-container>\r\n</div>", styles: [":host{width:100%;height:100%}.nc-splitter{display:flex;width:100%;height:100%;overflow:hidden}.nc-splitter .panel{flex:1 1 auto;overflow:auto;display:flex}.nc-splitter .nc-splitter-gutter{background:var(--ncGutterColor);flex:0 0 var(--ncGutterSize);position:relative;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.nc-splitter .nc-splitter-gutter:hover{background:var(--ncGutterHoverColor)}.nc-splitter .nc-splitter-gutter:before{content:\"\";position:absolute;z-index:1}.nc-splitter .nc-splitter-gutter:after{position:absolute;content:\"\";background:var(--ncGutterIconColor);z-index:2;top:50%;left:50%;transform:translate(-50%,-50%)}.nc-splitter .nc-splitter-gutter-horizontal{cursor:col-resize}.nc-splitter .nc-splitter-gutter-horizontal:before{top:0;left:50%;width:var(--ncGutterHitSize);height:100%;transform:translate(-50%)}.nc-splitter .nc-splitter-gutter-horizontal:after{width:var(--ncGutterSize);height:20px}.nc-splitter .nc-splitter-gutter-vertical{cursor:row-resize}.nc-splitter .nc-splitter-gutter-vertical:before{top:50%;left:0;width:100%;height:var(--ncGutterHitSize);transform:translateY(-50%)}.nc-splitter .nc-splitter-gutter-vertical:after{width:20px;height:var(--ncGutterSize)}.nc-splitter-horizontal{flex-direction:row}.nc-splitter-vertical{flex-direction:column}.nc-splitter-preview-line{position:absolute;pointer-events:none;z-index:1000;opacity:.8;transition:opacity .1s ease}.nc-splitter-preview-line.nc-splitter-preview-line-horizontal{cursor:col-resize}.nc-splitter-preview-line.nc-splitter-preview-line-vertical{cursor:row-resize}\n"], directives: [{ type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: SplitterComponent, decorators: [{ type: Component, args: [{ selector: 'nc-splitter', templateUrl: './splitter.component.html', styleUrls: ['./splitter.component.less'], changeDetection: ChangeDetectionStrategy.OnPush }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { ncLayout: [{ type: Input }], ncGutterSize: [{ type: Input }], ncGutterHitSize: [{ type: Input }], ncGutterColor: [{ type: Input }], ncGutterHoverColor: [{ type: Input }], ncGutterMoveColor: [{ type: Input }], ncGutterIconColor: [{ type: Input }], ncLazy: [{ type: Input }], panelComponents: [{ type: ContentChildren, args: [SplitterPanelComponent] }], ncResize: [{ type: Output }], ncResizeStart: [{ type: Output }], ncResizeEnd: [{ type: Output }] } }); class NcSplitterModule { } NcSplitterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcSplitterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcSplitterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcSplitterModule, declarations: [SplitterComponent, SplitterPanelComponent], imports: [CommonModule], exports: [SplitterComponent, SplitterPanelComponent] }); NcSplitterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcSplitterModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcSplitterModule, decorators: [{ type: NgModule, args: [{ declarations: [ SplitterComponent, SplitterPanelComponent ], imports: [ CommonModule ], exports: [ SplitterComponent, SplitterPanelComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { NcSplitterModule, SplitterComponent, SplitterPanelComponent }; //# sourceMappingURL=ng-cw-v12-splitter.js.map