@barefootjs/client 0.1.2 → 0.2.0
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/dist/runtime/apply-rest-attrs.d.ts.map +1 -1
- package/dist/runtime/index.js +178 -99
- package/dist/runtime/spread-attrs.d.ts +4 -7
- package/dist/runtime/spread-attrs.d.ts.map +1 -1
- package/dist/runtime/standalone.js +178 -99
- package/package.json +4 -4
- package/src/runtime/apply-rest-attrs.ts +28 -42
- package/src/runtime/spread-attrs.ts +13 -63
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-rest-attrs.d.ts","sourceRoot":"","sources":["../../src/runtime/apply-rest-attrs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"apply-rest-attrs.d.ts","sourceRoot":"","sources":["../../src/runtime/apply-rest-attrs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EAAE,GACpB,IAAI,CA4DN"}
|
package/dist/runtime/index.js
CHANGED
|
@@ -123,6 +123,156 @@ var BF_KEY = "data-key";
|
|
|
123
123
|
var BF_ASYNC = "bf-async";
|
|
124
124
|
var BF_ASYNC_RESOLVE = "bf-async-resolve";
|
|
125
125
|
var BF_PARENT_SCOPE_PLACEHOLDER = "__BF_PARENT_SCOPE__";
|
|
126
|
+
// ../shared/src/dom-prop.ts
|
|
127
|
+
var BOOLEAN_ATTRS = new Set([
|
|
128
|
+
"checked",
|
|
129
|
+
"disabled",
|
|
130
|
+
"readonly",
|
|
131
|
+
"selected",
|
|
132
|
+
"required",
|
|
133
|
+
"hidden",
|
|
134
|
+
"autofocus",
|
|
135
|
+
"autoplay",
|
|
136
|
+
"controls",
|
|
137
|
+
"loop",
|
|
138
|
+
"muted",
|
|
139
|
+
"open",
|
|
140
|
+
"multiple",
|
|
141
|
+
"novalidate",
|
|
142
|
+
"formnovalidate"
|
|
143
|
+
]);
|
|
144
|
+
var SVG_CAMEL_TO_KEBAB = {
|
|
145
|
+
strokeWidth: "stroke-width",
|
|
146
|
+
strokeLinecap: "stroke-linecap",
|
|
147
|
+
strokeLinejoin: "stroke-linejoin",
|
|
148
|
+
strokeDasharray: "stroke-dasharray",
|
|
149
|
+
strokeDashoffset: "stroke-dashoffset",
|
|
150
|
+
strokeMiterlimit: "stroke-miterlimit",
|
|
151
|
+
strokeOpacity: "stroke-opacity",
|
|
152
|
+
fillOpacity: "fill-opacity",
|
|
153
|
+
fillRule: "fill-rule",
|
|
154
|
+
stopColor: "stop-color",
|
|
155
|
+
stopOpacity: "stop-opacity",
|
|
156
|
+
textAnchor: "text-anchor",
|
|
157
|
+
dominantBaseline: "dominant-baseline",
|
|
158
|
+
alignmentBaseline: "alignment-baseline",
|
|
159
|
+
fontFamily: "font-family",
|
|
160
|
+
fontSize: "font-size",
|
|
161
|
+
fontWeight: "font-weight",
|
|
162
|
+
fontStyle: "font-style",
|
|
163
|
+
letterSpacing: "letter-spacing",
|
|
164
|
+
wordSpacing: "word-spacing",
|
|
165
|
+
pointerEvents: "pointer-events",
|
|
166
|
+
vectorEffect: "vector-effect",
|
|
167
|
+
colorInterpolation: "color-interpolation",
|
|
168
|
+
clipPath: "clip-path",
|
|
169
|
+
clipRule: "clip-rule",
|
|
170
|
+
markerStart: "marker-start",
|
|
171
|
+
markerMid: "marker-mid",
|
|
172
|
+
markerEnd: "marker-end"
|
|
173
|
+
};
|
|
174
|
+
var SVG_XML_CAMEL_ATTRS = new Set([
|
|
175
|
+
"allowReorder",
|
|
176
|
+
"attributeName",
|
|
177
|
+
"attributeType",
|
|
178
|
+
"autoReverse",
|
|
179
|
+
"baseFrequency",
|
|
180
|
+
"baseProfile",
|
|
181
|
+
"calcMode",
|
|
182
|
+
"clipPathUnits",
|
|
183
|
+
"contentScriptType",
|
|
184
|
+
"contentStyleType",
|
|
185
|
+
"diffuseConstant",
|
|
186
|
+
"edgeMode",
|
|
187
|
+
"externalResourcesRequired",
|
|
188
|
+
"filterRes",
|
|
189
|
+
"filterUnits",
|
|
190
|
+
"glyphRef",
|
|
191
|
+
"gradientTransform",
|
|
192
|
+
"gradientUnits",
|
|
193
|
+
"kernelMatrix",
|
|
194
|
+
"kernelUnitLength",
|
|
195
|
+
"keyPoints",
|
|
196
|
+
"keySplines",
|
|
197
|
+
"keyTimes",
|
|
198
|
+
"lengthAdjust",
|
|
199
|
+
"limitingConeAngle",
|
|
200
|
+
"markerHeight",
|
|
201
|
+
"markerUnits",
|
|
202
|
+
"markerWidth",
|
|
203
|
+
"maskContentUnits",
|
|
204
|
+
"maskUnits",
|
|
205
|
+
"numOctaves",
|
|
206
|
+
"pathLength",
|
|
207
|
+
"patternContentUnits",
|
|
208
|
+
"patternTransform",
|
|
209
|
+
"patternUnits",
|
|
210
|
+
"pointsAtX",
|
|
211
|
+
"pointsAtY",
|
|
212
|
+
"pointsAtZ",
|
|
213
|
+
"preserveAlpha",
|
|
214
|
+
"preserveAspectRatio",
|
|
215
|
+
"primitiveUnits",
|
|
216
|
+
"refX",
|
|
217
|
+
"refY",
|
|
218
|
+
"repeatCount",
|
|
219
|
+
"repeatDur",
|
|
220
|
+
"requiredExtensions",
|
|
221
|
+
"requiredFeatures",
|
|
222
|
+
"specularConstant",
|
|
223
|
+
"specularExponent",
|
|
224
|
+
"spreadMethod",
|
|
225
|
+
"startOffset",
|
|
226
|
+
"stdDeviation",
|
|
227
|
+
"stitchTiles",
|
|
228
|
+
"surfaceScale",
|
|
229
|
+
"systemLanguage",
|
|
230
|
+
"tableValues",
|
|
231
|
+
"targetX",
|
|
232
|
+
"targetY",
|
|
233
|
+
"textLength",
|
|
234
|
+
"viewBox",
|
|
235
|
+
"viewTarget",
|
|
236
|
+
"xChannelSelector",
|
|
237
|
+
"yChannelSelector",
|
|
238
|
+
"zoomAndPan"
|
|
239
|
+
]);
|
|
240
|
+
function isEventProp(key) {
|
|
241
|
+
return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
|
|
242
|
+
}
|
|
243
|
+
function classifyDOMProp(key) {
|
|
244
|
+
if (key === "children")
|
|
245
|
+
return { kind: "skip", attrName: key };
|
|
246
|
+
if (key === "ref")
|
|
247
|
+
return { kind: "ref", attrName: key };
|
|
248
|
+
if (isEventProp(key))
|
|
249
|
+
return { kind: "event", attrName: key };
|
|
250
|
+
const attrName = toHTMLAttrNameRuntime(key);
|
|
251
|
+
if (attrName === "style")
|
|
252
|
+
return { kind: "style", attrName };
|
|
253
|
+
if (attrName === "value")
|
|
254
|
+
return { kind: "property", attrName };
|
|
255
|
+
if (attrName === "checked")
|
|
256
|
+
return { kind: "property", attrName };
|
|
257
|
+
if (BOOLEAN_ATTRS.has(attrName.toLowerCase()))
|
|
258
|
+
return { kind: "boolean", attrName };
|
|
259
|
+
return { kind: "attr", attrName };
|
|
260
|
+
}
|
|
261
|
+
function toHTMLAttrNameRuntime(key) {
|
|
262
|
+
if (key === "className")
|
|
263
|
+
return "class";
|
|
264
|
+
if (key === "htmlFor")
|
|
265
|
+
return "for";
|
|
266
|
+
const svgKebab = SVG_CAMEL_TO_KEBAB[key];
|
|
267
|
+
if (svgKebab !== undefined)
|
|
268
|
+
return svgKebab;
|
|
269
|
+
if (SVG_XML_CAMEL_ATTRS.has(key))
|
|
270
|
+
return key;
|
|
271
|
+
if (key.startsWith("data") || key.startsWith("aria")) {
|
|
272
|
+
return key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
273
|
+
}
|
|
274
|
+
return key;
|
|
275
|
+
}
|
|
126
276
|
// src/context.ts
|
|
127
277
|
function createContext(defaultValue) {
|
|
128
278
|
return {
|
|
@@ -1580,13 +1730,6 @@ function styleToCss(value) {
|
|
|
1580
1730
|
}
|
|
1581
1731
|
|
|
1582
1732
|
// src/runtime/apply-rest-attrs.ts
|
|
1583
|
-
function toAttrName(key) {
|
|
1584
|
-
if (key === "className")
|
|
1585
|
-
return "class";
|
|
1586
|
-
if (key === "htmlFor")
|
|
1587
|
-
return "for";
|
|
1588
|
-
return key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
1589
|
-
}
|
|
1590
1733
|
var jsxToDomEventMap = { doubleclick: "dblclick" };
|
|
1591
1734
|
function toEventName(jsxPropName) {
|
|
1592
1735
|
const raw = (jsxPropName[2].toLowerCase() + jsxPropName.slice(3)).toLowerCase();
|
|
@@ -1594,127 +1737,61 @@ function toEventName(jsxPropName) {
|
|
|
1594
1737
|
}
|
|
1595
1738
|
function applyRestAttrs(el, source, excludeKeys) {
|
|
1596
1739
|
const exclude = new Set(excludeKeys);
|
|
1740
|
+
const classified = [];
|
|
1597
1741
|
for (const key of Object.keys(source)) {
|
|
1598
1742
|
if (exclude.has(key))
|
|
1599
1743
|
continue;
|
|
1600
|
-
|
|
1744
|
+
classified.push({ key, c: classifyDOMProp(key) });
|
|
1745
|
+
}
|
|
1746
|
+
for (const { key, c } of classified) {
|
|
1747
|
+
if (c.kind === "ref") {
|
|
1601
1748
|
const ref = source[key];
|
|
1602
1749
|
if (typeof ref === "function")
|
|
1603
1750
|
ref(el);
|
|
1604
|
-
|
|
1605
|
-
}
|
|
1606
|
-
if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase()) {
|
|
1751
|
+
} else if (c.kind === "event") {
|
|
1607
1752
|
const handler = source[key];
|
|
1608
1753
|
if (typeof handler === "function") {
|
|
1609
1754
|
el.addEventListener(toEventName(key), handler);
|
|
1610
1755
|
}
|
|
1611
1756
|
}
|
|
1612
1757
|
}
|
|
1758
|
+
const attrEntries = classified.filter(({ c }) => c.kind !== "ref" && c.kind !== "event" && c.kind !== "skip");
|
|
1613
1759
|
createEffect2(() => {
|
|
1614
|
-
for (const key of
|
|
1615
|
-
if (exclude.has(key))
|
|
1616
|
-
continue;
|
|
1617
|
-
if (key === "ref")
|
|
1618
|
-
continue;
|
|
1619
|
-
if (key === "children")
|
|
1620
|
-
continue;
|
|
1621
|
-
if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase())
|
|
1622
|
-
continue;
|
|
1760
|
+
for (const { key, c } of attrEntries) {
|
|
1623
1761
|
const value = source[key];
|
|
1624
|
-
const attr = toAttrName(key);
|
|
1625
1762
|
if (value != null && value !== false) {
|
|
1626
|
-
if (
|
|
1763
|
+
if (c.kind === "property" && c.attrName === "value" && "value" in el) {
|
|
1627
1764
|
const strVal = String(value);
|
|
1628
1765
|
if (el.value !== strVal)
|
|
1629
1766
|
el.value = strVal;
|
|
1630
|
-
} else if (
|
|
1767
|
+
} else if (c.kind === "property" && c.attrName === "checked" && "checked" in el) {
|
|
1631
1768
|
el.checked = !!value;
|
|
1632
|
-
} else if (
|
|
1769
|
+
} else if (c.kind === "boolean") {
|
|
1770
|
+
el.setAttribute(c.attrName, "");
|
|
1771
|
+
} else if (c.kind === "style") {
|
|
1633
1772
|
const css = styleToCss(value);
|
|
1634
1773
|
if (css == null)
|
|
1635
1774
|
el.removeAttribute("style");
|
|
1636
1775
|
else
|
|
1637
1776
|
el.setAttribute("style", css);
|
|
1638
1777
|
} else {
|
|
1639
|
-
el.setAttribute(
|
|
1778
|
+
el.setAttribute(c.attrName, String(value));
|
|
1640
1779
|
}
|
|
1641
1780
|
} else {
|
|
1642
|
-
if (
|
|
1781
|
+
if (c.kind === "property" && c.attrName === "value" && "value" in el) {
|
|
1782
|
+
el.value = "";
|
|
1783
|
+
} else if (c.kind === "property" && c.attrName === "checked" && "checked" in el) {
|
|
1643
1784
|
el.checked = false;
|
|
1785
|
+
} else if (c.kind === "boolean") {
|
|
1786
|
+
el.removeAttribute(c.attrName);
|
|
1644
1787
|
} else {
|
|
1645
|
-
el.removeAttribute(
|
|
1788
|
+
el.removeAttribute(c.attrName);
|
|
1646
1789
|
}
|
|
1647
1790
|
}
|
|
1648
1791
|
}
|
|
1649
1792
|
});
|
|
1650
1793
|
}
|
|
1651
1794
|
// src/runtime/spread-attrs.ts
|
|
1652
|
-
var SVG_CAMEL_CASE_ATTRS = new Set([
|
|
1653
|
-
"allowReorder",
|
|
1654
|
-
"attributeName",
|
|
1655
|
-
"attributeType",
|
|
1656
|
-
"autoReverse",
|
|
1657
|
-
"baseFrequency",
|
|
1658
|
-
"baseProfile",
|
|
1659
|
-
"calcMode",
|
|
1660
|
-
"clipPathUnits",
|
|
1661
|
-
"contentScriptType",
|
|
1662
|
-
"contentStyleType",
|
|
1663
|
-
"diffuseConstant",
|
|
1664
|
-
"edgeMode",
|
|
1665
|
-
"externalResourcesRequired",
|
|
1666
|
-
"filterRes",
|
|
1667
|
-
"filterUnits",
|
|
1668
|
-
"glyphRef",
|
|
1669
|
-
"gradientTransform",
|
|
1670
|
-
"gradientUnits",
|
|
1671
|
-
"kernelMatrix",
|
|
1672
|
-
"kernelUnitLength",
|
|
1673
|
-
"keyPoints",
|
|
1674
|
-
"keySplines",
|
|
1675
|
-
"keyTimes",
|
|
1676
|
-
"lengthAdjust",
|
|
1677
|
-
"limitingConeAngle",
|
|
1678
|
-
"markerHeight",
|
|
1679
|
-
"markerUnits",
|
|
1680
|
-
"markerWidth",
|
|
1681
|
-
"maskContentUnits",
|
|
1682
|
-
"maskUnits",
|
|
1683
|
-
"numOctaves",
|
|
1684
|
-
"pathLength",
|
|
1685
|
-
"patternContentUnits",
|
|
1686
|
-
"patternTransform",
|
|
1687
|
-
"patternUnits",
|
|
1688
|
-
"pointsAtX",
|
|
1689
|
-
"pointsAtY",
|
|
1690
|
-
"pointsAtZ",
|
|
1691
|
-
"preserveAlpha",
|
|
1692
|
-
"preserveAspectRatio",
|
|
1693
|
-
"primitiveUnits",
|
|
1694
|
-
"refX",
|
|
1695
|
-
"refY",
|
|
1696
|
-
"repeatCount",
|
|
1697
|
-
"repeatDur",
|
|
1698
|
-
"requiredExtensions",
|
|
1699
|
-
"requiredFeatures",
|
|
1700
|
-
"specularConstant",
|
|
1701
|
-
"specularExponent",
|
|
1702
|
-
"spreadMethod",
|
|
1703
|
-
"startOffset",
|
|
1704
|
-
"stdDeviation",
|
|
1705
|
-
"stitchTiles",
|
|
1706
|
-
"surfaceScale",
|
|
1707
|
-
"systemLanguage",
|
|
1708
|
-
"tableValues",
|
|
1709
|
-
"targetX",
|
|
1710
|
-
"targetY",
|
|
1711
|
-
"textLength",
|
|
1712
|
-
"viewBox",
|
|
1713
|
-
"viewTarget",
|
|
1714
|
-
"xChannelSelector",
|
|
1715
|
-
"yChannelSelector",
|
|
1716
|
-
"zoomAndPan"
|
|
1717
|
-
]);
|
|
1718
1795
|
function spreadAttrs(obj) {
|
|
1719
1796
|
if (!obj || typeof obj !== "object")
|
|
1720
1797
|
return "";
|
|
@@ -1722,18 +1799,20 @@ function spreadAttrs(obj) {
|
|
|
1722
1799
|
for (const [key, value] of Object.entries(obj)) {
|
|
1723
1800
|
if (value == null || value === false)
|
|
1724
1801
|
continue;
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
if (key === "children")
|
|
1802
|
+
const c = classifyDOMProp(key);
|
|
1803
|
+
if (c.kind === "event" || c.kind === "skip" || c.kind === "ref")
|
|
1728
1804
|
continue;
|
|
1729
|
-
if (
|
|
1805
|
+
if (c.kind === "style") {
|
|
1730
1806
|
const css = styleToCss(value);
|
|
1731
1807
|
if (css != null)
|
|
1732
1808
|
parts.push(`style="${css}"`);
|
|
1733
1809
|
continue;
|
|
1734
1810
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1811
|
+
if (c.kind === "boolean" && value === true) {
|
|
1812
|
+
parts.push(c.attrName);
|
|
1813
|
+
} else {
|
|
1814
|
+
parts.push(`${c.attrName}="${value}"`);
|
|
1815
|
+
}
|
|
1737
1816
|
}
|
|
1738
1817
|
return parts.join(" ");
|
|
1739
1818
|
}
|
|
@@ -7,13 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
9
|
* Convert an object to an HTML attribute string.
|
|
10
|
-
*
|
|
11
|
-
* event handlers,
|
|
12
|
-
* prop is routed through `styleToCss` so object
|
|
13
|
-
* a real CSS string
|
|
14
|
-
*
|
|
15
|
-
* SVG attributes listed in `SVG_CAMEL_CASE_ATTRS` are preserved
|
|
16
|
-
* verbatim — the SVG XML spec is case-sensitive for those names.
|
|
10
|
+
* Uses the shared classifyDOMProp classifier to determine how each prop
|
|
11
|
+
* maps to the DOM. Skips null/undefined/false, event handlers, ref, and
|
|
12
|
+
* children. The `style` prop is routed through `styleToCss` so object
|
|
13
|
+
* literals serialize to a real CSS string.
|
|
17
14
|
*/
|
|
18
15
|
export declare function spreadAttrs(obj: Record<string, unknown>): string;
|
|
19
16
|
//# sourceMappingURL=spread-attrs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spread-attrs.d.ts","sourceRoot":"","sources":["../../src/runtime/spread-attrs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"spread-attrs.d.ts","sourceRoot":"","sources":["../../src/runtime/spread-attrs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAmBhE"}
|
|
@@ -314,6 +314,156 @@ var BF_KEY = "data-key";
|
|
|
314
314
|
var BF_ASYNC = "bf-async";
|
|
315
315
|
var BF_ASYNC_RESOLVE = "bf-async-resolve";
|
|
316
316
|
var BF_PARENT_SCOPE_PLACEHOLDER = "__BF_PARENT_SCOPE__";
|
|
317
|
+
// ../shared/src/dom-prop.ts
|
|
318
|
+
var BOOLEAN_ATTRS = new Set([
|
|
319
|
+
"checked",
|
|
320
|
+
"disabled",
|
|
321
|
+
"readonly",
|
|
322
|
+
"selected",
|
|
323
|
+
"required",
|
|
324
|
+
"hidden",
|
|
325
|
+
"autofocus",
|
|
326
|
+
"autoplay",
|
|
327
|
+
"controls",
|
|
328
|
+
"loop",
|
|
329
|
+
"muted",
|
|
330
|
+
"open",
|
|
331
|
+
"multiple",
|
|
332
|
+
"novalidate",
|
|
333
|
+
"formnovalidate"
|
|
334
|
+
]);
|
|
335
|
+
var SVG_CAMEL_TO_KEBAB = {
|
|
336
|
+
strokeWidth: "stroke-width",
|
|
337
|
+
strokeLinecap: "stroke-linecap",
|
|
338
|
+
strokeLinejoin: "stroke-linejoin",
|
|
339
|
+
strokeDasharray: "stroke-dasharray",
|
|
340
|
+
strokeDashoffset: "stroke-dashoffset",
|
|
341
|
+
strokeMiterlimit: "stroke-miterlimit",
|
|
342
|
+
strokeOpacity: "stroke-opacity",
|
|
343
|
+
fillOpacity: "fill-opacity",
|
|
344
|
+
fillRule: "fill-rule",
|
|
345
|
+
stopColor: "stop-color",
|
|
346
|
+
stopOpacity: "stop-opacity",
|
|
347
|
+
textAnchor: "text-anchor",
|
|
348
|
+
dominantBaseline: "dominant-baseline",
|
|
349
|
+
alignmentBaseline: "alignment-baseline",
|
|
350
|
+
fontFamily: "font-family",
|
|
351
|
+
fontSize: "font-size",
|
|
352
|
+
fontWeight: "font-weight",
|
|
353
|
+
fontStyle: "font-style",
|
|
354
|
+
letterSpacing: "letter-spacing",
|
|
355
|
+
wordSpacing: "word-spacing",
|
|
356
|
+
pointerEvents: "pointer-events",
|
|
357
|
+
vectorEffect: "vector-effect",
|
|
358
|
+
colorInterpolation: "color-interpolation",
|
|
359
|
+
clipPath: "clip-path",
|
|
360
|
+
clipRule: "clip-rule",
|
|
361
|
+
markerStart: "marker-start",
|
|
362
|
+
markerMid: "marker-mid",
|
|
363
|
+
markerEnd: "marker-end"
|
|
364
|
+
};
|
|
365
|
+
var SVG_XML_CAMEL_ATTRS = new Set([
|
|
366
|
+
"allowReorder",
|
|
367
|
+
"attributeName",
|
|
368
|
+
"attributeType",
|
|
369
|
+
"autoReverse",
|
|
370
|
+
"baseFrequency",
|
|
371
|
+
"baseProfile",
|
|
372
|
+
"calcMode",
|
|
373
|
+
"clipPathUnits",
|
|
374
|
+
"contentScriptType",
|
|
375
|
+
"contentStyleType",
|
|
376
|
+
"diffuseConstant",
|
|
377
|
+
"edgeMode",
|
|
378
|
+
"externalResourcesRequired",
|
|
379
|
+
"filterRes",
|
|
380
|
+
"filterUnits",
|
|
381
|
+
"glyphRef",
|
|
382
|
+
"gradientTransform",
|
|
383
|
+
"gradientUnits",
|
|
384
|
+
"kernelMatrix",
|
|
385
|
+
"kernelUnitLength",
|
|
386
|
+
"keyPoints",
|
|
387
|
+
"keySplines",
|
|
388
|
+
"keyTimes",
|
|
389
|
+
"lengthAdjust",
|
|
390
|
+
"limitingConeAngle",
|
|
391
|
+
"markerHeight",
|
|
392
|
+
"markerUnits",
|
|
393
|
+
"markerWidth",
|
|
394
|
+
"maskContentUnits",
|
|
395
|
+
"maskUnits",
|
|
396
|
+
"numOctaves",
|
|
397
|
+
"pathLength",
|
|
398
|
+
"patternContentUnits",
|
|
399
|
+
"patternTransform",
|
|
400
|
+
"patternUnits",
|
|
401
|
+
"pointsAtX",
|
|
402
|
+
"pointsAtY",
|
|
403
|
+
"pointsAtZ",
|
|
404
|
+
"preserveAlpha",
|
|
405
|
+
"preserveAspectRatio",
|
|
406
|
+
"primitiveUnits",
|
|
407
|
+
"refX",
|
|
408
|
+
"refY",
|
|
409
|
+
"repeatCount",
|
|
410
|
+
"repeatDur",
|
|
411
|
+
"requiredExtensions",
|
|
412
|
+
"requiredFeatures",
|
|
413
|
+
"specularConstant",
|
|
414
|
+
"specularExponent",
|
|
415
|
+
"spreadMethod",
|
|
416
|
+
"startOffset",
|
|
417
|
+
"stdDeviation",
|
|
418
|
+
"stitchTiles",
|
|
419
|
+
"surfaceScale",
|
|
420
|
+
"systemLanguage",
|
|
421
|
+
"tableValues",
|
|
422
|
+
"targetX",
|
|
423
|
+
"targetY",
|
|
424
|
+
"textLength",
|
|
425
|
+
"viewBox",
|
|
426
|
+
"viewTarget",
|
|
427
|
+
"xChannelSelector",
|
|
428
|
+
"yChannelSelector",
|
|
429
|
+
"zoomAndPan"
|
|
430
|
+
]);
|
|
431
|
+
function isEventProp(key) {
|
|
432
|
+
return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
|
|
433
|
+
}
|
|
434
|
+
function classifyDOMProp(key) {
|
|
435
|
+
if (key === "children")
|
|
436
|
+
return { kind: "skip", attrName: key };
|
|
437
|
+
if (key === "ref")
|
|
438
|
+
return { kind: "ref", attrName: key };
|
|
439
|
+
if (isEventProp(key))
|
|
440
|
+
return { kind: "event", attrName: key };
|
|
441
|
+
const attrName = toHTMLAttrNameRuntime(key);
|
|
442
|
+
if (attrName === "style")
|
|
443
|
+
return { kind: "style", attrName };
|
|
444
|
+
if (attrName === "value")
|
|
445
|
+
return { kind: "property", attrName };
|
|
446
|
+
if (attrName === "checked")
|
|
447
|
+
return { kind: "property", attrName };
|
|
448
|
+
if (BOOLEAN_ATTRS.has(attrName.toLowerCase()))
|
|
449
|
+
return { kind: "boolean", attrName };
|
|
450
|
+
return { kind: "attr", attrName };
|
|
451
|
+
}
|
|
452
|
+
function toHTMLAttrNameRuntime(key) {
|
|
453
|
+
if (key === "className")
|
|
454
|
+
return "class";
|
|
455
|
+
if (key === "htmlFor")
|
|
456
|
+
return "for";
|
|
457
|
+
const svgKebab = SVG_CAMEL_TO_KEBAB[key];
|
|
458
|
+
if (svgKebab !== undefined)
|
|
459
|
+
return svgKebab;
|
|
460
|
+
if (SVG_XML_CAMEL_ATTRS.has(key))
|
|
461
|
+
return key;
|
|
462
|
+
if (key.startsWith("data") || key.startsWith("aria")) {
|
|
463
|
+
return key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
464
|
+
}
|
|
465
|
+
return key;
|
|
466
|
+
}
|
|
317
467
|
// src/context.ts
|
|
318
468
|
function createContext(defaultValue) {
|
|
319
469
|
return {
|
|
@@ -1766,13 +1916,6 @@ function styleToCss(value) {
|
|
|
1766
1916
|
}
|
|
1767
1917
|
|
|
1768
1918
|
// src/runtime/apply-rest-attrs.ts
|
|
1769
|
-
function toAttrName(key) {
|
|
1770
|
-
if (key === "className")
|
|
1771
|
-
return "class";
|
|
1772
|
-
if (key === "htmlFor")
|
|
1773
|
-
return "for";
|
|
1774
|
-
return key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
1775
|
-
}
|
|
1776
1919
|
var jsxToDomEventMap = { doubleclick: "dblclick" };
|
|
1777
1920
|
function toEventName(jsxPropName) {
|
|
1778
1921
|
const raw = (jsxPropName[2].toLowerCase() + jsxPropName.slice(3)).toLowerCase();
|
|
@@ -1780,127 +1923,61 @@ function toEventName(jsxPropName) {
|
|
|
1780
1923
|
}
|
|
1781
1924
|
function applyRestAttrs(el, source, excludeKeys) {
|
|
1782
1925
|
const exclude = new Set(excludeKeys);
|
|
1926
|
+
const classified = [];
|
|
1783
1927
|
for (const key of Object.keys(source)) {
|
|
1784
1928
|
if (exclude.has(key))
|
|
1785
1929
|
continue;
|
|
1786
|
-
|
|
1930
|
+
classified.push({ key, c: classifyDOMProp(key) });
|
|
1931
|
+
}
|
|
1932
|
+
for (const { key, c } of classified) {
|
|
1933
|
+
if (c.kind === "ref") {
|
|
1787
1934
|
const ref = source[key];
|
|
1788
1935
|
if (typeof ref === "function")
|
|
1789
1936
|
ref(el);
|
|
1790
|
-
|
|
1791
|
-
}
|
|
1792
|
-
if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase()) {
|
|
1937
|
+
} else if (c.kind === "event") {
|
|
1793
1938
|
const handler = source[key];
|
|
1794
1939
|
if (typeof handler === "function") {
|
|
1795
1940
|
el.addEventListener(toEventName(key), handler);
|
|
1796
1941
|
}
|
|
1797
1942
|
}
|
|
1798
1943
|
}
|
|
1944
|
+
const attrEntries = classified.filter(({ c }) => c.kind !== "ref" && c.kind !== "event" && c.kind !== "skip");
|
|
1799
1945
|
createEffect(() => {
|
|
1800
|
-
for (const key of
|
|
1801
|
-
if (exclude.has(key))
|
|
1802
|
-
continue;
|
|
1803
|
-
if (key === "ref")
|
|
1804
|
-
continue;
|
|
1805
|
-
if (key === "children")
|
|
1806
|
-
continue;
|
|
1807
|
-
if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase())
|
|
1808
|
-
continue;
|
|
1946
|
+
for (const { key, c } of attrEntries) {
|
|
1809
1947
|
const value = source[key];
|
|
1810
|
-
const attr = toAttrName(key);
|
|
1811
1948
|
if (value != null && value !== false) {
|
|
1812
|
-
if (
|
|
1949
|
+
if (c.kind === "property" && c.attrName === "value" && "value" in el) {
|
|
1813
1950
|
const strVal = String(value);
|
|
1814
1951
|
if (el.value !== strVal)
|
|
1815
1952
|
el.value = strVal;
|
|
1816
|
-
} else if (
|
|
1953
|
+
} else if (c.kind === "property" && c.attrName === "checked" && "checked" in el) {
|
|
1817
1954
|
el.checked = !!value;
|
|
1818
|
-
} else if (
|
|
1955
|
+
} else if (c.kind === "boolean") {
|
|
1956
|
+
el.setAttribute(c.attrName, "");
|
|
1957
|
+
} else if (c.kind === "style") {
|
|
1819
1958
|
const css = styleToCss(value);
|
|
1820
1959
|
if (css == null)
|
|
1821
1960
|
el.removeAttribute("style");
|
|
1822
1961
|
else
|
|
1823
1962
|
el.setAttribute("style", css);
|
|
1824
1963
|
} else {
|
|
1825
|
-
el.setAttribute(
|
|
1964
|
+
el.setAttribute(c.attrName, String(value));
|
|
1826
1965
|
}
|
|
1827
1966
|
} else {
|
|
1828
|
-
if (
|
|
1967
|
+
if (c.kind === "property" && c.attrName === "value" && "value" in el) {
|
|
1968
|
+
el.value = "";
|
|
1969
|
+
} else if (c.kind === "property" && c.attrName === "checked" && "checked" in el) {
|
|
1829
1970
|
el.checked = false;
|
|
1971
|
+
} else if (c.kind === "boolean") {
|
|
1972
|
+
el.removeAttribute(c.attrName);
|
|
1830
1973
|
} else {
|
|
1831
|
-
el.removeAttribute(
|
|
1974
|
+
el.removeAttribute(c.attrName);
|
|
1832
1975
|
}
|
|
1833
1976
|
}
|
|
1834
1977
|
}
|
|
1835
1978
|
});
|
|
1836
1979
|
}
|
|
1837
1980
|
// src/runtime/spread-attrs.ts
|
|
1838
|
-
var SVG_CAMEL_CASE_ATTRS = new Set([
|
|
1839
|
-
"allowReorder",
|
|
1840
|
-
"attributeName",
|
|
1841
|
-
"attributeType",
|
|
1842
|
-
"autoReverse",
|
|
1843
|
-
"baseFrequency",
|
|
1844
|
-
"baseProfile",
|
|
1845
|
-
"calcMode",
|
|
1846
|
-
"clipPathUnits",
|
|
1847
|
-
"contentScriptType",
|
|
1848
|
-
"contentStyleType",
|
|
1849
|
-
"diffuseConstant",
|
|
1850
|
-
"edgeMode",
|
|
1851
|
-
"externalResourcesRequired",
|
|
1852
|
-
"filterRes",
|
|
1853
|
-
"filterUnits",
|
|
1854
|
-
"glyphRef",
|
|
1855
|
-
"gradientTransform",
|
|
1856
|
-
"gradientUnits",
|
|
1857
|
-
"kernelMatrix",
|
|
1858
|
-
"kernelUnitLength",
|
|
1859
|
-
"keyPoints",
|
|
1860
|
-
"keySplines",
|
|
1861
|
-
"keyTimes",
|
|
1862
|
-
"lengthAdjust",
|
|
1863
|
-
"limitingConeAngle",
|
|
1864
|
-
"markerHeight",
|
|
1865
|
-
"markerUnits",
|
|
1866
|
-
"markerWidth",
|
|
1867
|
-
"maskContentUnits",
|
|
1868
|
-
"maskUnits",
|
|
1869
|
-
"numOctaves",
|
|
1870
|
-
"pathLength",
|
|
1871
|
-
"patternContentUnits",
|
|
1872
|
-
"patternTransform",
|
|
1873
|
-
"patternUnits",
|
|
1874
|
-
"pointsAtX",
|
|
1875
|
-
"pointsAtY",
|
|
1876
|
-
"pointsAtZ",
|
|
1877
|
-
"preserveAlpha",
|
|
1878
|
-
"preserveAspectRatio",
|
|
1879
|
-
"primitiveUnits",
|
|
1880
|
-
"refX",
|
|
1881
|
-
"refY",
|
|
1882
|
-
"repeatCount",
|
|
1883
|
-
"repeatDur",
|
|
1884
|
-
"requiredExtensions",
|
|
1885
|
-
"requiredFeatures",
|
|
1886
|
-
"specularConstant",
|
|
1887
|
-
"specularExponent",
|
|
1888
|
-
"spreadMethod",
|
|
1889
|
-
"startOffset",
|
|
1890
|
-
"stdDeviation",
|
|
1891
|
-
"stitchTiles",
|
|
1892
|
-
"surfaceScale",
|
|
1893
|
-
"systemLanguage",
|
|
1894
|
-
"tableValues",
|
|
1895
|
-
"targetX",
|
|
1896
|
-
"targetY",
|
|
1897
|
-
"textLength",
|
|
1898
|
-
"viewBox",
|
|
1899
|
-
"viewTarget",
|
|
1900
|
-
"xChannelSelector",
|
|
1901
|
-
"yChannelSelector",
|
|
1902
|
-
"zoomAndPan"
|
|
1903
|
-
]);
|
|
1904
1981
|
function spreadAttrs(obj) {
|
|
1905
1982
|
if (!obj || typeof obj !== "object")
|
|
1906
1983
|
return "";
|
|
@@ -1908,18 +1985,20 @@ function spreadAttrs(obj) {
|
|
|
1908
1985
|
for (const [key, value] of Object.entries(obj)) {
|
|
1909
1986
|
if (value == null || value === false)
|
|
1910
1987
|
continue;
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
if (key === "children")
|
|
1988
|
+
const c = classifyDOMProp(key);
|
|
1989
|
+
if (c.kind === "event" || c.kind === "skip" || c.kind === "ref")
|
|
1914
1990
|
continue;
|
|
1915
|
-
if (
|
|
1991
|
+
if (c.kind === "style") {
|
|
1916
1992
|
const css = styleToCss(value);
|
|
1917
1993
|
if (css != null)
|
|
1918
1994
|
parts.push(`style="${css}"`);
|
|
1919
1995
|
continue;
|
|
1920
1996
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1997
|
+
if (c.kind === "boolean" && value === true) {
|
|
1998
|
+
parts.push(c.attrName);
|
|
1999
|
+
} else {
|
|
2000
|
+
parts.push(`${c.attrName}="${value}"`);
|
|
2001
|
+
}
|
|
1923
2002
|
}
|
|
1924
2003
|
return parts.join(" ");
|
|
1925
2004
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "BarefootJS client package: reactive primitives (SSR-safe) plus browser runtime under the `/runtime` subpath (compiler target)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"directory": "packages/client"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@barefootjs/shared": "0.
|
|
58
|
+
"@barefootjs/shared": "0.2.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@barefootjs/jsx": "0.
|
|
61
|
+
"@barefootjs/jsx": "0.2.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependenciesMeta": {
|
|
64
64
|
"@barefootjs/jsx": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@barefootjs/jsx": "0.
|
|
69
|
+
"@barefootjs/jsx": "0.2.0",
|
|
70
70
|
"@happy-dom/global-registrator": "^20.0.11",
|
|
71
71
|
"typescript": "^5.0.0"
|
|
72
72
|
}
|
|
@@ -6,16 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createEffect } from '@barefootjs/client/reactive'
|
|
9
|
+
import { classifyDOMProp, type DOMPropClassification } from '@barefootjs/shared'
|
|
9
10
|
import { styleToCss } from './style'
|
|
10
11
|
|
|
11
|
-
/** Map of JSX prop names to HTML attribute names */
|
|
12
|
-
function toAttrName(key: string): string {
|
|
13
|
-
if (key === 'className') return 'class'
|
|
14
|
-
if (key === 'htmlFor') return 'for'
|
|
15
|
-
// Convert camelCase to kebab-case for data-* and aria-* style attributes
|
|
16
|
-
return key.replace(/([A-Z])/g, '-$1').toLowerCase()
|
|
17
|
-
}
|
|
18
|
-
|
|
19
12
|
/**
|
|
20
13
|
* Convert a JSX event prop name to a DOM event name for addEventListener.
|
|
21
14
|
* Handles: camelCase → lowercase, plus special mappings (doubleclick → dblclick).
|
|
@@ -23,7 +16,6 @@ function toAttrName(key: string): string {
|
|
|
23
16
|
*/
|
|
24
17
|
const jsxToDomEventMap: Record<string, string> = { doubleclick: 'dblclick' }
|
|
25
18
|
function toEventName(jsxPropName: string): string {
|
|
26
|
-
// onKeyDown → 'k' + 'eyDown' → 'keydown'
|
|
27
19
|
const raw = (jsxPropName[2].toLowerCase() + jsxPropName.slice(3)).toLowerCase()
|
|
28
20
|
return jsxToDomEventMap[raw] ?? raw
|
|
29
21
|
}
|
|
@@ -43,15 +35,19 @@ export function applyRestAttrs(
|
|
|
43
35
|
): void {
|
|
44
36
|
const exclude = new Set(excludeKeys)
|
|
45
37
|
|
|
46
|
-
//
|
|
38
|
+
// Precompute classifications once — keys are stable for rest props.
|
|
39
|
+
const classified: Array<{ key: string; c: DOMPropClassification }> = []
|
|
47
40
|
for (const key of Object.keys(source)) {
|
|
48
41
|
if (exclude.has(key)) continue
|
|
49
|
-
|
|
42
|
+
classified.push({ key, c: classifyDOMProp(key) })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Wire up event handlers and ref callbacks once (not reactively)
|
|
46
|
+
for (const { key, c } of classified) {
|
|
47
|
+
if (c.kind === 'ref') {
|
|
50
48
|
const ref = source[key]
|
|
51
49
|
if (typeof ref === 'function') (ref as (el: Element) => void)(el)
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
if (key.startsWith('on') && key.length > 2 && key[2] === key[2].toUpperCase()) {
|
|
50
|
+
} else if (c.kind === 'event') {
|
|
55
51
|
const handler = source[key]
|
|
56
52
|
if (typeof handler === 'function') {
|
|
57
53
|
el.addEventListener(toEventName(key), handler as EventListener)
|
|
@@ -59,49 +55,39 @@ export function applyRestAttrs(
|
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Event handlers and ref are wired up above, not as attributes
|
|
67
|
-
if (key === 'ref') continue
|
|
68
|
-
// `children` is a JSX construct rendered inside the element, never
|
|
69
|
-
// a DOM attribute. Without this exclusion, parent components that
|
|
70
|
-
// pass `children` through `{...props}` end up with
|
|
71
|
-
// `children="<p ...>...</p>"` written as a literal attribute on
|
|
72
|
-
// the wrapper div. The matching `spreadAttrs` (SSR-string) path
|
|
73
|
-
// already skips `children` for the same reason.
|
|
74
|
-
if (key === 'children') continue
|
|
75
|
-
if (key.startsWith('on') && key.length > 2 && key[2] === key[2].toUpperCase()) continue
|
|
58
|
+
// Filter to only attr-like entries for the reactive loop
|
|
59
|
+
const attrEntries = classified.filter(
|
|
60
|
+
({ c }) => c.kind !== 'ref' && c.kind !== 'event' && c.kind !== 'skip',
|
|
61
|
+
)
|
|
76
62
|
|
|
63
|
+
createEffect(() => {
|
|
64
|
+
for (const { key, c } of attrEntries) {
|
|
77
65
|
const value = source[key]
|
|
78
|
-
const attr = toAttrName(key)
|
|
79
66
|
|
|
80
67
|
if (value != null && value !== false) {
|
|
81
|
-
|
|
82
|
-
if (attr === 'value' && 'value' in el) {
|
|
68
|
+
if (c.kind === 'property' && c.attrName === 'value' && 'value' in el) {
|
|
83
69
|
const strVal = String(value)
|
|
84
70
|
if ((el as HTMLInputElement).value !== strVal) (el as HTMLInputElement).value = strVal
|
|
85
|
-
} else if (
|
|
71
|
+
} else if (c.kind === 'property' && c.attrName === 'checked' && 'checked' in el) {
|
|
86
72
|
(el as HTMLInputElement).checked = !!value
|
|
87
|
-
} else if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// both reach the DOM as a real CSS string instead of
|
|
91
|
-
// `[object Object]`. Mirrors the compiler's
|
|
92
|
-
// `setAttribute('style', styleToCss(...))` path used when the
|
|
93
|
-
// attribute is bound directly on a JSX element.
|
|
73
|
+
} else if (c.kind === 'boolean') {
|
|
74
|
+
el.setAttribute(c.attrName, '')
|
|
75
|
+
} else if (c.kind === 'style') {
|
|
94
76
|
const css = styleToCss(value)
|
|
95
77
|
if (css == null) el.removeAttribute('style')
|
|
96
78
|
else el.setAttribute('style', css)
|
|
97
79
|
} else {
|
|
98
|
-
el.setAttribute(
|
|
80
|
+
el.setAttribute(c.attrName, String(value))
|
|
99
81
|
}
|
|
100
82
|
} else {
|
|
101
|
-
if (
|
|
83
|
+
if (c.kind === 'property' && c.attrName === 'value' && 'value' in el) {
|
|
84
|
+
(el as HTMLInputElement).value = ''
|
|
85
|
+
} else if (c.kind === 'property' && c.attrName === 'checked' && 'checked' in el) {
|
|
102
86
|
(el as HTMLInputElement).checked = false
|
|
87
|
+
} else if (c.kind === 'boolean') {
|
|
88
|
+
el.removeAttribute(c.attrName)
|
|
103
89
|
} else {
|
|
104
|
-
el.removeAttribute(
|
|
90
|
+
el.removeAttribute(c.attrName)
|
|
105
91
|
}
|
|
106
92
|
}
|
|
107
93
|
}
|
|
@@ -6,83 +6,33 @@
|
|
|
6
6
|
* a static string for server/template rendering of computed local spreads.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { classifyDOMProp } from '@barefootjs/shared'
|
|
9
10
|
import { styleToCss } from './style'
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
-
* SVG attributes that are case-sensitive and MUST stay in camelCase.
|
|
13
|
-
*
|
|
14
|
-
* The default JSX-prop → HTML-attribute rewrite lower-cases camelCase
|
|
15
|
-
* (`fooBar` → `foo-bar`), which is correct for HTML attrs and for the
|
|
16
|
-
* CSS-style SVG presentation attrs (`strokeWidth` → `stroke-width`).
|
|
17
|
-
* The XML-namespaced SVG attrs below, though, are case-sensitive in
|
|
18
|
-
* the spec: `viewBox` lower-cased to `view-box` makes the browser
|
|
19
|
-
* treat it as an unknown user attribute and the SVG no longer renders
|
|
20
|
-
* (pointer events stop hitting the inner geometry — surfaced as the
|
|
21
|
-
* Form Builder e2e regression in #1244's merge-emit follow-up).
|
|
22
|
-
*
|
|
23
|
-
* Coordinates with the compile-time `SVG_CAMEL_TO_KEBAB` table in
|
|
24
|
-
* `packages/jsx/src/ir-to-client-js/utils.ts`: presentation attrs
|
|
25
|
-
* (`clipPath`, `strokeWidth`, …) live there and must NOT appear here,
|
|
26
|
-
* or the same JSX prop would lower to `clip-path` via the explicit-
|
|
27
|
-
* attr path and stay `clipPath` via the spread path — a silent
|
|
28
|
-
* divergence. The list below is XML attribute names that have no
|
|
29
|
-
* kebab-case mirror (`viewBox`, `clipPathUnits`, …).
|
|
30
|
-
*
|
|
31
|
-
* The list mirrors React DOM's `DOMProperty` case-preserving entries
|
|
32
|
-
* (only the attributes that appear on actual SVG elements; ARIA and
|
|
33
|
-
* XLink namespaces are unrelated and handled by their `aria-*` /
|
|
34
|
-
* `xlink:*` literal prefix).
|
|
35
|
-
*/
|
|
36
|
-
const SVG_CAMEL_CASE_ATTRS: ReadonlySet<string> = new Set([
|
|
37
|
-
'allowReorder', 'attributeName', 'attributeType', 'autoReverse',
|
|
38
|
-
'baseFrequency', 'baseProfile', 'calcMode', 'clipPathUnits',
|
|
39
|
-
'contentScriptType', 'contentStyleType', 'diffuseConstant', 'edgeMode',
|
|
40
|
-
'externalResourcesRequired', 'filterRes', 'filterUnits', 'glyphRef',
|
|
41
|
-
'gradientTransform', 'gradientUnits', 'kernelMatrix', 'kernelUnitLength',
|
|
42
|
-
'keyPoints', 'keySplines', 'keyTimes', 'lengthAdjust', 'limitingConeAngle',
|
|
43
|
-
'markerHeight', 'markerUnits', 'markerWidth', 'maskContentUnits',
|
|
44
|
-
'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits',
|
|
45
|
-
'patternTransform', 'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ',
|
|
46
|
-
'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits', 'refX', 'refY',
|
|
47
|
-
'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
|
|
48
|
-
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset',
|
|
49
|
-
'stdDeviation', 'stitchTiles', 'surfaceScale', 'systemLanguage',
|
|
50
|
-
'tableValues', 'targetX', 'targetY', 'textLength', 'viewBox', 'viewTarget',
|
|
51
|
-
'xChannelSelector', 'yChannelSelector', 'zoomAndPan',
|
|
52
|
-
])
|
|
53
|
-
|
|
54
12
|
/**
|
|
55
13
|
* Convert an object to an HTML attribute string.
|
|
56
|
-
*
|
|
57
|
-
* event handlers,
|
|
58
|
-
* prop is routed through `styleToCss` so object
|
|
59
|
-
* a real CSS string
|
|
60
|
-
*
|
|
61
|
-
* SVG attributes listed in `SVG_CAMEL_CASE_ATTRS` are preserved
|
|
62
|
-
* verbatim — the SVG XML spec is case-sensitive for those names.
|
|
14
|
+
* Uses the shared classifyDOMProp classifier to determine how each prop
|
|
15
|
+
* maps to the DOM. Skips null/undefined/false, event handlers, ref, and
|
|
16
|
+
* children. The `style` prop is routed through `styleToCss` so object
|
|
17
|
+
* literals serialize to a real CSS string.
|
|
63
18
|
*/
|
|
64
19
|
export function spreadAttrs(obj: Record<string, unknown>): string {
|
|
65
20
|
if (!obj || typeof obj !== 'object') return ''
|
|
66
21
|
const parts: string[] = []
|
|
67
22
|
for (const [key, value] of Object.entries(obj)) {
|
|
68
23
|
if (value == null || value === false) continue
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
if (key === 'children') continue
|
|
73
|
-
if (key === 'style') {
|
|
24
|
+
const c = classifyDOMProp(key)
|
|
25
|
+
if (c.kind === 'event' || c.kind === 'skip' || c.kind === 'ref') continue
|
|
26
|
+
if (c.kind === 'style') {
|
|
74
27
|
const css = styleToCss(value)
|
|
75
28
|
if (css != null) parts.push(`style="${css}"`)
|
|
76
29
|
continue
|
|
77
30
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
: SVG_CAMEL_CASE_ATTRS.has(key) ? key
|
|
84
|
-
: key.replace(/([A-Z])/g, '-$1').toLowerCase()
|
|
85
|
-
parts.push(value === true ? attr : `${attr}="${value}"`)
|
|
31
|
+
if (c.kind === 'boolean' && value === true) {
|
|
32
|
+
parts.push(c.attrName)
|
|
33
|
+
} else {
|
|
34
|
+
parts.push(`${c.attrName}="${value}"`)
|
|
35
|
+
}
|
|
86
36
|
}
|
|
87
37
|
return parts.join(' ')
|
|
88
38
|
}
|