UNPKG

e-virt-table

Version:

A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.

137 lines 5.03 kB
const PX_VALUE_RE = /^-?\d*\.?\d+px$/; /** 内容缩放:逻辑坐标 ↔ 物理像素,canvas / DOM 共用 */ export default class ZoomScale { constructor() { Object.defineProperty(this, "value", { enumerable: true, configurable: true, writable: true, value: 1 }); } get isDefault() { return this.value === 1; } clamp(value, min, max) { return Math.min(max, Math.max(min, value)); } /** @returns 是否发生变化 */ set(value, min, max) { const next = this.clamp(value, min, max); if (Math.abs(next - this.value) < 0.0001) { return false; } this.value = next; return true; } /** 逻辑 -> 物理 */ toVisual(logical) { return logical * this.value; } /** 物理 -> 逻辑 */ toLogical(physical) { return physical / this.value; } toPx(logical) { return `${this.toVisual(logical)}px`; } /** DOM 测量高度 -> 逻辑高度 */ domHeightToLogical(physicalHeight) { return Math.round(physicalHeight / this.value); } /** 逻辑内容尺寸 -> 物理尺寸 */ contentToPhysical(logical) { return logical * this.value; } scaleStyle(style) { if (this.isDefault) { return style; } const result = { ...style }; for (const key of Object.keys(result)) { const val = result[key]; if (typeof val === 'string' && PX_VALUE_RE.test(val)) { result[key] = `${parseFloat(val) * this.value}px`; } } return result; } assignScaledStyle(el, style) { Object.assign(el.style, this.scaleStyle(style)); } /** 容器物理宽度 -> 逻辑可视宽度 */ containerWidth(containerPhysicalWidth) { return Math.floor(containerPhysicalWidth / this.value); } resolveStageWidth(options) { const containerLogical = this.containerWidth(options.containerPhysicalWidth); const stageWidth = options.fillContainer ? containerLogical : Math.min(Math.floor(options.contentWidth + options.scrollerTrackSize), containerLogical); const stagePhysicalWidth = Math.min(Math.round(this.toVisual(stageWidth)), Math.floor(options.containerPhysicalWidth)); return { stageWidth, stagePhysicalWidth }; } applyStageHeight(stageElement, physicalHeight) { const stagePhysicalHeight = Math.floor(physicalHeight); const stageHeight = Math.floor(stagePhysicalHeight / this.value); stageElement.style.height = `${stagePhysicalHeight}px`; return { stageHeight, stagePhysicalHeight }; } /** floating-ui 锚点:逻辑单元格 -> 视口物理矩形 */ getViewportRect(logical, containerRect) { const x = this.toVisual(logical.x) + containerRect.x; const y = this.toVisual(logical.y) + containerRect.y; const width = this.toVisual(logical.width); const height = this.toVisual(logical.height); return { x, y, left: x, top: y, right: x + width, bottom: y + height, width, height, }; } /** overlayer 单元格:外层物理定位 + 内层 zoom 缩放自定义 render */ createOverlayerCellElement(cell, cssPrefix) { const cellEl = document.createElement('div'); this.assignScaledStyle(cellEl, cell.style); Object.keys(cell.domDataset || {}).forEach((key) => { cellEl.setAttribute(key, cell.domDataset[key]); }); if (typeof cell.render !== 'function') { return cellEl; } if (this.isDefault) { cell.render(cellEl, cell); return cellEl; } const logicalWidth = 'visibleWidth' in cell ? cell.visibleWidth : cell.width; const logicalHeight = 'visibleHeight' in cell ? cell.visibleHeight : cell.height; const isAutoHeight = 'autoRowHeight' in cell && cell.autoRowHeight && 'renderType' in cell && cell.renderType === 'default'; const contentEl = document.createElement('div'); contentEl.className = `${cssPrefix}-overlayer-cell-content`; contentEl.style.width = `${logicalWidth}px`; contentEl.style.boxSizing = 'border-box'; if (isAutoHeight) { contentEl.style.height = 'auto'; contentEl.style.minHeight = `${logicalHeight}px`; cellEl.style.height = 'auto'; } else { contentEl.style.height = `${logicalHeight}px`; cellEl.style.height = this.toPx(logicalHeight); } contentEl.style.zoom = String(this.value); cellEl.style.width = this.toPx(logicalWidth); cellEl.appendChild(contentEl); cell.render(contentEl, cell); return cellEl; } } //# sourceMappingURL=ZoomScale.js.map