@tarojs/components
Version:
104 lines (101 loc) • 4.77 kB
JavaScript
import { camelToDashCase } from './case.js';
/**
* Modify from https://github.com/ionic-team/stencil-ds-output-targets/blob/main/packages/react-output-target/react-component-lib/utils/attachProps.ts
* MIT License https://github.com/ionic-team/stencil-ds-output-targets/blob/main/LICENSE
*/
const arrayToMap = (arr) => {
const map = new Map();
arr.forEach((s) => map.set(s, s));
return map;
};
const getClassName = (classList, newProps, oldProps) => {
const newClassProp = newProps.className || newProps.class;
const oldClassProp = oldProps.className || oldProps.class;
// map the classes to Maps for performance
const currentClasses = arrayToMap(classList);
const incomingPropClasses = arrayToMap(newClassProp ? newClassProp.split(' ') : []);
const oldPropClasses = arrayToMap(oldClassProp ? oldClassProp.split(' ') : []);
const finalClassNames = [];
// loop through each of the current classes on the component
// to see if it should be a part of the classNames added
currentClasses.forEach((currentClass) => {
if (incomingPropClasses.has(currentClass)) {
// add it as its already included in classnames coming in from newProps
finalClassNames.push(currentClass);
incomingPropClasses.delete(currentClass);
}
else if (!oldPropClasses.has(currentClass)) {
// add it as it has NOT been removed by user
finalClassNames.push(currentClass);
}
});
incomingPropClasses.forEach((s) => finalClassNames.push(s));
return finalClassNames.join(' ');
};
// Note(taro): 禁用 react 合成事件抛出 (避免自定义事件属性调用问题, 例如: event.detail.value 等)
const isCoveredByReact = (__eventNameSuffix) => false;
const syncEvent = (node, eventName, newEventHandler) => {
const eventStore = node.__events || (node.__events = {});
const oldEventHandler = eventStore[eventName];
// Remove old listener so they don't double up.
if (oldEventHandler) {
node.removeEventListener(eventName, oldEventHandler);
}
// Bind new listener.
node.addEventListener(eventName, (eventStore[eventName] = function handler(e) {
if (newEventHandler) {
newEventHandler.call(this, e);
}
}));
};
const attachProps = (node, newProps, oldProps = {}) => {
// some test frameworks don't render DOM elements, so we test here to make sure we are dealing with DOM first
if (node instanceof Element) {
Object.keys(oldProps).forEach((name) => {
if (['style', 'children', 'ref', 'class', 'className', 'forwardedRef'].includes(name)) {
return;
}
// Note: 移除节点上冗余事件、属性
if (!newProps.hasOwnProperty(name)) {
if (/^on([A-Z].*)/.test(name)) {
const eventName = name.substring(2);
const eventNameLc = eventName.toLowerCase();
if (!isCoveredByReact(eventNameLc)) {
syncEvent(node, eventNameLc, undefined);
}
}
else {
node[name] = null;
node.removeAttribute(camelToDashCase(name));
}
}
});
// add any classes in className to the class list
node.className = getClassName(node.classList, newProps, oldProps);
Object.keys(newProps).forEach((name) => {
/** Note(taro): 优化 style 属性的处理
* 1. 考虑到兼容旧版本项目,支持使用字符串配置 style 属性,但这并非推荐写法,且不考虑优化在 style 移除时同步删除属性
* 2. style 属性应当交与前端 UI 框架自行处理,不考虑实现类似于 reactify-wc 的更新策略
*/
if ((name === 'style' && typeof newProps[name] !== 'string') || ['children', 'ref', 'class', 'className', 'forwardedRef'].includes(name)) {
return;
}
if (/^on([A-Z].*)/.test(name)) {
const eventName = name.substring(2);
const eventNameLc = eventName.toLowerCase();
if (!isCoveredByReact(eventNameLc)) {
syncEvent(node, eventNameLc, newProps[name]);
}
}
else {
node[name] = newProps[name];
const propType = typeof newProps[name];
if (propType === 'string') {
node.setAttribute(camelToDashCase(name), newProps[name]);
}
}
});
}
};
export { attachProps, getClassName, isCoveredByReact, syncEvent };
//# sourceMappingURL=attachProps.js.map