-
Notifications
You must be signed in to change notification settings - Fork 771
/
set-accessor.ts
129 lines (117 loc) · 4.61 KB
/
set-accessor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* Production setAccessor() function based on Preact by
* Jason Miller (@developit)
* Licensed under the MIT License
* https://github.com/developit/preact/blob/master/LICENSE
*
* Modified for Stencil's compiler and vdom
*/
import { BUILD } from '@build-conditionals';
import { isMemberInElement, plt } from '@platform';
import { isComplexType, toLowerCase } from '@utils';
import { VNODE_FLAGS, XLINK_NS } from '../runtime-constants';
export const setAccessor = (elm: HTMLElement, memberName: string, oldValue: any, newValue: any, isSvg: boolean, flags: number) => {
if (oldValue === newValue) {
return;
}
if (BUILD.vdomClass && memberName === 'class') {
const classList = elm.classList;
parseClassList(oldValue).forEach(cls => classList.remove(cls));
parseClassList(newValue).forEach(cls => classList.add(cls));
} else if (BUILD.vdomStyle && memberName === 'style') {
// update style attribute, css properties and values
if (BUILD.updatable) {
for (const prop in oldValue) {
if (!newValue || newValue[prop] == null) {
if (!BUILD.hydrateServerSide && prop.includes('-')) {
elm.style.removeProperty(prop);
} else {
(elm as any).style[prop] = '';
}
}
}
}
for (const prop in newValue) {
if (!oldValue || newValue[prop] !== oldValue[prop]) {
if (!BUILD.hydrateServerSide && prop.includes('-')) {
elm.style.setProperty(prop, newValue[prop]);
} else {
(elm as any).style[prop] = newValue[prop];
}
}
}
} else if (BUILD.vdomKey && memberName === 'key') {
// minifier will clean this up
} else if (BUILD.vdomRef && memberName === 'ref') {
// minifier will clean this up
if (newValue) {
newValue(elm);
}
} else if (BUILD.vdomListener && memberName.startsWith('on') && !isMemberInElement(elm, memberName)) {
// Event Handlers
// so if the member name starts with "on" and the 3rd characters is
// a capital letter, and it's not already a member on the element,
// then we're assuming it's an event listener
if (isMemberInElement(elm, toLowerCase(memberName))) {
// standard event
// the JSX attribute could have been "onMouseOver" and the
// member name "onmouseover" is on the element's prototype
// so let's add the listener "mouseover", which is all lowercased
memberName = toLowerCase(memberName.substring(2));
} else {
// custom event
// the JSX attribute could have been "onMyCustomEvent"
// so let's trim off the "on" prefix and lowercase the first character
// and add the listener "myCustomEvent"
// except for the first character, we keep the event name case
memberName = toLowerCase(memberName[2]) + memberName.substring(3);
}
if (oldValue) {
plt.rel(elm, memberName, oldValue, false);
}
if (newValue) {
plt.ael(elm, memberName, newValue, false);
}
} else {
// Set property if it exists and it's not a SVG
const isProp = isMemberInElement(elm, memberName);
const isComplex = isComplexType(newValue);
if ((isProp || (isComplex && newValue !== null)) && !isSvg) {
try {
if (!elm.tagName.includes('-')) {
const n = newValue == null ? '' : newValue;
// Workaround for Safari, moving the <input> caret when re-assigning the same valued
if ((elm as any)[memberName] !== n) {
(elm as any)[memberName] = n;
}
} else {
(elm as any)[memberName] = newValue;
}
} catch (e) {}
}
/**
* Need to manually update attribute if:
* - memberName is not an attribute
* - if we are rendering the host element in order to reflect attribute
* - if it's a SVG, since properties might not work in <svg>
* - if the newValue is null/undefined or 'false'.
*/
const isXlinkNs = BUILD.svg && isSvg && (memberName !== (memberName = memberName.replace(/^xlink\:?/, ''))) ? true : false;
if (newValue == null || newValue === false) {
if (isXlinkNs) {
elm.removeAttributeNS(XLINK_NS, toLowerCase(memberName));
} else {
elm.removeAttribute(memberName);
}
} else if ((!isProp || (flags & VNODE_FLAGS.isHost) || isSvg) && !isComplex) {
newValue = newValue === true ? '' : newValue.toString();
if (isXlinkNs) {
elm.setAttributeNS(XLINK_NS, toLowerCase(memberName), newValue);
} else {
elm.setAttribute(memberName, newValue);
}
}
}
};
const parseClassList = (value: string | undefined | null): string[] =>
(!value) ? [] : value.split(/\s+/).filter(c => c);