mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-06 00:12:21 +00:00
refactor: simplify CSSStyleDeclaration, tailor-make for use-case
This commit is contained in:
+24
-216
@@ -316,222 +316,48 @@ function getCssStr(element) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CSSStyleDeclaration = function(node) {
|
const CSSStyleDeclaration = function(node) {
|
||||||
this.parentNode = node;
|
this.style = node.props.style;
|
||||||
|
|
||||||
this.properties = new Map();
|
this.properties = new Map();
|
||||||
this.hasSynced = false;
|
const { styles } = node;
|
||||||
|
if (!styles || styles.length === 0) {
|
||||||
this.styleAttr = null;
|
|
||||||
this.styleValue = null;
|
|
||||||
|
|
||||||
this.parseError = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a deep clone of this object.
|
|
||||||
*
|
|
||||||
* @param parentNode the parentNode to assign to the cloned result
|
|
||||||
*/
|
|
||||||
CSSStyleDeclaration.prototype.clone = function(parentNode) {
|
|
||||||
let nodeData = {};
|
|
||||||
|
|
||||||
Object.keys(this).forEach(key => {
|
|
||||||
if (key !== 'parentNode') {
|
|
||||||
nodeData[key] = this[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Deep-clone node data.
|
|
||||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
|
||||||
|
|
||||||
const clone = new CSSStyleDeclaration(parentNode);
|
|
||||||
Object.assign(clone, nodeData);
|
|
||||||
return clone;
|
|
||||||
};
|
|
||||||
|
|
||||||
// attr.style
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.addStyleHandler = function() {
|
|
||||||
this.styleAttr = {
|
|
||||||
// empty style attr
|
|
||||||
name: 'style',
|
|
||||||
value: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperty(this.parentNode, 'styles', {
|
|
||||||
get: this.getStyleAttr.bind(this),
|
|
||||||
set: this.setStyleAttr.bind(this),
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addStyleValueHandler();
|
|
||||||
};
|
|
||||||
|
|
||||||
// attr.style.value
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.addStyleValueHandler = function() {
|
|
||||||
Object.defineProperty(this.styleAttr, 'value', {
|
|
||||||
get: this.getStyleValue.bind(this),
|
|
||||||
set: this.setStyleValue.bind(this),
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.getStyleAttr = function() {
|
|
||||||
return this.styleAttr;
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
|
|
||||||
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
|
|
||||||
|
|
||||||
this.styleAttr = newStyleAttr;
|
|
||||||
this.addStyleValueHandler();
|
|
||||||
this.hasSynced = false; // raw css changed
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.getStyleValue = function() {
|
|
||||||
return this.getCssText();
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.setStyleValue = function(newValue) {
|
|
||||||
this.properties.clear(); // reset all existing properties
|
|
||||||
this.styleValue = newValue;
|
|
||||||
this.hasSynced = false; // raw css changed
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype._loadCssText = function() {
|
|
||||||
if (this.hasSynced) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
|
|
||||||
|
|
||||||
if (!this.styleValue || this.styleValue.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const inlineCssStr = this.styleValue;
|
|
||||||
|
|
||||||
let declarations = {};
|
let declarations = {};
|
||||||
try {
|
try {
|
||||||
declarations = csstree.parse(inlineCssStr, {
|
declarations = csstree.parse(styles, {
|
||||||
context: 'declarationList',
|
context: 'declarationList',
|
||||||
parseValue: false,
|
parseValue: false,
|
||||||
});
|
});
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
this.parseError = parseError;
|
console.warn(
|
||||||
|
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
|
||||||
|
parseError,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.parseError = false;
|
|
||||||
|
|
||||||
declarations.children.each(declaration => {
|
declarations.children.each(declaration => {
|
||||||
try {
|
try {
|
||||||
const styleDeclaration = csstreeToStyleDeclaration(declaration);
|
const { name, value, priority } = csstreeToStyleDeclaration(declaration);
|
||||||
this.setProperty(
|
this.setProperty(name, value, priority);
|
||||||
styleDeclaration.name,
|
|
||||||
styleDeclaration.value,
|
|
||||||
styleDeclaration.priority,
|
|
||||||
);
|
|
||||||
} catch (styleError) {
|
} catch (styleError) {
|
||||||
if (styleError.message !== 'Unknown node type: undefined') {
|
if (styleError.message !== 'Unknown node type: undefined') {
|
||||||
this.parseError = styleError;
|
console.warn(
|
||||||
|
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
|
||||||
|
styleError,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// only reads from properties
|
CSSStyleDeclaration.prototype.getProperty = function(propertyName) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the textual representation of the declaration block (equivalent to .cssText attribute).
|
|
||||||
*
|
|
||||||
* @return {String} Textual representation of the declaration block (empty string for no properties)
|
|
||||||
*/
|
|
||||||
CSSStyleDeclaration.prototype.getCssText = function() {
|
|
||||||
const properties = this.getProperties();
|
|
||||||
|
|
||||||
if (this.parseError) {
|
|
||||||
// in case of a parse error, pass through original styles
|
|
||||||
return this.styleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cssText = [];
|
|
||||||
properties.forEach((property, propertyName) => {
|
|
||||||
const strImportant = property.priority === 'important' ? '!important' : '';
|
|
||||||
cssText.push(
|
|
||||||
propertyName.trim() + ':' + property.value.trim() + strImportant,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return cssText.join(';');
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype._handleParseError = function() {
|
|
||||||
if (this.parseError) {
|
|
||||||
console.warn(
|
|
||||||
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
|
|
||||||
this.parseError,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
|
|
||||||
if (typeof propertyName === 'undefined') {
|
if (typeof propertyName === 'undefined') {
|
||||||
throw Error('1 argument required, but only 0 present.');
|
throw Error('1 argument required, but only 0 present.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const properties = this.getProperties();
|
return this.properties.get(propertyName.trim());
|
||||||
this._handleParseError();
|
|
||||||
|
|
||||||
return properties.get(propertyName.trim());
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the optional priority, "important".
|
|
||||||
*
|
|
||||||
* @param {String} propertyName representing the property name to be checked.
|
|
||||||
* @return {String} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
|
|
||||||
*/
|
|
||||||
CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
|
|
||||||
const property = this._getProperty(propertyName);
|
|
||||||
return property ? property.priority : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the property value given a property name.
|
|
||||||
*
|
|
||||||
* @param {String} propertyName representing the property name to be checked.
|
|
||||||
* @return {String} value containing the value of the property. If not set, returns the empty string.
|
|
||||||
*/
|
|
||||||
CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
|
|
||||||
const property = this._getProperty(propertyName);
|
|
||||||
return property ? property.value : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a property name.
|
|
||||||
*
|
|
||||||
* @param {Number} index of the node to be fetched. The index is zero-based.
|
|
||||||
* @return {String} propertyName that is the name of the CSS property at the specified index.
|
|
||||||
*/
|
|
||||||
CSSStyleDeclaration.prototype.item = function(index) {
|
|
||||||
if (typeof index === 'undefined') {
|
|
||||||
throw Error('1 argument required, but only 0 present.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const properties = this.getProperties();
|
|
||||||
this._handleParseError();
|
|
||||||
|
|
||||||
return Array.from(properties.keys())[index];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all properties of the node.
|
|
||||||
*
|
|
||||||
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
|
|
||||||
*/
|
|
||||||
CSSStyleDeclaration.prototype.getProperties = function() {
|
|
||||||
this._loadCssText();
|
|
||||||
return this.properties;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// writes to properties
|
// writes to properties
|
||||||
@@ -553,17 +379,14 @@ CSSStyleDeclaration.prototype.setProperty = function(
|
|||||||
throw Error('propertyName argument required, but only not present.');
|
throw Error('propertyName argument required, but only not present.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const properties = this.getProperties();
|
const trimmedValue = value.trim();
|
||||||
this._handleParseError();
|
|
||||||
|
|
||||||
let trimmedValue = value.trim();
|
|
||||||
const property = {
|
const property = {
|
||||||
value: trimmedValue,
|
value: trimmedValue,
|
||||||
priority: priority.trim(),
|
priority: priority.trim(),
|
||||||
};
|
};
|
||||||
let key = propertyName.trim();
|
const key = propertyName.trim();
|
||||||
properties.set(key, property);
|
this.properties.set(key, property);
|
||||||
this.parentNode.props.style[camelCase(key)] = trimmedValue;
|
this.style[camelCase(key)] = trimmedValue;
|
||||||
|
|
||||||
return property;
|
return property;
|
||||||
};
|
};
|
||||||
@@ -596,18 +419,10 @@ const opts = {
|
|||||||
|
|
||||||
function initStyle(selectedEl) {
|
function initStyle(selectedEl) {
|
||||||
if (!selectedEl.style) {
|
if (!selectedEl.style) {
|
||||||
let value = selectedEl.styles || '';
|
|
||||||
if (!selectedEl.props.style) {
|
if (!selectedEl.props.style) {
|
||||||
selectedEl.props.style = {};
|
selectedEl.props.style = {};
|
||||||
}
|
}
|
||||||
selectedEl.style = new CSSStyleDeclaration(selectedEl);
|
selectedEl.style = new CSSStyleDeclaration(selectedEl);
|
||||||
selectedEl.style.addStyleHandler();
|
|
||||||
selectedEl.styles = {
|
|
||||||
name: 'style',
|
|
||||||
value: value,
|
|
||||||
prefix: '',
|
|
||||||
local: '',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,22 +529,15 @@ export function inlineStyles(document) {
|
|||||||
// no inline styles, external styles, external styles used
|
// no inline styles, external styles, external styles used
|
||||||
// inline styles, external styles same priority as inline styles, inline styles used
|
// inline styles, external styles same priority as inline styles, inline styles used
|
||||||
// inline styles, external styles higher priority than inline styles, external styles used
|
// inline styles, external styles higher priority than inline styles, external styles used
|
||||||
const styleDeclaration = csstreeToStyleDeclaration(
|
const { name, value, priority } = csstreeToStyleDeclaration(
|
||||||
styleCsstreeDeclaration,
|
styleCsstreeDeclaration,
|
||||||
);
|
);
|
||||||
initStyle(selectedEl);
|
initStyle(selectedEl);
|
||||||
if (
|
const styleProperty = selectedEl.style.getProperty(name);
|
||||||
selectedEl.style.getPropertyValue(styleDeclaration.name) !== null &&
|
if (styleProperty && styleProperty.priority >= priority) {
|
||||||
selectedEl.style.getPropertyPriority(styleDeclaration.name) >=
|
|
||||||
styleDeclaration.priority
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectedEl.style.setProperty(
|
selectedEl.style.setProperty(name, value, priority);
|
||||||
styleDeclaration.name,
|
|
||||||
styleDeclaration.value,
|
|
||||||
styleDeclaration.priority,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user