UNPKG

@visactor/vgrammar-core

Version:

VGrammar is a visual grammar library

350 lines (340 loc) 23 kB
import { array, has, isBoolean, isNil, isFunction, isString, isArray, get, isEmpty, isEqual as isObjEqual, isObject } from "@visactor/vutils"; import { BridgeElementKey, MARK_OVERLAP_HIDE_KEY } from "./constants"; import { DiffState, HOOK_EVENT, GrammarMarkType, BuiltInEncodeNames } from "./enums"; import { invokeEncoder, invokeEncoderToItems } from "./mark/encode"; import { removeGraphicItem } from "./util/graphic"; import { transformAttributes } from "./attributes/transform"; import { getLargeRectsPoints, getLargeSymbolsPoints, getLinePoints, isValidPointsChannel, isPointsMarkType } from "./attributes/helpers"; import { getLineSegmentConfigs, getLinePointsFromSegments, parseCollectionMarkAttributes, getConnectLineSegmentConfigs, removeSegmentAttrs } from "./attributes/line"; import { CustomPath2D } from "@visactor/vrender-core"; import { invokeFunctionType, parseField } from "../parse/util"; export class Element { constructor(mark) { this.data = null, this.states = [], this.diffState = DiffState.enter, this.isReserved = !1, this.runtimeStatesEncoder = null, this.items = [], this.getStateAttrs = (stateName, nextStates) => { var _a, _b, _c, _d; const isRuntimeState = !isNil(null === (_a = this.runtimeStatesEncoder) || void 0 === _a ? void 0 : _a[stateName]), encoder = isRuntimeState ? Object.assign(Object.assign({}, null === (_b = this.mark.getSpec().encode) || void 0 === _b ? void 0 : _b[stateName]), this.runtimeStatesEncoder[stateName]) : null === (_c = this.mark.getSpec().encode) || void 0 === _c ? void 0 : _c[stateName]; if (!encoder) return {}; if (isFunction(encoder)) return encoder(this.getDatum(), this, stateName, nextStates); if (!isRuntimeState && (null === (_d = this.graphicItem.states) || void 0 === _d ? void 0 : _d[stateName])) return this.graphicItem.states[stateName]; const stateItems = this.items.map((item => Object.assign({}, item, { nextAttrs: {} }))); invokeEncoderToItems(this, stateItems, encoder, this.mark.parameters()); const graphicAttributes = this.transformElementItems(stateItems, this.mark.markType); return this.graphicItem.states ? this.graphicItem.states[stateName] || (this.graphicItem.states[stateName] = graphicAttributes) : this.graphicItem.states = { [stateName]: graphicAttributes }, graphicAttributes; }, this.mark = mark; } initGraphicItem(attributes = {}) { if (this.graphicItem) return; const attrTransforms = this.mark.getAttributeTransforms(); if (this.graphicItem = this.mark.addGraphicItem(attrTransforms ? transformAttributes(attrTransforms, attributes, this) : attributes, this.groupKey), !this.graphicItem) return; const {graphicName: graphicName} = this.mark.getSpec(); isString(graphicName) ? this.graphicItem.name = graphicName : isFunction(graphicName) && (this.graphicItem.name = graphicName(this)), this.graphicItem[BridgeElementKey] = this, attrTransforms && (this.graphicItem.onBeforeAttributeUpdate = attributes => { if (!this.mark) return attributes; return transformAttributes(attrTransforms, attributes, this); }), this.clearGraphicAttributes(), this.mark.needAnimate() && (this.setPrevGraphicAttributes(null), this.setNextGraphicAttributes(Object.assign({}, attributes)), this.setFinalGraphicAttributes(Object.assign({}, attributes))); } updateGraphicItem() { var _a; if (!this.graphicItem) return; this.diffState === DiffState.exit ? this.graphicItem.releaseStatus = "willRelease" : this.graphicItem.releaseStatus = void 0; const stateAnimation = null === (_a = this.mark.animate) || void 0 === _a ? void 0 : _a.getAnimationConfigs("state"); stateAnimation && 0 !== stateAnimation.length && (this.graphicItem.stateAnimateConfig = stateAnimation[0].originConfig); } getGraphicItem() { return this.graphicItem; } removeGraphicItem() { var _a, _b; this.graphicItem && (null === (_b = null === (_a = this.graphicItem.animates) || void 0 === _a ? void 0 : _a.forEach) || void 0 === _b || _b.call(_a, (animate => animate.stop()))), this.graphicItem && (removeGraphicItem(this.graphicItem), this.graphicItem[BridgeElementKey] = null, this.graphicItem = null); } resetGraphicItem() { this.graphicItem && (this.graphicItem = null); } getBounds() { var _a; return null === (_a = this.graphicItem) || void 0 === _a ? void 0 : _a.AABBBounds; } getStates() { return this.states; } updateData(groupKey, data, key) { var _a; this.mark.emit(HOOK_EVENT.BEFORE_ELEMENT_UPDATE_DATA, { groupKey: groupKey, data: data, key: key }, this), this.data = data; const keyGetter = parseField(key); return this.items = data.map((datum => ({ datum: datum, key: keyGetter(datum), view: this.mark.view, nextAttrs: {} }))), this.groupKey = groupKey, this.key = this.mark.isCollectionMark() ? groupKey : null === (_a = this.items) || void 0 === _a ? void 0 : _a[0].key, this.mark.emit(HOOK_EVENT.AFTER_ELEMENT_UPDATE_DATA, { groupKey: groupKey, data: data, key: key }, this), this.items; } state(markState, parameters) { var _a; const isCollectionMark = this.mark.isCollectionMark(), prevStateValues = this.states, newStateValues = array(invokeFunctionType(markState, parameters, this.getDatum(), this)), stateSort = null === (_a = this.mark.getSpec()) || void 0 === _a ? void 0 : _a.stateSort; stateSort && newStateValues.length && newStateValues.sort(stateSort); const isStateChanged = newStateValues.length !== prevStateValues.length || newStateValues.some(((newState, index) => newState !== prevStateValues[index])); this.states = newStateValues, !isCollectionMark && isStateChanged && this.diffState === DiffState.unChange && (this.diffState = DiffState.update); } encodeGraphic(attrs) { this.coordinateTransformEncode(this.items); const graphicAttributes = this.transformElementItems(this.items, this.mark.markType); attrs && Object.assign(graphicAttributes, attrs), this.graphicItem ? (this.graphicItem.clearStates(), this.graphicItem.states = {}, this.graphicItem.stateProxy = null, MARK_OVERLAP_HIDE_KEY in this.graphicItem.attribute && "visible" in graphicAttributes && delete this.graphicItem.attribute[MARK_OVERLAP_HIDE_KEY], this.applyGraphicAttributes(graphicAttributes)) : this.initGraphicItem(graphicAttributes), this.diffState !== DiffState.enter && this.diffState !== DiffState.update || !this.states.length || this.useStates(this.states), this.mark.markType === GrammarMarkType.shape && (this.graphicItem.datum = this.items[0].datum), this.items.forEach((item => { item.nextAttrs = {}; })), this._setCustomizedShape(); } _setCustomizedShape() { var _a; if (!this.graphicItem) return; const setCustomizedShape = null === (_a = this.mark.getSpec()) || void 0 === _a ? void 0 : _a.setCustomizedShape; setCustomizedShape && (this.graphicItem.pathProxy = attrs => setCustomizedShape(this.data, attrs, new CustomPath2D)); } encodeItems(items, encoders, isReentered = !1, parameters) { const isCollectionMark = this.mark.isCollectionMark(), updateEncoder = encoders[BuiltInEncodeNames.update], enterEncoder = encoders[BuiltInEncodeNames.enter], exitEncoder = encoders[BuiltInEncodeNames.exit], onlyFullEncodeFirst = this.mark.isLargeMode() || isCollectionMark && !this.mark.getSpec().enableSegments; this.diffState === DiffState.enter ? (enterEncoder && invokeEncoderToItems(this, items, enterEncoder, parameters, onlyFullEncodeFirst), updateEncoder && invokeEncoderToItems(this, items, updateEncoder, parameters, onlyFullEncodeFirst)) : this.diffState === DiffState.update ? ((isCollectionMark && enterEncoder || isReentered) && invokeEncoderToItems(this, items, enterEncoder, parameters, onlyFullEncodeFirst), updateEncoder && invokeEncoderToItems(this, items, updateEncoder, parameters, onlyFullEncodeFirst)) : this.diffState === DiffState.exit && exitEncoder && (isReentered && invokeEncoderToItems(this, items, enterEncoder, parameters, onlyFullEncodeFirst), invokeEncoderToItems(this, items, exitEncoder, parameters, onlyFullEncodeFirst)); } coordinateTransformEncode(items) { if (!this.mark.coord || "arc" === this.mark.markType || !0 === this.mark.disableCoordinateTransform) return; const coord = this.mark.coord.output(); items.forEach((item => { const nextAttrs = item.nextAttrs, convertedPoint = coord.convert(nextAttrs); Object.assign(nextAttrs, convertedPoint); })); } hasStateAnimation() { var _a; const stateAnimation = null === (_a = this.mark.animate) || void 0 === _a ? void 0 : _a.getAnimationConfigs("state"); return stateAnimation && stateAnimation.length > 0; } clearStates(hasAnimation) { const stateAnimationEnable = isBoolean(hasAnimation) ? hasAnimation : this.hasStateAnimation(); this.states = [], this.graphicItem && this.graphicItem.clearStates(stateAnimationEnable), this.runtimeStatesEncoder && (this.runtimeStatesEncoder = {}); } _updateRuntimeStates(state, attrs) { this.runtimeStatesEncoder || (this.runtimeStatesEncoder = {}), this.runtimeStatesEncoder[state] = attrs; } hasState(state) { return this.states && state && this.states.includes(state); } updateStates(states) { if (!this.graphicItem) return !1; let nextStates = this.states.slice(); const encode = this.mark.getSpec().encode; let forceClearState = !1, hasUpdate = !1; return Object.keys(states).forEach((stateKey => { var _a; if (!stateKey) return; const stateValue = states[stateKey]; if (isObject(stateValue) && !isObjEqual(stateValue, null === (_a = this.runtimeStatesEncoder) || void 0 === _a ? void 0 : _a[stateKey])) nextStates.includes(stateKey) ? forceClearState = !0 : nextStates.push(stateKey), this._updateRuntimeStates(stateKey, stateValue), hasUpdate = !0; else if (stateValue) !nextStates.includes(stateKey) && (null == encode ? void 0 : encode[stateKey]) && (nextStates.push(stateKey), hasUpdate = !0); else if (nextStates.length) { const newNextStates = nextStates.filter((state => state !== stateKey)); newNextStates.length !== nextStates.length && (hasUpdate = !0, nextStates = newNextStates), this.runtimeStatesEncoder && this.runtimeStatesEncoder[stateKey] && (this.runtimeStatesEncoder[stateKey] = null); } })), forceClearState && this.graphicItem.clearStates(), !!hasUpdate && (this.useStates(nextStates), !0); } addState(state, attrs) { var _a; if (!this.graphicItem) return !1; if (attrs && isString(state) && !isObjEqual(attrs, null === (_a = this.runtimeStatesEncoder) || void 0 === _a ? void 0 : _a[state])) { const nextStates = this.states.slice(); return nextStates.includes(state) ? this.graphicItem.clearStates() : nextStates.push(state), this._updateRuntimeStates(state, attrs), this.useStates(nextStates), !0; } const encode = this.mark.getSpec().encode, nextStates = array(state).reduce(((nextStates, stateName) => (stateName && !nextStates.includes(stateName) && (null == encode ? void 0 : encode[stateName]) && nextStates.push(stateName), nextStates)), this.states.slice()); return nextStates.length !== this.states.length && (this.useStates(nextStates), !0); } removeState(state) { if (!this.graphicItem) return !1; const states = array(state); if (!states.length) return !1; const nextStates = this.states.filter((state => !states.includes(state))); return nextStates.length !== this.states.length && (this.runtimeStatesEncoder && states.forEach((state => { this.runtimeStatesEncoder[state] = null; })), this.useStates(nextStates), !0); } useStates(states, hasAnimation) { var _a; if (!this.graphicItem) return !1; this.mark.emit(HOOK_EVENT.BEFORE_ELEMENT_STATE, { states: states }, this); const stateSort = null === (_a = this.mark.getSpec()) || void 0 === _a ? void 0 : _a.stateSort; stateSort && states.sort(stateSort), this.states = states; const stateAnimationEnable = isBoolean(hasAnimation) ? hasAnimation : this.hasStateAnimation(); return this.graphicItem.stateProxy = this.getStateAttrs, this.graphicItem.useStates(this.states, stateAnimationEnable), this.mark.emit(HOOK_EVENT.AFTER_ELEMENT_STATE, { states: states }, this), !0; } diffAttributes(graphicAttributes) { const diffResult = {}, finalGraphicAttributes = this.getFinalGraphicAttributes(); for (const key in graphicAttributes) has(finalGraphicAttributes, key) && isObjEqual(finalGraphicAttributes[key], graphicAttributes[key]) || (diffResult[key] = graphicAttributes[key]); return diffResult; } transformElementItems(items, markType, computePoints) { var _a, _b, _c, _d, _e; const item = items[0]; if (!item.nextAttrs || 0 === Object.keys(item.nextAttrs).length) return {}; let nextAttrs = item.nextAttrs; if (isPointsMarkType(markType) && items && items.length && isNil(null === (_a = item.nextAttrs) || void 0 === _a ? void 0 : _a.points) && (!0 === computePoints || isValidPointsChannel(Object.keys(item.nextAttrs), this.mark.markType))) { const markSpec = this.mark.getSpec(), lastPoints = this.getGraphicAttribute("points", !1), lastSegments = this.getGraphicAttribute("segments", !1), enableSegments = markSpec.enableSegments, connectNullsEncoder = null === (_b = this.mark.getSpec().encode) || void 0 === _b ? void 0 : _b[BuiltInEncodeNames.connectNulls], itemNextAttrs = items.map((item => item.nextAttrs)), isProgressive = this.mark.isProgressive(); if (nextAttrs = parseCollectionMarkAttributes(nextAttrs), markType === GrammarMarkType.line || markType === GrammarMarkType.area) { const linePoints = getLinePoints(items, !0, lastPoints, markType === GrammarMarkType.area); if (isProgressive) nextAttrs.segments = (null !== (_e = null === (_d = null === (_c = this.graphicItem) || void 0 === _c ? void 0 : _c.attribute) || void 0 === _d ? void 0 : _d.segments) && void 0 !== _e ? _e : []).concat([ { points: linePoints } ]); else if (connectNullsEncoder) { if (nextAttrs.segments = getConnectLineSegmentConfigs(itemNextAttrs, linePoints, this), nextAttrs.segments && nextAttrs.segments.some((seg => seg.isConnect))) { const connectStyle = invokeEncoder(connectNullsEncoder, this.getDatum(), this, this.mark.parameters()); connectStyle && nextAttrs.segments.forEach((seg => { seg.isConnect && Object.assign(seg, connectStyle); })); } nextAttrs.points = linePoints; } else if (enableSegments) { const points = linePoints && 0 !== linePoints.length ? linePoints : getLinePointsFromSegments(lastSegments), segments = getLineSegmentConfigs(itemNextAttrs, points, this); segments ? (nextAttrs.segments = segments, nextAttrs.points = null) : (nextAttrs.segments = null, nextAttrs.points = points), nextAttrs = removeSegmentAttrs(nextAttrs, this); } else nextAttrs.points = linePoints, nextAttrs.segments = null; } else markType === GrammarMarkType.largeRects ? nextAttrs.points = getLargeRectsPoints(items, !0, lastPoints) : markType === GrammarMarkType.largeSymbols && (nextAttrs.points = getLargeSymbolsPoints(items, !0, lastPoints)); } return nextAttrs; } applyGraphicAttributes(graphicAttributes) { var _a, _b, _c; if (!isEmpty(graphicAttributes)) if (this.mark.needAnimate()) { const nextGraphicAttributes = this.diffAttributes(graphicAttributes), prevGraphicAttributes = null !== (_a = this.getPrevGraphicAttributes()) && void 0 !== _a ? _a : {}, finalGraphicAttributes = null !== (_b = this.getFinalGraphicAttributes()) && void 0 !== _b ? _b : {}; Object.keys(nextGraphicAttributes).forEach((channel => { prevGraphicAttributes[channel] = this.getGraphicAttribute(channel), finalGraphicAttributes[channel] = nextGraphicAttributes[channel]; })), this.setNextGraphicAttributes(nextGraphicAttributes), this.setPrevGraphicAttributes(prevGraphicAttributes), this.setFinalGraphicAttributes(finalGraphicAttributes); const currentAnimators = null === (_c = this.mark.animate) || void 0 === _c ? void 0 : _c.getElementAnimators(this).filter((animator => { var _a; return !(null === (_a = animator.animationOptions.timeline.controlOptions) || void 0 === _a ? void 0 : _a.ignoreLoopFinalAttributes) || !animator.animationOptions.timeline.loop; })), animateGraphicAttributes = (currentAnimators || []).reduce(((attributes, animator) => Object.assign(attributes, animator.getEndAttributes())), {}), currentGraphicAttributes = Object.assign({}, animateGraphicAttributes, finalGraphicAttributes); this.graphicItem.setAttributes(currentGraphicAttributes); } else this.graphicItem.setAttributes(graphicAttributes); } getGraphicAttribute(channel, prev = !1) { var _a; if (!this.graphicItem) return; if (prev) { let value; const prevGraphicAttributes = this.getPrevGraphicAttributes(); if (!isNil(value = get(prevGraphicAttributes, channel))) return value; } const trans = this.mark.getAttributeTransforms(); let getKey = [ channel ]; if (trans && trans.length) { const channelTransform = trans.find((entry => entry.storedAttrs && entry.channels.includes(channel))); channelTransform && (getKey = [ channelTransform.storedAttrs, channel ]); } return get(null === (_a = this.graphicItem) || void 0 === _a ? void 0 : _a.attribute, getKey); } setGraphicAttribute(channel, value, final = !0) { if (!this.graphicItem) return; const finalGraphicAttributes = this.getFinalGraphicAttributes(), prevGraphicAttributes = this.getPrevGraphicAttributes(); final && finalGraphicAttributes && (finalGraphicAttributes[channel] = value), prevGraphicAttributes && !has(prevGraphicAttributes, channel) && (prevGraphicAttributes[channel] = this.graphicItem.attribute[channel]), this.graphicItem.setAttribute(channel, value); } setGraphicAttributes(attributes, final = !0) { if (!this.graphicItem) return; const finalGraphicAttributes = this.getFinalGraphicAttributes(), prevGraphicAttributes = this.getPrevGraphicAttributes(); Object.keys(attributes).forEach((key => { finalGraphicAttributes && final && (finalGraphicAttributes[key] = attributes[key]), prevGraphicAttributes && !has(prevGraphicAttributes, key) && (prevGraphicAttributes[key] = this.graphicItem.attribute[key]); })), this.graphicItem.setAttributes(attributes); } getFinalGraphicAttributes() { return this.graphicItem.finalAttrs; } setFinalGraphicAttributes(attributes) { this.graphicItem.finalAttrs = attributes; } getPrevGraphicAttributes() { return this.graphicItem.prevAttrs; } setPrevGraphicAttributes(attributes) { this.graphicItem.prevAttrs = attributes; } getNextGraphicAttributes() { return this.graphicItem.nextAttrs; } getFinalAnimationAttribute(channel) { var _a, _b; return null !== (_b = null === (_a = this.getFinalGraphicAttributes()) || void 0 === _a ? void 0 : _a[channel]) && void 0 !== _b ? _b : this.getGraphicAttribute(channel); } getFinalAnimationAttributes() { var _a; return null !== (_a = this.getFinalGraphicAttributes()) && void 0 !== _a ? _a : this.graphicItem.attribute; } setNextGraphicAttributes(attributes) { this.graphicItem.nextAttrs = attributes; } clearChangedGraphicAttributes() { this.graphicItem && (this.setPrevGraphicAttributes(null), this.setNextGraphicAttributes(null)); } clearGraphicAttributes() { this.graphicItem && (this.graphicItem.prevAttrs && this.setPrevGraphicAttributes(null), this.graphicItem.nextAttrs && this.setNextGraphicAttributes(null), this.graphicItem.finalAttrs && this.setFinalGraphicAttributes(null)); } remove() { this.graphicItem && (removeGraphicItem(this.graphicItem), this.graphicItem = null); } release() { this.removeGraphicItem(), this.mark = null, this.data = null, this.items = null; } getItemAttribute(channel) { var _a, _b; if (null === (_a = this.items) || void 0 === _a ? void 0 : _a.length) return this.mark.isCollectionMark() ? isNil(channel) ? this.items.map((item => item.nextAttrs)) : this.items.map((item => { var _a; return null === (_a = item.nextAttrs) || void 0 === _a ? void 0 : _a[channel]; })) : isNil(channel) ? this.items[0].nextAttrs : null === (_b = this.items[0].nextAttrs) || void 0 === _b ? void 0 : _b[channel]; } setItemAttributes(attributes) { var _a; (null === (_a = this.items) || void 0 === _a ? void 0 : _a.length) && (this.mark.isCollectionMark() ? isArray(attributes) && this.items.forEach(((item, index) => { Object.assign(item.nextAttrs, attributes[index]); })) : Object.assign(this.items[0].nextAttrs, attributes)); } getItem() { var _a, _b; return this.mark && this.mark.isCollectionMark() ? null !== (_a = this.items) && void 0 !== _a ? _a : [] : null === (_b = this.items) || void 0 === _b ? void 0 : _b[0]; } getDatum() { var _a, _b; return this.mark && this.mark.isCollectionMark() ? null !== (_a = this.data) && void 0 !== _a ? _a : [] : null === (_b = this.data) || void 0 === _b ? void 0 : _b[0]; } } //# sourceMappingURL=element.js.map