Skip to content

Commit 273f072

Browse files
committed
feat(svg): implement svg handling
1 parent 1485526 commit 273f072

File tree

4 files changed

+72
-10
lines changed

4 files changed

+72
-10
lines changed

src/babel-plugin-transform-jsx.cjs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ module.exports = function (babel) {
4242
},
4343
JSXElement(path, state) {
4444
const openingElement = path.get("openingElement");
45-
const typeValue = openingElement.node.name.name;
46-
const type = t.react.isCompatTag(typeValue)
47-
? t.stringLiteral(typeValue)
48-
: t.identifier(typeValue);
45+
46+
const typeValue = openingElement.node.name;
47+
const type = t.isJSXNamespacedName(typeValue)
48+
? t.stringLiteral(`${typeValue.namespace.name}:${typeValue.name.name}`)
49+
: t.react.isCompatTag(typeValue.name)
50+
? t.stringLiteral(typeValue.name)
51+
: t.identifier(typeValue.name);
4952
const children = t.react
5053
.buildChildren(path.node)
5154
.map((child) => t.arrowFunctionExpression([], child));

src/reconciler/host.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,25 @@ export const hostReconcile: Reconciler = (opt) => {
4040
// create new element
4141
const element = untracked(() => {
4242
const shadowElement = opt.shadowElement as ShadowHostElement;
43-
return typeof shadowElement.type === "string"
44-
? document.createElement(shadowElement.type)
45-
: new shadowElement.type();
43+
44+
if (typeof shadowElement.type === "string") {
45+
const elementParts = shadowElement.type.split(":");
46+
if (elementParts.length === 1) {
47+
return document.createElement(shadowElement.type);
48+
} else if (elementParts.length === 2) {
49+
if (elementParts[0] === "svg") {
50+
return document.createElementNS(
51+
"http://www.w3.org/2000/svg",
52+
elementParts[1],
53+
);
54+
} else {
55+
throw new Error(`Unsupported namespace: ${elementParts[0]}`);
56+
}
57+
} else {
58+
throw new Error("element cant have several namespaces");
59+
}
60+
}
61+
return new shadowElement.type();
4662
});
4763

4864
opt.shadowCache.node = element;

src/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ export namespace JSX {
8585
onplusneweventasync?: (evt: PlusnewAsyncEvent) => void;
8686
};
8787
} & {
88-
[Tag in keyof SVGElementTagNameMap]: IntrinsicElementAttributes<
89-
SVGElementTagNameMap[Tag]
90-
> & {
88+
[Tag in keyof SVGElementTagNameMap as Tag extends "svg"
89+
? Tag
90+
: `svg:${Tag}`]: IntrinsicElementAttributes<SVGElementTagNameMap[Tag]> & {
9191
children?: ShadowElement;
9292
onplusnewerror?: (evt: PlusnewErrorEvent) => void;
9393
onplusneweventasync?: (evt: PlusnewAsyncEvent) => void;

test/svg.test.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { expect } from "@esm-bundle/chai";
2+
import { createComponent, mount } from "@plusnew/webcomponent";
3+
4+
describe("svg", () => {
5+
let container: HTMLElement;
6+
7+
beforeEach(() => {
8+
container = document.createElement("div");
9+
document.body.appendChild(container);
10+
});
11+
12+
afterEach(() => {
13+
container.remove();
14+
});
15+
16+
it("async event dispatch", async () => {
17+
const Component = createComponent(
18+
"test-svg",
19+
class Component extends HTMLElement {
20+
render(this: Component) {
21+
return (
22+
<svg>
23+
<svg:circle />
24+
</svg>
25+
);
26+
}
27+
},
28+
);
29+
30+
mount(container, <Component />);
31+
32+
expect(container.childNodes.length).to.equal(1);
33+
34+
const component = container.childNodes[0] as HTMLElement;
35+
const element = component.shadowRoot?.childNodes[0] as SVGElement;
36+
37+
expect(element.tagName).to.eql("SVG");
38+
expect((element.childNodes[0] as Element).tagName).to.eql("circle");
39+
expect((element.childNodes[0] as Element).namespaceURI).to.eql(
40+
"http://www.w3.org/2000/svg",
41+
);
42+
});
43+
});

0 commit comments

Comments
 (0)