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