@b9g/crank 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_css.cjs.map +1 -1
- package/_css.js.map +1 -1
- package/_svg.cjs +94 -0
- package/_svg.cjs.map +1 -0
- package/_svg.js +92 -0
- package/_svg.js.map +1 -0
- package/_utils.cjs.map +1 -1
- package/_utils.js.map +1 -1
- package/async.cjs +2 -1
- package/async.cjs.map +1 -1
- package/async.js +2 -1
- package/async.js.map +1 -1
- package/crank.cjs +198 -16
- package/crank.cjs.map +1 -1
- package/crank.js +198 -16
- package/crank.js.map +1 -1
- package/dom.cjs +326 -258
- package/dom.cjs.map +1 -1
- package/dom.js +326 -258
- package/dom.js.map +1 -1
- package/event-target.cjs.map +1 -1
- package/event-target.js.map +1 -1
- package/html.cjs +35 -5
- package/html.cjs.map +1 -1
- package/html.js +35 -5
- package/html.js.map +1 -1
- package/jsx-runtime.cjs.map +1 -1
- package/jsx-runtime.js.map +1 -1
- package/jsx-tag.cjs +86 -21
- package/jsx-tag.cjs.map +1 -1
- package/jsx-tag.js +86 -22
- package/jsx-tag.js.map +1 -1
- package/package.json +1 -2
- package/standalone.cjs +7 -0
- package/standalone.cjs.map +1 -1
- package/standalone.js +2 -0
- package/standalone.js.map +1 -1
- package/umd.js +653 -285
- package/umd.js.map +1 -1
- package/_css.d.ts +0 -21
- package/_utils.d.ts +0 -14
- package/async.d.ts +0 -107
- package/crank.d.ts +0 -732
- package/dom.d.ts +0 -14
- package/event-target.d.ts +0 -26
- package/html.d.ts +0 -24
- package/jsx-runtime.d.ts +0 -6
- package/jsx-tag.d.ts +0 -4
- package/standalone.d.ts +0 -2
- package/umd.d.ts +0 -3
package/dom.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="dom.d.ts" />
|
|
2
2
|
import { Renderer, Portal } from './crank.js';
|
|
3
3
|
import { camelToKebabCase, formatStyleValue } from './_css.js';
|
|
4
|
+
import { REACT_SVG_PROPS } from './_svg.js';
|
|
4
5
|
|
|
5
6
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
6
7
|
const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML";
|
|
@@ -59,19 +60,325 @@ function emitHydrationWarning(propName, quietProps, expectedValue, actualValue,
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
function patchProp(element, name, value, oldValue, props, isSVG, isMathML, copyProps, quietProps, isHydrating) {
|
|
64
|
+
if (copyProps != null && copyProps.has(name)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// handle prop:name or attr:name properties
|
|
68
|
+
const colonIndex = name.indexOf(":");
|
|
69
|
+
if (colonIndex !== -1) {
|
|
70
|
+
const [ns, name1] = [name.slice(0, colonIndex), name.slice(colonIndex + 1)];
|
|
71
|
+
switch (ns) {
|
|
72
|
+
case "prop":
|
|
73
|
+
element[name1] = value;
|
|
74
|
+
return;
|
|
75
|
+
case "attr":
|
|
76
|
+
if (value == null || value === false) {
|
|
77
|
+
if (isHydrating && element.hasAttribute(name1)) {
|
|
78
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name1), element);
|
|
79
|
+
}
|
|
80
|
+
element.removeAttribute(name1);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
else if (value === true) {
|
|
84
|
+
if (isHydrating && !element.hasAttribute(name1)) {
|
|
85
|
+
emitHydrationWarning(name, quietProps, value, null, element);
|
|
86
|
+
}
|
|
87
|
+
element.setAttribute(name1, "");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (typeof value !== "string") {
|
|
91
|
+
value = String(value);
|
|
92
|
+
}
|
|
93
|
+
if (isHydrating && element.getAttribute(name1) !== value) {
|
|
94
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name1), element);
|
|
95
|
+
}
|
|
96
|
+
element.setAttribute(name1, value);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
switch (name) {
|
|
101
|
+
// TODO: fix hydration warnings for the style prop
|
|
102
|
+
case "style": {
|
|
103
|
+
const style = element.style;
|
|
104
|
+
if (value == null || value === false) {
|
|
105
|
+
if (isHydrating && style.cssText !== "") {
|
|
106
|
+
emitHydrationWarning(name, quietProps, value, style.cssText, element);
|
|
107
|
+
}
|
|
108
|
+
element.removeAttribute("style");
|
|
109
|
+
}
|
|
110
|
+
else if (value === true) {
|
|
111
|
+
if (isHydrating && style.cssText !== "") {
|
|
112
|
+
emitHydrationWarning(name, quietProps, "", style.cssText, element);
|
|
113
|
+
}
|
|
114
|
+
element.setAttribute("style", "");
|
|
115
|
+
}
|
|
116
|
+
else if (typeof value === "string") {
|
|
117
|
+
if (style.cssText !== value) {
|
|
118
|
+
// TODO: Fix hydration warnings for styles
|
|
119
|
+
//if (isHydrating) {
|
|
120
|
+
// emitHydrationWarning(
|
|
121
|
+
// name,
|
|
122
|
+
// quietProps,
|
|
123
|
+
// value,
|
|
124
|
+
// style.cssText,
|
|
125
|
+
// element,
|
|
126
|
+
// );
|
|
127
|
+
//}
|
|
128
|
+
style.cssText = value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
if (typeof oldValue === "string") {
|
|
133
|
+
// if the old value was a string, we need to clear the style
|
|
134
|
+
// TODO: only clear the styles enumerated in the old value
|
|
135
|
+
style.cssText = "";
|
|
136
|
+
}
|
|
137
|
+
// First pass: remove styles present in oldValue but not in value
|
|
138
|
+
if (oldValue) {
|
|
139
|
+
for (const styleName in oldValue) {
|
|
140
|
+
if (value && styleName in value)
|
|
141
|
+
continue;
|
|
142
|
+
const cssName = camelToKebabCase(styleName);
|
|
143
|
+
if (isHydrating && style.getPropertyValue(cssName) !== "") {
|
|
144
|
+
emitHydrationWarning(name, quietProps, null, style.getPropertyValue(cssName), element, `style.${styleName}`);
|
|
145
|
+
}
|
|
146
|
+
style.removeProperty(cssName);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Second pass: apply all styles from value
|
|
150
|
+
if (value) {
|
|
151
|
+
for (const styleName in value) {
|
|
152
|
+
const cssName = camelToKebabCase(styleName);
|
|
153
|
+
const styleValue = value[styleName];
|
|
154
|
+
if (styleValue == null) {
|
|
155
|
+
if (isHydrating && style.getPropertyValue(cssName) !== "") {
|
|
156
|
+
emitHydrationWarning(name, quietProps, null, style.getPropertyValue(cssName), element, `style.${styleName}`);
|
|
157
|
+
}
|
|
158
|
+
style.removeProperty(cssName);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const formattedValue = formatStyleValue(cssName, styleValue);
|
|
162
|
+
if (style.getPropertyValue(cssName) !== formattedValue) {
|
|
163
|
+
// TODO: hydration warnings for style props
|
|
164
|
+
//if (isHydrating) {
|
|
165
|
+
// emitHydrationWarning(
|
|
166
|
+
// name,
|
|
167
|
+
// quietProps,
|
|
168
|
+
// formattedValue,
|
|
169
|
+
// style.getPropertyValue(cssName),
|
|
170
|
+
// element,
|
|
171
|
+
// `style.${styleName}`,
|
|
172
|
+
// );
|
|
173
|
+
//}
|
|
174
|
+
style.setProperty(cssName, formattedValue);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case "class":
|
|
183
|
+
case "className":
|
|
184
|
+
if (name === "className" && "class" in props)
|
|
185
|
+
break;
|
|
186
|
+
if (value === true) {
|
|
187
|
+
if (isHydrating && element.getAttribute("class") !== "") {
|
|
188
|
+
emitHydrationWarning(name, quietProps, "", element.getAttribute("class"), element);
|
|
189
|
+
}
|
|
190
|
+
element.setAttribute("class", "");
|
|
191
|
+
}
|
|
192
|
+
else if (value == null) {
|
|
193
|
+
if (isHydrating && element.hasAttribute("class")) {
|
|
194
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute("class"), element);
|
|
195
|
+
}
|
|
196
|
+
element.removeAttribute("class");
|
|
197
|
+
}
|
|
198
|
+
else if (typeof value === "object") {
|
|
199
|
+
// class={{"included-class": true, "excluded-class": false}} syntax
|
|
200
|
+
if (typeof oldValue === "string") {
|
|
201
|
+
// if the old value was a string, we need to clear all classes
|
|
202
|
+
element.setAttribute("class", "");
|
|
203
|
+
}
|
|
204
|
+
let shouldIssueWarning = false;
|
|
205
|
+
const hydratingClasses = isHydrating
|
|
206
|
+
? new Set(Array.from(element.classList))
|
|
207
|
+
: undefined;
|
|
208
|
+
const hydratingClassName = isHydrating
|
|
209
|
+
? element.getAttribute("class")
|
|
210
|
+
: undefined;
|
|
211
|
+
// Two passes: removes first, then adds. This ensures that
|
|
212
|
+
// overlapping classes in different keys are handled correctly.
|
|
213
|
+
// e.g. {"a b": false, "b c": true} should result in "b c"
|
|
214
|
+
// Remove pass: iterate oldValue for classes to remove
|
|
215
|
+
if (oldValue) {
|
|
216
|
+
for (const classNames in oldValue) {
|
|
217
|
+
if (value && value[classNames])
|
|
218
|
+
continue;
|
|
219
|
+
const classes = classNames.split(/\s+/).filter(Boolean);
|
|
220
|
+
element.classList.remove(...classes);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Add pass: iterate value for classes to add
|
|
224
|
+
if (value) {
|
|
225
|
+
for (const classNames in value) {
|
|
226
|
+
if (!value[classNames])
|
|
227
|
+
continue;
|
|
228
|
+
const classes = classNames.split(/\s+/).filter(Boolean);
|
|
229
|
+
element.classList.add(...classes);
|
|
230
|
+
for (const className of classes) {
|
|
231
|
+
if (hydratingClasses && hydratingClasses.has(className)) {
|
|
232
|
+
hydratingClasses.delete(className);
|
|
233
|
+
}
|
|
234
|
+
else if (isHydrating) {
|
|
235
|
+
shouldIssueWarning = true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (shouldIssueWarning ||
|
|
241
|
+
(hydratingClasses && hydratingClasses.size > 0)) {
|
|
242
|
+
emitHydrationWarning(name, quietProps, Object.keys(value)
|
|
243
|
+
.filter((k) => value[k])
|
|
244
|
+
.join(" "), hydratingClassName || "", element);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else if (!isSVG && !isMathML) {
|
|
248
|
+
if (element.className !== value) {
|
|
249
|
+
if (isHydrating) {
|
|
250
|
+
emitHydrationWarning(name, quietProps, value, element.className, element);
|
|
251
|
+
}
|
|
252
|
+
element.className = value;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else if (element.getAttribute("class") !== value) {
|
|
256
|
+
if (isHydrating) {
|
|
257
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute("class"), element);
|
|
258
|
+
}
|
|
259
|
+
element.setAttribute("class", value);
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
case "innerHTML":
|
|
263
|
+
if (value !== oldValue) {
|
|
264
|
+
if (isHydrating) {
|
|
265
|
+
emitHydrationWarning(name, quietProps, value, element.innerHTML, element);
|
|
266
|
+
}
|
|
267
|
+
element.innerHTML = value;
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case "dangerouslySetInnerHTML": {
|
|
271
|
+
const htmlValue = value && typeof value === "object" && "__html" in value
|
|
272
|
+
? (value.__html ?? "")
|
|
273
|
+
: "";
|
|
274
|
+
const oldHtmlValue = oldValue && typeof oldValue === "object" && "__html" in oldValue
|
|
275
|
+
? (oldValue.__html ?? "")
|
|
276
|
+
: "";
|
|
277
|
+
if (htmlValue !== oldHtmlValue) {
|
|
278
|
+
element.innerHTML = htmlValue;
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
case "htmlFor":
|
|
283
|
+
if ("for" in props)
|
|
284
|
+
break;
|
|
285
|
+
if (value == null || value === false) {
|
|
286
|
+
element.removeAttribute("for");
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
element.setAttribute("for", String(value === true ? "" : value));
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
default: {
|
|
293
|
+
if (name[0] === "o" &&
|
|
294
|
+
name[1] === "n" &&
|
|
295
|
+
name[2] === name[2].toUpperCase() &&
|
|
296
|
+
typeof value === "function") {
|
|
297
|
+
// Support React-style event names (onClick, onChange, etc.)
|
|
298
|
+
name = name.toLowerCase();
|
|
299
|
+
}
|
|
300
|
+
// Support React-style SVG attribute names (strokeWidth, etc.)
|
|
301
|
+
if (isSVG && name in REACT_SVG_PROPS) {
|
|
302
|
+
name = REACT_SVG_PROPS[name];
|
|
303
|
+
}
|
|
304
|
+
// try to set the property directly
|
|
305
|
+
if (name in element &&
|
|
306
|
+
// boolean properties will coerce strings, but sometimes they map to
|
|
307
|
+
// enumerated attributes, where truthy strings ("false", "no") map to
|
|
308
|
+
// falsy properties, so we force using setAttribute.
|
|
309
|
+
!(typeof value === "string" &&
|
|
310
|
+
typeof element[name] === "boolean") &&
|
|
311
|
+
isWritableProperty(element, name)) {
|
|
312
|
+
// For URL properties like src and href, the DOM property returns the
|
|
313
|
+
// resolved absolute URL. We need to resolve the prop value the same way
|
|
314
|
+
// to compare correctly.
|
|
315
|
+
let domValue = element[name];
|
|
316
|
+
let propValue = value;
|
|
317
|
+
if ((name === "src" || name === "href") &&
|
|
318
|
+
typeof value === "string" &&
|
|
319
|
+
typeof domValue === "string") {
|
|
320
|
+
try {
|
|
321
|
+
propValue = new URL(value, element.baseURI).href;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// Invalid URL, use original value for comparison
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (propValue !== domValue || oldValue === undefined) {
|
|
328
|
+
if (isHydrating &&
|
|
329
|
+
typeof element[name] === "string" &&
|
|
330
|
+
element[name] !== value) {
|
|
331
|
+
emitHydrationWarning(name, quietProps, value, element[name], element);
|
|
332
|
+
}
|
|
333
|
+
// if the property is writable, assign it directly
|
|
334
|
+
element[name] = value;
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (value === true) {
|
|
339
|
+
value = "";
|
|
340
|
+
}
|
|
341
|
+
else if (value == null || value === false) {
|
|
342
|
+
if (isHydrating && element.hasAttribute(name)) {
|
|
343
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name), element);
|
|
344
|
+
}
|
|
345
|
+
element.removeAttribute(name);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
else if (typeof value !== "string") {
|
|
349
|
+
value = String(value);
|
|
350
|
+
}
|
|
351
|
+
if (element.getAttribute(name) !== value) {
|
|
352
|
+
if (isHydrating) {
|
|
353
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name), element);
|
|
354
|
+
}
|
|
355
|
+
element.setAttribute(name, value);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
62
360
|
const adapter = {
|
|
63
|
-
scope({ scope: xmlns, tag, props, }) {
|
|
361
|
+
scope({ scope: xmlns, tag, props, root, }) {
|
|
64
362
|
switch (tag) {
|
|
65
|
-
case Portal:
|
|
66
|
-
|
|
67
|
-
xmlns =
|
|
363
|
+
case Portal: {
|
|
364
|
+
const ns = root instanceof Element ? root.namespaceURI : null;
|
|
365
|
+
xmlns =
|
|
366
|
+
ns === SVG_NAMESPACE
|
|
367
|
+
? SVG_NAMESPACE
|
|
368
|
+
: ns === MATHML_NAMESPACE
|
|
369
|
+
? MATHML_NAMESPACE
|
|
370
|
+
: undefined;
|
|
68
371
|
break;
|
|
372
|
+
}
|
|
69
373
|
case "svg":
|
|
70
374
|
xmlns = SVG_NAMESPACE;
|
|
71
375
|
break;
|
|
72
376
|
case "math":
|
|
73
377
|
xmlns = MATHML_NAMESPACE;
|
|
74
378
|
break;
|
|
379
|
+
case "foreignObject":
|
|
380
|
+
xmlns = undefined;
|
|
381
|
+
break;
|
|
75
382
|
}
|
|
76
383
|
return props.xmlns || xmlns;
|
|
77
384
|
},
|
|
@@ -85,6 +392,9 @@ const adapter = {
|
|
|
85
392
|
else if (tag.toLowerCase() === "math") {
|
|
86
393
|
xmlns = MATHML_NAMESPACE;
|
|
87
394
|
}
|
|
395
|
+
else if (tag === "foreignObject") {
|
|
396
|
+
xmlns = SVG_NAMESPACE;
|
|
397
|
+
}
|
|
88
398
|
const doc = getRootDocument(root);
|
|
89
399
|
return xmlns ? doc.createElementNS(xmlns, tag) : doc.createElement(tag);
|
|
90
400
|
},
|
|
@@ -108,7 +418,7 @@ const adapter = {
|
|
|
108
418
|
}
|
|
109
419
|
return Array.from(node.childNodes);
|
|
110
420
|
},
|
|
111
|
-
patch({ tagName, node, props, oldProps, scope: xmlns, copyProps, quietProps, isHydrating, }) {
|
|
421
|
+
patch({ tag, tagName, node, props, oldProps, scope: xmlns, copyProps, quietProps, isHydrating, }) {
|
|
112
422
|
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
113
423
|
throw new TypeError(`Cannot patch node: ${String(node)}`);
|
|
114
424
|
}
|
|
@@ -116,268 +426,26 @@ const adapter = {
|
|
|
116
426
|
console.error(`Both "class" and "className" set in props for <${tagName}>. Use one or the other.`);
|
|
117
427
|
}
|
|
118
428
|
const element = node;
|
|
119
|
-
const isSVG = xmlns === SVG_NAMESPACE;
|
|
429
|
+
const isSVG = xmlns === SVG_NAMESPACE || tag === "foreignObject";
|
|
120
430
|
const isMathML = xmlns === MATHML_NAMESPACE;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (copyProps != null && copyProps.has(name)) {
|
|
431
|
+
// First pass: iterate oldProps to handle removals
|
|
432
|
+
if (oldProps) {
|
|
433
|
+
for (let name in oldProps) {
|
|
434
|
+
if (name in props)
|
|
126
435
|
continue;
|
|
127
|
-
|
|
128
|
-
// handle prop:name or attr:name properties
|
|
129
|
-
const colonIndex = name.indexOf(":");
|
|
130
|
-
if (colonIndex !== -1) {
|
|
131
|
-
const [ns, name1] = [
|
|
132
|
-
name.slice(0, colonIndex),
|
|
133
|
-
name.slice(colonIndex + 1),
|
|
134
|
-
];
|
|
135
|
-
switch (ns) {
|
|
136
|
-
case "prop":
|
|
137
|
-
node[name1] = value;
|
|
138
|
-
continue;
|
|
139
|
-
case "attr":
|
|
140
|
-
if (value == null || value === false) {
|
|
141
|
-
if (isHydrating && element.hasAttribute(name1)) {
|
|
142
|
-
emitHydrationWarning(name, quietProps, value, element.getAttribute(name1), element);
|
|
143
|
-
}
|
|
144
|
-
element.removeAttribute(name1);
|
|
145
|
-
}
|
|
146
|
-
else if (value === true) {
|
|
147
|
-
if (isHydrating && !element.hasAttribute(name1)) {
|
|
148
|
-
emitHydrationWarning(name, quietProps, value, null, element);
|
|
149
|
-
}
|
|
150
|
-
element.setAttribute(name1, "");
|
|
151
|
-
}
|
|
152
|
-
else if (typeof value !== "string") {
|
|
153
|
-
value = String(value);
|
|
154
|
-
}
|
|
155
|
-
if (isHydrating && element.getAttribute(name1) !== value) {
|
|
156
|
-
emitHydrationWarning(name, quietProps, value, element.getAttribute(name1), element);
|
|
157
|
-
}
|
|
158
|
-
element.setAttribute(name1, String(value));
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
switch (name) {
|
|
164
|
-
// TODO: fix hydration warnings for the style prop
|
|
165
|
-
case "style": {
|
|
166
|
-
const style = element.style;
|
|
167
|
-
if (value == null || value === false) {
|
|
168
|
-
if (isHydrating && style.cssText !== "") {
|
|
169
|
-
emitHydrationWarning(name, quietProps, value, style.cssText, element);
|
|
170
|
-
}
|
|
171
|
-
element.removeAttribute("style");
|
|
172
|
-
}
|
|
173
|
-
else if (value === true) {
|
|
174
|
-
if (isHydrating && style.cssText !== "") {
|
|
175
|
-
emitHydrationWarning(name, quietProps, "", style.cssText, element);
|
|
176
|
-
}
|
|
177
|
-
element.setAttribute("style", "");
|
|
178
|
-
}
|
|
179
|
-
else if (typeof value === "string") {
|
|
180
|
-
if (style.cssText !== value) {
|
|
181
|
-
// TODO: Fix hydration warnings for styles
|
|
182
|
-
//if (isHydrating) {
|
|
183
|
-
// emitHydrationWarning(
|
|
184
|
-
// name,
|
|
185
|
-
// quietProps,
|
|
186
|
-
// value,
|
|
187
|
-
// style.cssText,
|
|
188
|
-
// element,
|
|
189
|
-
// );
|
|
190
|
-
//}
|
|
191
|
-
style.cssText = value;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
if (typeof oldValue === "string") {
|
|
196
|
-
// if the old value was a string, we need to clear the style
|
|
197
|
-
// TODO: only clear the styles enumerated in the old value
|
|
198
|
-
style.cssText = "";
|
|
199
|
-
}
|
|
200
|
-
for (const styleName in { ...oldValue, ...value }) {
|
|
201
|
-
const cssName = camelToKebabCase(styleName);
|
|
202
|
-
const styleValue = value && value[styleName];
|
|
203
|
-
if (styleValue == null) {
|
|
204
|
-
if (isHydrating && style.getPropertyValue(cssName) !== "") {
|
|
205
|
-
emitHydrationWarning(name, quietProps, null, style.getPropertyValue(cssName), element, `style.${styleName}`);
|
|
206
|
-
}
|
|
207
|
-
style.removeProperty(cssName);
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
const formattedValue = formatStyleValue(cssName, styleValue);
|
|
211
|
-
if (style.getPropertyValue(cssName) !== formattedValue) {
|
|
212
|
-
// TODO: hydration warnings for style props
|
|
213
|
-
//if (isHydrating) {
|
|
214
|
-
// emitHydrationWarning(
|
|
215
|
-
// name,
|
|
216
|
-
// quietProps,
|
|
217
|
-
// formattedValue,
|
|
218
|
-
// style.getPropertyValue(cssName),
|
|
219
|
-
// element,
|
|
220
|
-
// `style.${styleName}`,
|
|
221
|
-
// );
|
|
222
|
-
//}
|
|
223
|
-
style.setProperty(cssName, formattedValue);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
case "class":
|
|
231
|
-
case "className":
|
|
232
|
-
if (value === true) {
|
|
233
|
-
if (isHydrating && element.getAttribute("class") !== "") {
|
|
234
|
-
emitHydrationWarning(name, quietProps, "", element.getAttribute("class"), element);
|
|
235
|
-
}
|
|
236
|
-
element.setAttribute("class", "");
|
|
237
|
-
}
|
|
238
|
-
else if (value == null) {
|
|
239
|
-
if (isHydrating && element.hasAttribute("class")) {
|
|
240
|
-
emitHydrationWarning(name, quietProps, value, element.getAttribute("class"), element);
|
|
241
|
-
}
|
|
242
|
-
element.removeAttribute("class");
|
|
243
|
-
}
|
|
244
|
-
else if (typeof value === "object") {
|
|
245
|
-
// class={{"included-class": true, "excluded-class": false}} syntax
|
|
246
|
-
if (typeof oldValue === "string") {
|
|
247
|
-
// if the old value was a string, we need to clear all classes
|
|
248
|
-
element.setAttribute("class", "");
|
|
249
|
-
}
|
|
250
|
-
let shouldIssueWarning = false;
|
|
251
|
-
const hydratingClasses = isHydrating
|
|
252
|
-
? new Set(Array.from(element.classList))
|
|
253
|
-
: undefined;
|
|
254
|
-
const hydratingClassName = isHydrating
|
|
255
|
-
? element.getAttribute("class")
|
|
256
|
-
: undefined;
|
|
257
|
-
const allClassNames = { ...oldValue, ...value };
|
|
258
|
-
// Two passes: removes first, then adds. This ensures that
|
|
259
|
-
// overlapping classes in different keys are handled correctly.
|
|
260
|
-
// e.g. {"a b": false, "b c": true} should result in "b c"
|
|
261
|
-
for (const classNames in allClassNames) {
|
|
262
|
-
if (!(value && value[classNames])) {
|
|
263
|
-
const classes = classNames.split(/\s+/).filter(Boolean);
|
|
264
|
-
element.classList.remove(...classes);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
for (const classNames in allClassNames) {
|
|
268
|
-
if (value && value[classNames]) {
|
|
269
|
-
const classes = classNames.split(/\s+/).filter(Boolean);
|
|
270
|
-
element.classList.add(...classes);
|
|
271
|
-
for (const className of classes) {
|
|
272
|
-
if (hydratingClasses && hydratingClasses.has(className)) {
|
|
273
|
-
hydratingClasses.delete(className);
|
|
274
|
-
}
|
|
275
|
-
else if (isHydrating) {
|
|
276
|
-
shouldIssueWarning = true;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (shouldIssueWarning ||
|
|
282
|
-
(hydratingClasses && hydratingClasses.size > 0)) {
|
|
283
|
-
emitHydrationWarning(name, quietProps, Object.keys(value)
|
|
284
|
-
.filter((k) => value[k])
|
|
285
|
-
.join(" "), hydratingClassName || "", element);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
else if (!isSVG && !isMathML) {
|
|
289
|
-
if (element.className !== value) {
|
|
290
|
-
if (isHydrating) {
|
|
291
|
-
emitHydrationWarning(name, quietProps, value, element.className, element);
|
|
292
|
-
}
|
|
293
|
-
element.className = value;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else if (element.getAttribute("class") !== value) {
|
|
297
|
-
if (isHydrating) {
|
|
298
|
-
emitHydrationWarning(name, quietProps, value, element.getAttribute("class"), element);
|
|
299
|
-
}
|
|
300
|
-
element.setAttribute("class", value);
|
|
301
|
-
}
|
|
302
|
-
break;
|
|
303
|
-
case "innerHTML":
|
|
304
|
-
if (value !== oldValue) {
|
|
305
|
-
if (isHydrating) {
|
|
306
|
-
emitHydrationWarning(name, quietProps, value, element.innerHTML, element);
|
|
307
|
-
}
|
|
308
|
-
element.innerHTML = value;
|
|
309
|
-
}
|
|
310
|
-
break;
|
|
311
|
-
default: {
|
|
312
|
-
if (name[0] === "o" &&
|
|
313
|
-
name[1] === "n" &&
|
|
314
|
-
name[2] === name[2].toUpperCase() &&
|
|
315
|
-
typeof value === "function") {
|
|
316
|
-
// Support React-style event names (onClick, onChange, etc.)
|
|
317
|
-
name = name.toLowerCase();
|
|
318
|
-
}
|
|
319
|
-
// try to set the property directly
|
|
320
|
-
if (name in element &&
|
|
321
|
-
// boolean properties will coerce strings, but sometimes they map to
|
|
322
|
-
// enumerated attributes, where truthy strings ("false", "no") map to
|
|
323
|
-
// falsy properties, so we force using setAttribute.
|
|
324
|
-
!(typeof value === "string" &&
|
|
325
|
-
typeof element[name] === "boolean") &&
|
|
326
|
-
isWritableProperty(element, name)) {
|
|
327
|
-
// For URL properties like src and href, the DOM property returns the
|
|
328
|
-
// resolved absolute URL. We need to resolve the prop value the same way
|
|
329
|
-
// to compare correctly.
|
|
330
|
-
let domValue = element[name];
|
|
331
|
-
let propValue = value;
|
|
332
|
-
if ((name === "src" || name === "href") &&
|
|
333
|
-
typeof value === "string" &&
|
|
334
|
-
typeof domValue === "string") {
|
|
335
|
-
try {
|
|
336
|
-
propValue = new URL(value, element.baseURI).href;
|
|
337
|
-
}
|
|
338
|
-
catch {
|
|
339
|
-
// Invalid URL, use original value for comparison
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
if (propValue !== domValue || oldValue === undefined) {
|
|
343
|
-
if (isHydrating &&
|
|
344
|
-
typeof element[name] === "string" &&
|
|
345
|
-
element[name] !== value) {
|
|
346
|
-
emitHydrationWarning(name, quietProps, value, element[name], element);
|
|
347
|
-
}
|
|
348
|
-
// if the property is writable, assign it directly
|
|
349
|
-
element[name] = value;
|
|
350
|
-
}
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
if (value === true) {
|
|
354
|
-
value = "";
|
|
355
|
-
}
|
|
356
|
-
else if (value == null || value === false) {
|
|
357
|
-
if (isHydrating && element.hasAttribute(name)) {
|
|
358
|
-
emitHydrationWarning(name, quietProps, value, element.getAttribute(name), element);
|
|
359
|
-
}
|
|
360
|
-
element.removeAttribute(name);
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
else if (typeof value !== "string") {
|
|
364
|
-
value = String(value);
|
|
365
|
-
}
|
|
366
|
-
if (element.getAttribute(name) !== value) {
|
|
367
|
-
if (isHydrating) {
|
|
368
|
-
emitHydrationWarning(name, quietProps, value, element.getAttribute(name), element);
|
|
369
|
-
}
|
|
370
|
-
element.setAttribute(name, value);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
436
|
+
patchProp(element, name, undefined, oldProps[name], props, isSVG, isMathML, copyProps, quietProps, isHydrating);
|
|
373
437
|
}
|
|
374
438
|
}
|
|
439
|
+
// Second pass: iterate props to handle additions and updates
|
|
440
|
+
for (let name in props) {
|
|
441
|
+
patchProp(element, name, props[name], oldProps ? oldProps[name] : undefined, props, isSVG, isMathML, copyProps, quietProps, isHydrating);
|
|
442
|
+
}
|
|
375
443
|
},
|
|
376
444
|
arrange({ tag, node, props, children, }) {
|
|
377
445
|
if (tag === Portal && (node == null || typeof node.nodeType !== "number")) {
|
|
378
446
|
throw new TypeError(`<Portal> root is not a node. Received: ${String(node)}`);
|
|
379
447
|
}
|
|
380
|
-
if (!("innerHTML" in props)) {
|
|
448
|
+
if (!("innerHTML" in props) && !("dangerouslySetInnerHTML" in props)) {
|
|
381
449
|
let oldChild = node.firstChild;
|
|
382
450
|
for (let i = 0; i < children.length; i++) {
|
|
383
451
|
const newChild = children[i];
|