UNPKG

@visactor/vchart

Version:

charts lib based @visactor/VGrammar

333 lines (328 loc) 17.5 kB
import { isEqual } from "@visactor/vutils"; import { error } from "../util/debug"; import { getItemLayoutWithTag, layoutBottomInlineItems, layoutLeftInlineItems, layoutRightInlineItems, layoutTopInlineItems } from "./util"; export class Layout { constructor(_spec, ctx) { this.leftCurrent = 0, this.topCurrent = 0, this.rightCurrent = 0, this.bottomCurrent = 0, this.recomputeWidth = !1, this.recomputeHeight = !1, this._onError = null == ctx ? void 0 : ctx.onError; } _layoutInit(_chart, items, chartLayoutRect, chartViewBox) { this._chartLayoutRect = chartLayoutRect, this._chartViewBox = chartViewBox, this.leftCurrent = chartLayoutRect.x, this.topCurrent = chartLayoutRect.y, this.rightCurrent = chartLayoutRect.x + chartLayoutRect.width, this.bottomCurrent = chartLayoutRect.height + chartLayoutRect.y, this.recomputeWidth = !1, this.recomputeHeight = !1, items.sort(((a, b) => b.layoutLevel - a.layoutLevel)); } _layoutNormalItems(items, recompute) { this.layoutNormalInlineItems(items.filter((x => "normal-inline" === x.layoutType)), recompute), this.layoutNormalItems(items.filter((x => "normal" === x.layoutType)), recompute); } _groupItems(items) { const regionItems = items.filter((x => "region" === x.layoutType)), relativeItems = items.filter((x => "region-relative" === x.layoutType)), relativeOverlapItems = items.filter((x => "region-relative-overlap" === x.layoutType)), allRelatives = relativeItems.concat(relativeOverlapItems), overlapItems = { left: { items: [], rect: { width: 0, height: 0 } }, right: { items: [], rect: { width: 0, height: 0 } }, top: { items: [], rect: { width: 0, height: 0 } }, bottom: { items: [], rect: { width: 0, height: 0 } }, z: { items: [], rect: { width: 0, height: 0 } } }; return relativeOverlapItems.forEach((i => { overlapItems[i.layoutOrient].items.push(i); })), { regionItems: regionItems, relativeItems: relativeItems, relativeOverlapItems: relativeOverlapItems, allRelatives: allRelatives, overlapItems: overlapItems }; } layoutItems(_chart, items, chartLayoutRect, chartViewBox) { var _a, _b, _c; this._layoutInit(_chart, items, chartLayoutRect, chartViewBox); const recompute = { recomputeWidth: this.recomputeWidth, recomputeHeight: this.recomputeHeight }, secondLayoutLeftRight = null !== (_c = null === (_b = null === (_a = null == _chart ? void 0 : _chart.getSpec()) || void 0 === _a ? void 0 : _a.layout) || void 0 === _b ? void 0 : _b.secondLayoutLeftRight) && void 0 !== _c && _c; this._layoutNormalItems(items, recompute); const layoutTemp = { left: this.leftCurrent, top: this.topCurrent, right: this.rightCurrent, bottom: this.bottomCurrent }, {regionItems: regionItems, relativeItems: relativeItems, relativeOverlapItems: relativeOverlapItems, allRelatives: allRelatives, overlapItems: overlapItems} = this._groupItems(items); this.layoutRegionItems(regionItems, relativeItems, relativeOverlapItems, overlapItems, recompute, secondLayoutLeftRight), this._processAutoIndent(regionItems, relativeItems, relativeOverlapItems, overlapItems, allRelatives, layoutTemp, recompute, secondLayoutLeftRight), this.layoutAbsoluteItems(items.filter((x => "absolute" === x.layoutType))), items.forEach((item => { item.clearWillLayoutTag(); })); } _processAutoIndent(regionItems, relativeItems, relativeOverlapItems, overlapItems = { left: { items: [], rect: { width: 0, height: 0 } }, right: { items: [], rect: { width: 0, height: 0 } }, top: { items: [], rect: { width: 0, height: 0 } }, bottom: { items: [], rect: { width: 0, height: 0 } }, z: { items: [], rect: { width: 0, height: 0 } } }, allRelatives, layoutTemp, recompute, secondLayoutLeftRight = !1) { if (allRelatives.some((i => i.autoIndent))) { const {top: top, bottom: bottom, left: left, right: right} = this._checkAutoIndent(allRelatives, layoutTemp); (top || bottom || left || right) && (this.topCurrent = layoutTemp.top + top, this.bottomCurrent = layoutTemp.bottom - bottom, this.leftCurrent = layoutTemp.left + left, this.rightCurrent = layoutTemp.right - right, this.layoutRegionItems(regionItems, relativeItems, relativeOverlapItems, overlapItems, recompute, secondLayoutLeftRight)); } } layoutNormalItems(normalItems, recompute) { normalItems.forEach((item => { const {rect: rect} = getItemLayoutWithTag(item, this, recompute); "left" === item.layoutOrient ? (item.setLayoutStartPosition({ x: this.leftCurrent + item.layoutOffsetX + item.layoutPaddingLeft, y: this.topCurrent + item.layoutOffsetY + item.layoutPaddingTop }), this.leftCurrent += rect.width + item.layoutPaddingLeft + item.layoutPaddingRight) : "top" === item.layoutOrient ? (item.setLayoutStartPosition({ x: this.leftCurrent + item.layoutOffsetX + item.layoutPaddingLeft, y: this.topCurrent + item.layoutOffsetY + item.layoutPaddingTop }), this.topCurrent += rect.height + item.layoutPaddingTop + item.layoutPaddingBottom) : "right" === item.layoutOrient ? (item.setLayoutStartPosition({ x: this.rightCurrent + item.layoutOffsetX - rect.width - item.layoutPaddingRight, y: this.topCurrent + item.layoutOffsetY + item.layoutPaddingTop }), this.rightCurrent -= rect.width + item.layoutPaddingLeft + item.layoutPaddingRight) : "bottom" === item.layoutOrient && (item.setLayoutStartPosition({ x: this.leftCurrent + item.layoutOffsetX + item.layoutPaddingRight, y: this.bottomCurrent + item.layoutOffsetY - rect.height - item.layoutPaddingBottom }), this.bottomCurrent -= rect.height + item.layoutPaddingTop + item.layoutPaddingBottom); })); } layoutNormalInlineItems(normalItems, recompute) { const leftItems = normalItems.filter((item => "left" === item.layoutOrient)), rightItems = normalItems.filter((item => "right" === item.layoutOrient)), topItems = normalItems.filter((item => "top" === item.layoutOrient)), bottomItems = normalItems.filter((item => "bottom" === item.layoutOrient)), limitWidth = this._chartLayoutRect.width + this._chartLayoutRect.x, limitHeight = this._chartLayoutRect.height + this._chartLayoutRect.y; return leftItems.length && layoutLeftInlineItems(leftItems, this, limitHeight, recompute), topItems.length && layoutTopInlineItems(topItems, this, limitWidth, recompute), rightItems.length && layoutRightInlineItems(rightItems, this, limitHeight, recompute), bottomItems.length && layoutBottomInlineItems(bottomItems, this, limitWidth, recompute), !0; } _layoutRelativeOverlap(orient, info, recompute) { info.items.forEach((item => { const {rect: rect} = getItemLayoutWithTag(item, this, recompute); info.rect.width = Math.max(rect.width + item.layoutPaddingLeft + item.layoutPaddingRight, info.rect.width), info.rect.height = Math.max(rect.height + item.layoutPaddingTop + item.layoutPaddingBottom, info.rect.height); })), info.items.forEach((item => { isEqual(item.getLayoutRect(), info.rect) || item.setLayoutRect(info.rect), "left" === orient ? item.setLayoutStartPosition({ x: this.leftCurrent + item.layoutOffsetX }) : "right" === orient ? item.setLayoutStartPosition({ x: this.rightCurrent - info.rect.width + item.layoutOffsetX }) : "top" === orient ? item.setLayoutStartPosition({ x: this.topCurrent + item.layoutOffsetY }) : item.setLayoutStartPosition({ x: this.bottomCurrent - info.rect.height + item.layoutOffsetY }); })), "left" === orient ? this.leftCurrent += info.rect.width : "right" === orient ? this.rightCurrent -= info.rect.width : "top" === orient ? this.topCurrent += info.rect.height : this.bottomCurrent -= info.rect.height; } _layoutRelativeItem(item, recompute) { const {rect: rect} = getItemLayoutWithTag(item, this, recompute, !1); "left" === item.layoutOrient || "right" === item.layoutOrient ? item.setLayoutRect({ width: rect.width }) : item.setLayoutRect({ height: rect.height }), "left" === item.layoutOrient ? (item.setLayoutStartPosition({ x: this.leftCurrent + item.layoutOffsetX + item.layoutPaddingLeft }), this.leftCurrent += rect.width + item.layoutPaddingLeft + item.layoutPaddingRight) : "right" === item.layoutOrient ? (this.rightCurrent -= rect.width + item.layoutPaddingLeft + item.layoutPaddingRight, item.setLayoutStartPosition({ x: this.rightCurrent + item.layoutOffsetX + item.layoutPaddingLeft })) : "top" === item.layoutOrient ? (item.setLayoutStartPosition({ y: this.topCurrent + item.layoutOffsetY + item.layoutPaddingTop }), this.topCurrent += rect.height + item.layoutPaddingTop + item.layoutPaddingBottom) : "bottom" === item.layoutOrient && (this.bottomCurrent -= rect.height + item.layoutPaddingTop + item.layoutPaddingBottom, item.setLayoutStartPosition({ y: this.bottomCurrent + item.layoutOffsetY + item.layoutPaddingTop })); } _layoutRegionItem(regionItems, regionRelativeTotalWidth, regionRelativeTotalHeight) { const regionWidth = Math.max(Math.min(regionRelativeTotalWidth, ...regionItems.map((region => { var _a; return null !== (_a = region.maxWidth) && void 0 !== _a ? _a : Number.MAX_VALUE; }))), 0), regionHeight = Math.max(Math.min(regionRelativeTotalHeight, ...regionItems.map((region => { var _a; return null !== (_a = region.maxHeight) && void 0 !== _a ? _a : Number.MAX_VALUE; }))), 0); return regionItems.forEach((region => { const width = regionWidth - region.layoutPaddingLeft - region.layoutPaddingRight, height = regionHeight - region.layoutPaddingTop - region.layoutPaddingBottom; region.setLayoutRect({ width: width, height: height }), region.setLayoutStartPosition({ x: this.leftCurrent + region.layoutOffsetX + region.layoutPaddingLeft, y: this.topCurrent + region.layoutOffsetY + region.layoutPaddingTop }); })), { regionHeight: regionHeight, regionWidth: regionWidth }; } layoutRegionItems(regionItems, regionRelativeItems, regionRelativeOverlapItems, overlapItems = { left: { items: [], rect: { width: 0, height: 0 } }, right: { items: [], rect: { width: 0, height: 0 } }, top: { items: [], rect: { width: 0, height: 0 } }, bottom: { items: [], rect: { width: 0, height: 0 } }, z: { items: [], rect: { width: 0, height: 0 } } }, recompute, secondLayoutLeftRight = !1) { const leftBeforeLayout = this.leftCurrent, rightBeforeLayout = this.rightCurrent; let regionRelativeTotalWidth = this.rightCurrent - this.leftCurrent, regionRelativeTotalHeight = this.bottomCurrent - this.topCurrent; regionRelativeItems.filter((x => "left" === x.layoutOrient || "right" === x.layoutOrient)).forEach((item => { this._layoutRelativeItem(item, recompute); })), this._layoutRelativeOverlap("left", overlapItems.left, recompute), this._layoutRelativeOverlap("right", overlapItems.right, recompute), regionRelativeTotalWidth = this.rightCurrent - this.leftCurrent, regionRelativeItems.filter((x => "top" === x.layoutOrient || "bottom" === x.layoutOrient)).forEach((item => { this._layoutRelativeItem(item, recompute); })), this._layoutRelativeOverlap("top", overlapItems.top, recompute), this._layoutRelativeOverlap("bottom", overlapItems.bottom, recompute), regionRelativeTotalHeight = this.bottomCurrent - this.topCurrent, secondLayoutLeftRight && (this.leftCurrent = leftBeforeLayout, this.rightCurrent = rightBeforeLayout, regionRelativeItems.filter((x => "left" === x.layoutOrient || "right" === x.layoutOrient)).forEach((item => { this._layoutRelativeItem(item, recompute); })), regionRelativeTotalWidth = this.rightCurrent - this.leftCurrent); const {regionWidth: regionWidth, regionHeight: regionHeight} = this._layoutRegionItem(regionItems, regionRelativeTotalWidth, regionRelativeTotalHeight); regionRelativeItems.concat(regionRelativeOverlapItems).forEach((item => { if ([ "left", "right" ].includes(item.layoutOrient)) { const relativeRegion = this.filterRegionsWithID(regionItems, item.layoutBindRegionID[0]); item.setLayoutRect({ height: relativeRegion.getLayoutRect().height }), item.setLayoutStartPosition({ y: relativeRegion.getLayoutStartPoint().y + item.layoutOffsetY + item.layoutPaddingTop }), "right" === item.layoutOrient && item.setLayoutStartPosition({ x: item.getLayoutStartPoint().x + regionWidth - regionRelativeTotalWidth }); } else if ([ "top", "bottom" ].includes(item.layoutOrient)) { const relativeRegion = this.filterRegionsWithID(regionItems, item.layoutBindRegionID[0]); item.setLayoutRect({ width: relativeRegion.getLayoutRect().width }), item.setLayoutStartPosition({ x: relativeRegion.getLayoutStartPoint().x + item.layoutOffsetX + item.layoutPaddingLeft }), "bottom" === item.layoutOrient && item.setLayoutStartPosition({ y: item.getLayoutStartPoint().y + regionHeight - regionRelativeTotalHeight }); } })); } layoutAbsoluteItems(absoluteItems) { absoluteItems.forEach((item => { item.willLayoutTag && item.absoluteLayoutInRect(this._chartLayoutRect); })); } filterRegionsWithID(items, id) { var _a; const target = items.find((x => x.getModelId() === id)); return target || (null !== (_a = this._onError) && void 0 !== _a ? _a : error)("can not find target region item, invalid id"), target; } getItemComputeLayoutRect(item) { return { width: this.rightCurrent - this.leftCurrent - item.layoutPaddingLeft - item.layoutPaddingRight, height: this.bottomCurrent - this.topCurrent - item.layoutPaddingTop - item.layoutPaddingBottom }; } _checkAutoIndent(items, layoutTemp) { const result = { top: 0, left: 0, bottom: 0, right: 0 }; return items.forEach((i => { if (!i.getModelVisible() || !i.autoIndent) return; const vOrH = "left" === i.layoutOrient || "right" === i.layoutOrient, itemOuter = i.getLastComputeOutBounds(), outer = this._getOutInLayout(itemOuter, i, layoutTemp); vOrH ? (result.top = Math.max(result.top, outer.top), result.bottom = Math.max(result.bottom, outer.bottom)) : (result.left = Math.max(result.left, outer.left), result.right = Math.max(result.right, outer.right)); })), result; } _getOutInLayout(itemOuter, i, tempBorder) { const {x: x, y: y} = i.getLayoutStartPoint(), {width: width, height: height} = i.getLayoutRect(); return { left: tempBorder.left - (x - itemOuter.x1), right: x + width + itemOuter.x2 - tempBorder.right, top: tempBorder.top - (y - itemOuter.y1), bottom: y + height + itemOuter.y2 - tempBorder.bottom }; } } Layout.type = "base"; //# sourceMappingURL=base-layout.js.map