@visactor/vgrammar-core
Version:
VGrammar is a visual grammar library
203 lines (197 loc) • 13.4 kB
JavaScript
import { has, isNil, isBoolean, isFunction, isEqual } from "@visactor/vutils";
import { cloneTransformAttributes, transformAttributes } from "./attributes/transform";
import { BridgeElementKey, CollectionMarkType } from "./constants";
import { DiffState, HOOK_EVENT, GrammarMarkType } from "./enums";
import { Element } from "./element";
import { invokeEncoderToItems } from "./mark/encode";
export class GlyphElement extends Element {
constructor(mark) {
super(mark), 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], glyphStateAttributes = {};
if (!encoder) return glyphStateAttributes;
if (isFunction(encoder)) return glyphStateAttributes.attributes = encoder(this.getDatum(), this, stateName, nextStates),
glyphStateAttributes;
if (!isRuntimeState && (null === (_d = this.graphicItem.glyphStates) || void 0 === _d ? void 0 : _d[stateName])) return this.graphicItem.glyphStates[stateName];
if (encoder) {
const item = this.items[0], targetItems = [ Object.assign({}, item, {
nextAttrs: {}
}) ];
return invokeEncoderToItems(this, targetItems, encoder, this.mark.parameters()),
this.coordinateTransformEncode(targetItems), glyphStateAttributes.attributes = targetItems[0].nextAttrs,
this.graphicItem.glyphStates ? this.graphicItem.glyphStates[stateName] || (this.graphicItem.glyphStates[stateName] = glyphStateAttributes) : this.graphicItem.glyphStates = {
[stateName]: glyphStateAttributes
}, glyphStateAttributes;
}
return glyphStateAttributes;
}, this.glyphMeta = this.mark.getGlyphMeta();
}
getGlyphGraphicItems() {
return this.glyphGraphicItems;
}
initGraphicItem(attributes = {}) {
if (this.graphicItem) return;
this.graphicItem = this.mark.addGraphicItem(attributes, this.groupKey), this.graphicItem[BridgeElementKey] = this,
this.graphicItem.onBeforeAttributeUpdate = this._onGlyphAttributeUpdate(!1);
const glyphMarks = this.glyphMeta.getMarks();
this.glyphGraphicItems = {}, this.graphicItem.getSubGraphic().forEach((graphic => {
const markType = glyphMarks[graphic.name];
this.glyphGraphicItems[graphic.name] = graphic, graphic.onBeforeAttributeUpdate = attributes => {
if (!this.mark) return attributes;
return transformAttributes(markType, attributes, this, graphic.name);
};
})), this.clearGraphicAttributes();
}
useStates(states, hasAnimation) {
if (!this.graphicItem) return !1;
this.mark.emit(HOOK_EVENT.BEFORE_ELEMENT_STATE, {
states: states
}, this), this.states = states.slice();
const stateAnimationEnable = isBoolean(hasAnimation) ? hasAnimation : this.hasStateAnimation();
return this.graphicItem.glyphStateProxy = this.getStateAttrs, this.graphicItem.useStates(this.states, stateAnimationEnable),
this.mark.emit(HOOK_EVENT.AFTER_ELEMENT_STATE, {
states: states
}, this), !0;
}
encodeGraphic() {
this.coordinateTransformEncode(this.items);
const graphicAttributes = this.transformElementItems(this.items, this.mark.markType), isGraphicInit = !this.graphicItem;
this.graphicItem ? (this.graphicItem.clearStates(), this.graphicItem.states = {},
this.graphicItem.stateProxy = null) : this.initGraphicItem(), this.diffState === DiffState.enter || isGraphicInit ? (this.graphicItem.onBeforeAttributeUpdate = this._onGlyphAttributeUpdate(!0),
this.applyGraphicAttributes(graphicAttributes), this.graphicItem.onBeforeAttributeUpdate = this._onGlyphAttributeUpdate(!1)) : this.applyGraphicAttributes(graphicAttributes),
this.diffState !== DiffState.enter && this.diffState !== DiffState.update || !this.states.length || (Object.values(this.glyphGraphicItems).forEach((graphicItem => {
graphicItem.states = {};
})), this.useStates(this.states)), this.items.map((item => {
item.nextAttrs = {};
}));
}
encodeCustom(nextAttrs) {
var _a;
let customEncodeValues = {};
const channelEncoder = this.glyphMeta.getChannelEncoder(), functionEncoder = this.glyphMeta.getFunctionEncoder();
if (functionEncoder && (customEncodeValues = functionEncoder.call(null, Object.assign({}, null === (_a = this.graphicItem) || void 0 === _a ? void 0 : _a.attribute, nextAttrs), this.getDatum(), this, this.mark.getGlyphConfig())),
channelEncoder) {
let allAttrs;
Object.keys(channelEncoder).forEach((channel => {
var _a;
if (!isNil(nextAttrs[channel])) {
allAttrs || (allAttrs = Object.assign({}, null === (_a = this.graphicItem) || void 0 === _a ? void 0 : _a.attribute, nextAttrs));
const encodeResult = channelEncoder[channel].call(null, channel, nextAttrs[channel], allAttrs, this.getDatum(), this, this.mark.getGlyphConfig());
Object.keys(null != encodeResult ? encodeResult : {}).forEach((markName => {
var _a;
customEncodeValues[markName] = Object.assign(null !== (_a = customEncodeValues[markName]) && void 0 !== _a ? _a : {}, encodeResult[markName]);
}));
}
}));
}
return customEncodeValues;
}
encodeDefault() {
const defaultEncodeValues = {};
if (this.glyphMeta.getDefaultEncoder()) {
const defaultEncodeResult = this.glyphMeta.getDefaultEncoder().call(null, this.getDatum(), this, this.mark.getGlyphConfig());
Object.assign(defaultEncodeValues, defaultEncodeResult);
}
return defaultEncodeValues;
}
_onGlyphAttributeUpdate(first = !1) {
return attributes => {
if (!this.mark) return attributes;
const glyphMarks = this.glyphMeta.getMarks(), graphicAttributes = transformAttributes(this.mark.getAttributeTransforms(), attributes, this), defaultEncodeValues = first ? this.encodeDefault() : null, customEncodeValues = this.encodeCustom(attributes);
return Object.keys(glyphMarks).forEach((markName => {
const markType = glyphMarks[markName], graphicItem = this.glyphGraphicItems[markName], customAttributes = null == customEncodeValues ? void 0 : customEncodeValues[markName], additionalAttributes = Object.assign({}, customAttributes);
if (first) {
const defaultAttributes = null == defaultEncodeValues ? void 0 : defaultEncodeValues[markName];
Object.keys(null != defaultAttributes ? defaultAttributes : {}).forEach((key => {
has(this.items[0].nextAttrs, key) || has(additionalAttributes, key) || (additionalAttributes[key] = defaultAttributes[key]);
}));
}
const glyphAttributes = Object.assign({}, cloneTransformAttributes(markType, attributes), additionalAttributes), glyphItems = this._generateGlyphItems(markType, this.items, glyphAttributes);
this.coordinateTransformEncode(glyphItems);
const graphicAttributes = this.transformElementItems(glyphItems, markType);
this.applyGlyphGraphicAttributes(graphicAttributes, markName, graphicItem), markType === GrammarMarkType.shape && (graphicItem.datum = glyphItems[0].datum);
})), graphicAttributes;
};
}
_generateGlyphItems(markType, items, additionalAttributes) {
const glyphItems = items.map((item => Object.assign({}, item, {
nextAttrs: additionalAttributes
})));
return CollectionMarkType.includes(markType) && this.mark.getSpec().enableSegments && glyphItems.forEach(((glyphItem, index) => {
glyphItem.nextAttrs = Object.assign({}, items[index].nextAttrs, additionalAttributes);
})), glyphItems;
}
getGraphicAttribute(channel, prev = !1, markName) {
if (!this.graphicItem) return;
const prevGraphicAttributes = this.getPrevGraphicAttributes(markName);
if (prev && has(prevGraphicAttributes, channel)) return prevGraphicAttributes[channel];
return (markName ? this.glyphGraphicItems[markName] : this.graphicItem).attribute[channel];
}
setGraphicAttribute(channel, value, final = !0, markName) {
if (!this.graphicItem) return;
const graphicItem = markName ? this.glyphGraphicItems[markName] : this.graphicItem, finalGraphicAttributes = this.getFinalGraphicAttributes(markName), prevGraphicAttributes = this.getPrevGraphicAttributes(markName);
final && (finalGraphicAttributes[channel] = value), has(prevGraphicAttributes, channel) || (prevGraphicAttributes[channel] = graphicItem.attribute[channel]),
graphicItem.setAttribute(channel, value);
}
setGraphicAttributes(attributes, final = !0, markName) {
if (!this.graphicItem) return;
const graphicItem = markName ? this.glyphGraphicItems[markName] : this.graphicItem, finalGraphicAttributes = this.getFinalGraphicAttributes(markName), prevGraphicAttributes = this.getPrevGraphicAttributes(markName);
Object.keys(attributes).forEach((key => {
final && (finalGraphicAttributes[key] = attributes[key]), has(prevGraphicAttributes, key) || (prevGraphicAttributes[key] = graphicItem.attribute[key]);
})), graphicItem.setAttributes(attributes);
}
diffAttributes(graphicAttributes, markName) {
const diffResult = {}, finalGraphicAttributes = this.getFinalGraphicAttributes(markName);
for (const key in graphicAttributes) has(finalGraphicAttributes, key) && isEqual(finalGraphicAttributes[key], graphicAttributes[key]) || (diffResult[key] = graphicAttributes[key]);
return diffResult;
}
applyGlyphGraphicAttributes(graphicAttributes, markName, graphicItem) {
var _a, _b;
if (this.mark.needAnimate()) {
const nextGraphicAttributes = this.diffAttributes(graphicAttributes, markName), prevGraphicAttributes = null !== (_a = this.getPrevGraphicAttributes(markName)) && void 0 !== _a ? _a : {}, finalGraphicAttributes = null !== (_b = this.getFinalGraphicAttributes(markName)) && void 0 !== _b ? _b : {};
Object.keys(nextGraphicAttributes).forEach((channel => {
prevGraphicAttributes[channel] = graphicItem.attribute[channel], finalGraphicAttributes[channel] = nextGraphicAttributes[channel];
})), this.setNextGraphicAttributes(nextGraphicAttributes, markName), this.setPrevGraphicAttributes(prevGraphicAttributes, markName),
this.setFinalGraphicAttributes(finalGraphicAttributes, markName), graphicItem.setAttributes(nextGraphicAttributes);
} else graphicItem.setAttributes(graphicAttributes);
}
getFinalGraphicAttributes(markName) {
return (markName ? this.glyphGraphicItems[markName] : this.graphicItem).finalAttrs;
}
setFinalGraphicAttributes(attributes, markName) {
(markName ? this.glyphGraphicItems[markName] : this.graphicItem).finalAttrs = attributes;
}
getPrevGraphicAttributes(markName) {
return (markName ? this.glyphGraphicItems[markName] : this.graphicItem).prevAttrs;
}
setPrevGraphicAttributes(attributes, markName) {
(markName ? this.glyphGraphicItems[markName] : this.graphicItem).prevAttrs = attributes;
}
getNextGraphicAttributes(markName) {
return (markName ? this.glyphGraphicItems[markName] : this.graphicItem).nextAttrs;
}
setNextGraphicAttributes(attributes, markName) {
(markName ? this.glyphGraphicItems[markName] : this.graphicItem).nextAttrs = attributes;
}
clearChangedGraphicAttributes() {
this.setPrevGraphicAttributes(null), this.setNextGraphicAttributes(null), Object.keys(this.glyphGraphicItems).forEach((markName => {
this.setPrevGraphicAttributes(null, markName), this.setNextGraphicAttributes(null, markName);
}));
}
clearGraphicAttributes() {
this.setPrevGraphicAttributes(null), this.setNextGraphicAttributes(null), this.setFinalGraphicAttributes(null),
Object.keys(this.glyphGraphicItems).forEach((markName => {
this.setPrevGraphicAttributes(null, markName), this.setNextGraphicAttributes(null, markName),
this.setFinalGraphicAttributes(null, markName);
}));
}
remove() {
this.glyphGraphicItems = null, super.remove();
}
release() {
this.glyphGraphicItems && (Object.values(this.glyphGraphicItems).forEach((graphicItem => {
graphicItem[BridgeElementKey] = null;
})), this.glyphGraphicItems = null), super.release();
}
}
//# sourceMappingURL=glyph-element.js.map