You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
18 KiB
481 lines
18 KiB
1 month ago
|
|
||
|
/*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||
|
* or more contributor license agreements. See the NOTICE file
|
||
|
* distributed with this work for additional information
|
||
|
* regarding copyright ownership. The ASF licenses this file
|
||
|
* to you under the Apache License, Version 2.0 (the
|
||
|
* "License"); you may not use this file except in compliance
|
||
|
* with the License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing,
|
||
|
* software distributed under the License is distributed on an
|
||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
* KIND, either express or implied. See the License for the
|
||
|
* specific language governing permissions and limitations
|
||
|
* under the License.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||
|
* or more contributor license agreements. See the NOTICE file
|
||
|
* distributed with this work for additional information
|
||
|
* regarding copyright ownership. The ASF licenses this file
|
||
|
* to you under the Apache License, Version 2.0 (the
|
||
|
* "License"); you may not use this file except in compliance
|
||
|
* with the License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing,
|
||
|
* software distributed under the License is distributed on an
|
||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
* KIND, either express or implied. See the License for the
|
||
|
* specific language governing permissions and limitations
|
||
|
* under the License.
|
||
|
*/
|
||
|
import { makeInner, normalizeToArray } from '../util/model.js';
|
||
|
import { assert, bind, each, eqNaN, extend, hasOwn, indexOf, isArrayLike, keys, reduce } from 'zrender/lib/core/util.js';
|
||
|
import { cloneValue } from 'zrender/lib/animation/Animator.js';
|
||
|
import Displayable from 'zrender/lib/graphic/Displayable.js';
|
||
|
import { getAnimationConfig } from './basicTransition.js';
|
||
|
import { Path } from '../util/graphic.js';
|
||
|
import { warn } from '../util/log.js';
|
||
|
import { TRANSFORMABLE_PROPS } from 'zrender/lib/core/Transformable.js';
|
||
|
var LEGACY_TRANSFORM_PROPS_MAP = {
|
||
|
position: ['x', 'y'],
|
||
|
scale: ['scaleX', 'scaleY'],
|
||
|
origin: ['originX', 'originY']
|
||
|
};
|
||
|
var LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP);
|
||
|
var TRANSFORM_PROPS_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {
|
||
|
obj[key] = 1;
|
||
|
return obj;
|
||
|
}, {});
|
||
|
var transformPropNamesStr = TRANSFORMABLE_PROPS.join(', ');
|
||
|
// '' means root
|
||
|
export var ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra'];
|
||
|
;
|
||
|
var transitionInnerStore = makeInner();
|
||
|
;
|
||
|
function getElementAnimationConfig(animationType, el, elOption, parentModel, dataIndex) {
|
||
|
var animationProp = animationType + "Animation";
|
||
|
var config = getAnimationConfig(animationType, parentModel, dataIndex) || {};
|
||
|
var userDuring = transitionInnerStore(el).userDuring;
|
||
|
// Only set when duration is > 0 and it's need to be animated.
|
||
|
if (config.duration > 0) {
|
||
|
// For simplicity, if during not specified, the previous during will not work any more.
|
||
|
config.during = userDuring ? bind(duringCall, {
|
||
|
el: el,
|
||
|
userDuring: userDuring
|
||
|
}) : null;
|
||
|
config.setToFinal = true;
|
||
|
config.scope = animationType;
|
||
|
}
|
||
|
extend(config, elOption[animationProp]);
|
||
|
return config;
|
||
|
}
|
||
|
export function applyUpdateTransition(el, elOption, animatableModel, opts) {
|
||
|
opts = opts || {};
|
||
|
var dataIndex = opts.dataIndex,
|
||
|
isInit = opts.isInit,
|
||
|
clearStyle = opts.clearStyle;
|
||
|
var hasAnimation = animatableModel.isAnimationEnabled();
|
||
|
// Save the meta info for further morphing. Like apply on the sub morphing elements.
|
||
|
var store = transitionInnerStore(el);
|
||
|
var styleOpt = elOption.style;
|
||
|
store.userDuring = elOption.during;
|
||
|
var transFromProps = {};
|
||
|
var propsToSet = {};
|
||
|
prepareTransformAllPropsFinal(el, elOption, propsToSet);
|
||
|
prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet);
|
||
|
prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet);
|
||
|
if (!isInit && hasAnimation) {
|
||
|
prepareTransformTransitionFrom(el, elOption, transFromProps);
|
||
|
prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps);
|
||
|
prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps);
|
||
|
prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps);
|
||
|
}
|
||
|
propsToSet.style = styleOpt;
|
||
|
applyPropsDirectly(el, propsToSet, clearStyle);
|
||
|
applyMiscProps(el, elOption);
|
||
|
if (hasAnimation) {
|
||
|
if (isInit) {
|
||
|
var enterFromProps_1 = {};
|
||
|
each(ELEMENT_ANIMATABLE_PROPS, function (propName) {
|
||
|
var prop = propName ? elOption[propName] : elOption;
|
||
|
if (prop && prop.enterFrom) {
|
||
|
if (propName) {
|
||
|
enterFromProps_1[propName] = enterFromProps_1[propName] || {};
|
||
|
}
|
||
|
extend(propName ? enterFromProps_1[propName] : enterFromProps_1, prop.enterFrom);
|
||
|
}
|
||
|
});
|
||
|
var config = getElementAnimationConfig('enter', el, elOption, animatableModel, dataIndex);
|
||
|
if (config.duration > 0) {
|
||
|
el.animateFrom(enterFromProps_1, config);
|
||
|
}
|
||
|
} else {
|
||
|
applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps);
|
||
|
}
|
||
|
}
|
||
|
// Store leave to be used in leave transition.
|
||
|
updateLeaveTo(el, elOption);
|
||
|
styleOpt ? el.dirty() : el.markRedraw();
|
||
|
}
|
||
|
export function updateLeaveTo(el, elOption) {
|
||
|
// Try merge to previous set leaveTo
|
||
|
var leaveToProps = transitionInnerStore(el).leaveToProps;
|
||
|
for (var i = 0; i < ELEMENT_ANIMATABLE_PROPS.length; i++) {
|
||
|
var propName = ELEMENT_ANIMATABLE_PROPS[i];
|
||
|
var prop = propName ? elOption[propName] : elOption;
|
||
|
if (prop && prop.leaveTo) {
|
||
|
if (!leaveToProps) {
|
||
|
leaveToProps = transitionInnerStore(el).leaveToProps = {};
|
||
|
}
|
||
|
if (propName) {
|
||
|
leaveToProps[propName] = leaveToProps[propName] || {};
|
||
|
}
|
||
|
extend(propName ? leaveToProps[propName] : leaveToProps, prop.leaveTo);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
export function applyLeaveTransition(el, elOption, animatableModel, onRemove) {
|
||
|
if (el) {
|
||
|
var parent_1 = el.parent;
|
||
|
var leaveToProps = transitionInnerStore(el).leaveToProps;
|
||
|
if (leaveToProps) {
|
||
|
// TODO TODO use leave after leaveAnimation in series is introduced
|
||
|
// TODO Data index?
|
||
|
var config = getElementAnimationConfig('update', el, elOption, animatableModel, 0);
|
||
|
config.done = function () {
|
||
|
parent_1.remove(el);
|
||
|
onRemove && onRemove();
|
||
|
};
|
||
|
el.animateTo(leaveToProps, config);
|
||
|
} else {
|
||
|
parent_1.remove(el);
|
||
|
onRemove && onRemove();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
export function isTransitionAll(transition) {
|
||
|
return transition === 'all';
|
||
|
}
|
||
|
function applyPropsDirectly(el,
|
||
|
// Can be null/undefined
|
||
|
allPropsFinal, clearStyle) {
|
||
|
var styleOpt = allPropsFinal.style;
|
||
|
if (!el.isGroup && styleOpt) {
|
||
|
if (clearStyle) {
|
||
|
el.useStyle({});
|
||
|
// When style object changed, how to trade the existing animation?
|
||
|
// It is probably complicated and not needed to cover all the cases.
|
||
|
// But still need consider the case:
|
||
|
// (1) When using init animation on `style.opacity`, and before the animation
|
||
|
// ended users triggers an update by mousewhel. At that time the init
|
||
|
// animation should better be continued rather than terminated.
|
||
|
// So after `useStyle` called, we should change the animation target manually
|
||
|
// to continue the effect of the init animation.
|
||
|
// (2) PENDING: If the previous animation targeted at a `val1`, and currently we need
|
||
|
// to update the value to `val2` and no animation declared, should be terminate
|
||
|
// the previous animation or just modify the target of the animation?
|
||
|
// Therotically That will happen not only on `style` but also on `shape` and
|
||
|
// `transfrom` props. But we haven't handle this case at present yet.
|
||
|
// (3) PENDING: Is it proper to visit `animators` and `targetName`?
|
||
|
var animators = el.animators;
|
||
|
for (var i = 0; i < animators.length; i++) {
|
||
|
var animator = animators[i];
|
||
|
// targetName is the "topKey".
|
||
|
if (animator.targetName === 'style') {
|
||
|
animator.changeTarget(el.style);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
el.setStyle(styleOpt);
|
||
|
}
|
||
|
if (allPropsFinal) {
|
||
|
// Not set style here.
|
||
|
allPropsFinal.style = null;
|
||
|
// Set el to the final state firstly.
|
||
|
allPropsFinal && el.attr(allPropsFinal);
|
||
|
allPropsFinal.style = styleOpt;
|
||
|
}
|
||
|
}
|
||
|
function applyPropsTransition(el, elOption, dataIndex, model,
|
||
|
// Can be null/undefined
|
||
|
transFromProps) {
|
||
|
if (transFromProps) {
|
||
|
var config = getElementAnimationConfig('update', el, elOption, model, dataIndex);
|
||
|
if (config.duration > 0) {
|
||
|
el.animateFrom(transFromProps, config);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function applyMiscProps(el, elOption) {
|
||
|
// Merge by default.
|
||
|
hasOwn(elOption, 'silent') && (el.silent = elOption.silent);
|
||
|
hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore);
|
||
|
if (el instanceof Displayable) {
|
||
|
hasOwn(elOption, 'invisible') && (el.invisible = elOption.invisible);
|
||
|
}
|
||
|
if (el instanceof Path) {
|
||
|
hasOwn(elOption, 'autoBatch') && (el.autoBatch = elOption.autoBatch);
|
||
|
}
|
||
|
}
|
||
|
// Use it to avoid it be exposed to user.
|
||
|
var tmpDuringScope = {};
|
||
|
var transitionDuringAPI = {
|
||
|
// Usually other props do not need to be changed in animation during.
|
||
|
setTransform: function (key, val) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `setTransform`.');
|
||
|
}
|
||
|
tmpDuringScope.el[key] = val;
|
||
|
return this;
|
||
|
},
|
||
|
getTransform: function (key) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `getTransform`.');
|
||
|
}
|
||
|
return tmpDuringScope.el[key];
|
||
|
},
|
||
|
setShape: function (key, val) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assertNotReserved(key);
|
||
|
}
|
||
|
var el = tmpDuringScope.el;
|
||
|
var shape = el.shape || (el.shape = {});
|
||
|
shape[key] = val;
|
||
|
el.dirtyShape && el.dirtyShape();
|
||
|
return this;
|
||
|
},
|
||
|
getShape: function (key) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assertNotReserved(key);
|
||
|
}
|
||
|
var shape = tmpDuringScope.el.shape;
|
||
|
if (shape) {
|
||
|
return shape[key];
|
||
|
}
|
||
|
},
|
||
|
setStyle: function (key, val) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assertNotReserved(key);
|
||
|
}
|
||
|
var el = tmpDuringScope.el;
|
||
|
var style = el.style;
|
||
|
if (style) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
if (eqNaN(val)) {
|
||
|
warn('style.' + key + ' must not be assigned with NaN.');
|
||
|
}
|
||
|
}
|
||
|
style[key] = val;
|
||
|
el.dirtyStyle && el.dirtyStyle();
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
getStyle: function (key) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assertNotReserved(key);
|
||
|
}
|
||
|
var style = tmpDuringScope.el.style;
|
||
|
if (style) {
|
||
|
return style[key];
|
||
|
}
|
||
|
},
|
||
|
setExtra: function (key, val) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assertNotReserved(key);
|
||
|
}
|
||
|
var extra = tmpDuringScope.el.extra || (tmpDuringScope.el.extra = {});
|
||
|
extra[key] = val;
|
||
|
return this;
|
||
|
},
|
||
|
getExtra: function (key) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
assertNotReserved(key);
|
||
|
}
|
||
|
var extra = tmpDuringScope.el.extra;
|
||
|
if (extra) {
|
||
|
return extra[key];
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
function assertNotReserved(key) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
if (key === 'transition' || key === 'enterFrom' || key === 'leaveTo') {
|
||
|
throw new Error('key must not be "' + key + '"');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function duringCall() {
|
||
|
// Do not provide "percent" until some requirements come.
|
||
|
// Because consider thies case:
|
||
|
// enterFrom: {x: 100, y: 30}, transition: 'x'.
|
||
|
// And enter duration is different from update duration.
|
||
|
// Thus it might be confused about the meaning of "percent" in during callback.
|
||
|
var scope = this;
|
||
|
var el = scope.el;
|
||
|
if (!el) {
|
||
|
return;
|
||
|
}
|
||
|
// If el is remove from zr by reason like legend, during still need to called,
|
||
|
// because el will be added back to zr and the prop value should not be incorrect.
|
||
|
var latestUserDuring = transitionInnerStore(el).userDuring;
|
||
|
var scopeUserDuring = scope.userDuring;
|
||
|
// Ensured a during is only called once in each animation frame.
|
||
|
// If a during is called multiple times in one frame, maybe some users' calculation logic
|
||
|
// might be wrong (not sure whether this usage exists).
|
||
|
// The case of a during might be called twice can be: by default there is a animator for
|
||
|
// 'x', 'y' when init. Before the init animation finished, call `setOption` to start
|
||
|
// another animators for 'style'/'shape'/'extra'.
|
||
|
if (latestUserDuring !== scopeUserDuring) {
|
||
|
// release
|
||
|
scope.el = scope.userDuring = null;
|
||
|
return;
|
||
|
}
|
||
|
tmpDuringScope.el = el;
|
||
|
// Give no `this` to user in "during" calling.
|
||
|
scopeUserDuring(transitionDuringAPI);
|
||
|
// FIXME: if in future meet the case that some prop will be both modified in `during` and `state`,
|
||
|
// consider the issue that the prop might be incorrect when return to "normal" state.
|
||
|
}
|
||
|
|
||
|
function prepareShapeOrExtraTransitionFrom(mainAttr, fromEl, elOption, transFromProps) {
|
||
|
var attrOpt = elOption[mainAttr];
|
||
|
if (!attrOpt) {
|
||
|
return;
|
||
|
}
|
||
|
var elPropsInAttr = fromEl[mainAttr];
|
||
|
var transFromPropsInAttr;
|
||
|
if (elPropsInAttr) {
|
||
|
var transition = elOption.transition;
|
||
|
var attrTransition = attrOpt.transition;
|
||
|
if (attrTransition) {
|
||
|
!transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});
|
||
|
if (isTransitionAll(attrTransition)) {
|
||
|
extend(transFromPropsInAttr, elPropsInAttr);
|
||
|
} else {
|
||
|
var transitionKeys = normalizeToArray(attrTransition);
|
||
|
for (var i = 0; i < transitionKeys.length; i++) {
|
||
|
var key = transitionKeys[i];
|
||
|
var elVal = elPropsInAttr[key];
|
||
|
transFromPropsInAttr[key] = elVal;
|
||
|
}
|
||
|
}
|
||
|
} else if (isTransitionAll(transition) || indexOf(transition, mainAttr) >= 0) {
|
||
|
!transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});
|
||
|
var elPropsInAttrKeys = keys(elPropsInAttr);
|
||
|
for (var i = 0; i < elPropsInAttrKeys.length; i++) {
|
||
|
var key = elPropsInAttrKeys[i];
|
||
|
var elVal = elPropsInAttr[key];
|
||
|
if (isNonStyleTransitionEnabled(attrOpt[key], elVal)) {
|
||
|
transFromPropsInAttr[key] = elVal;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function prepareShapeOrExtraAllPropsFinal(mainAttr, elOption, allProps) {
|
||
|
var attrOpt = elOption[mainAttr];
|
||
|
if (!attrOpt) {
|
||
|
return;
|
||
|
}
|
||
|
var allPropsInAttr = allProps[mainAttr] = {};
|
||
|
var keysInAttr = keys(attrOpt);
|
||
|
for (var i = 0; i < keysInAttr.length; i++) {
|
||
|
var key = keysInAttr[i];
|
||
|
// To avoid share one object with different element, and
|
||
|
// to avoid user modify the object inexpectedly, have to clone.
|
||
|
allPropsInAttr[key] = cloneValue(attrOpt[key]);
|
||
|
}
|
||
|
}
|
||
|
function prepareTransformTransitionFrom(el, elOption, transFromProps) {
|
||
|
var transition = elOption.transition;
|
||
|
var transitionKeys = isTransitionAll(transition) ? TRANSFORMABLE_PROPS : normalizeToArray(transition || []);
|
||
|
for (var i = 0; i < transitionKeys.length; i++) {
|
||
|
var key = transitionKeys[i];
|
||
|
if (key === 'style' || key === 'shape' || key === 'extra') {
|
||
|
continue;
|
||
|
}
|
||
|
var elVal = el[key];
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
checkTransformPropRefer(key, 'el.transition');
|
||
|
}
|
||
|
// Do not clone, animator will perform that clone.
|
||
|
transFromProps[key] = elVal;
|
||
|
}
|
||
|
}
|
||
|
function prepareTransformAllPropsFinal(el, elOption, allProps) {
|
||
|
for (var i = 0; i < LEGACY_TRANSFORM_PROPS.length; i++) {
|
||
|
var legacyName = LEGACY_TRANSFORM_PROPS[i];
|
||
|
var xyName = LEGACY_TRANSFORM_PROPS_MAP[legacyName];
|
||
|
var legacyArr = elOption[legacyName];
|
||
|
if (legacyArr) {
|
||
|
allProps[xyName[0]] = legacyArr[0];
|
||
|
allProps[xyName[1]] = legacyArr[1];
|
||
|
}
|
||
|
}
|
||
|
for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {
|
||
|
var key = TRANSFORMABLE_PROPS[i];
|
||
|
if (elOption[key] != null) {
|
||
|
allProps[key] = elOption[key];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function prepareStyleTransitionFrom(fromEl, elOption, styleOpt, transFromProps) {
|
||
|
if (!styleOpt) {
|
||
|
return;
|
||
|
}
|
||
|
var fromElStyle = fromEl.style;
|
||
|
var transFromStyleProps;
|
||
|
if (fromElStyle) {
|
||
|
var styleTransition = styleOpt.transition;
|
||
|
var elTransition = elOption.transition;
|
||
|
if (styleTransition && !isTransitionAll(styleTransition)) {
|
||
|
var transitionKeys = normalizeToArray(styleTransition);
|
||
|
!transFromStyleProps && (transFromStyleProps = transFromProps.style = {});
|
||
|
for (var i = 0; i < transitionKeys.length; i++) {
|
||
|
var key = transitionKeys[i];
|
||
|
var elVal = fromElStyle[key];
|
||
|
// Do not clone, see `checkNonStyleTansitionRefer`.
|
||
|
transFromStyleProps[key] = elVal;
|
||
|
}
|
||
|
} else if (fromEl.getAnimationStyleProps && (isTransitionAll(elTransition) || isTransitionAll(styleTransition) || indexOf(elTransition, 'style') >= 0)) {
|
||
|
var animationProps = fromEl.getAnimationStyleProps();
|
||
|
var animationStyleProps = animationProps ? animationProps.style : null;
|
||
|
if (animationStyleProps) {
|
||
|
!transFromStyleProps && (transFromStyleProps = transFromProps.style = {});
|
||
|
var styleKeys = keys(styleOpt);
|
||
|
for (var i = 0; i < styleKeys.length; i++) {
|
||
|
var key = styleKeys[i];
|
||
|
if (animationStyleProps[key]) {
|
||
|
var elVal = fromElStyle[key];
|
||
|
transFromStyleProps[key] = elVal;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function isNonStyleTransitionEnabled(optVal, elVal) {
|
||
|
// The same as `checkNonStyleTansitionRefer`.
|
||
|
return !isArrayLike(optVal) ? optVal != null && isFinite(optVal) : optVal !== elVal;
|
||
|
}
|
||
|
var checkTransformPropRefer;
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
checkTransformPropRefer = function (key, usedIn) {
|
||
|
if (!hasOwn(TRANSFORM_PROPS_MAP, key)) {
|
||
|
warn('Prop `' + key + '` is not a permitted in `' + usedIn + '`. ' + 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.');
|
||
|
}
|
||
|
};
|
||
|
}
|