83 lines
2.5 KiB
Go
83 lines
2.5 KiB
Go
// SPDX-Licence-Identifier: EUPL-1.2
|
|
package main
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var canonicalSlotOrder = []string{"H", "L", "C", "R", "F"}
|
|
var reservedCustomElementTags = map[string]struct{}{
|
|
"annotation-xml": {},
|
|
"color-profile": {},
|
|
"font-face": {},
|
|
"font-face-src": {},
|
|
"font-face-uri": {},
|
|
"font-face-format": {},
|
|
"font-face-name": {},
|
|
"missing-glyph": {},
|
|
}
|
|
|
|
// cmd/wasm/components.go: isValidCustomElementTag reports whether tag is a safe
|
|
// custom element name.
|
|
// It mirrors the codegen package validation without importing the heavier
|
|
// template and logging dependencies into the WASM-linked path.
|
|
func isValidCustomElementTag(tag string) bool {
|
|
if tag == "" || !strings.Contains(tag, "-") {
|
|
return false
|
|
}
|
|
if tag[0] < 'a' || tag[0] > 'z' {
|
|
return false
|
|
}
|
|
if _, reserved := reservedCustomElementTags[tag]; reserved {
|
|
return false
|
|
}
|
|
|
|
for i := range len(tag) {
|
|
ch := tag[i]
|
|
switch {
|
|
case ch >= 'a' && ch <= 'z':
|
|
case ch >= '0' && ch <= '9':
|
|
case ch == '-' || ch == '.' || ch == '_':
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// cmd/wasm/components.go: tagToClassName converts a kebab-case tag into a
|
|
// PascalCase class name.
|
|
// Example: tagToClassName("nav-bar") returns NavBar.
|
|
func tagToClassName(tag string) string {
|
|
var b strings.Builder
|
|
for part := range strings.SplitSeq(tag, "-") {
|
|
if len(part) == 0 {
|
|
continue
|
|
}
|
|
b.WriteString(strings.ToUpper(part[:1]))
|
|
b.WriteString(part[1:])
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// cmd/wasm/components.go: jsStringLiteral returns a quoted JavaScript string literal.
|
|
func jsStringLiteral(s string) string {
|
|
return strconv.Quote(s)
|
|
}
|
|
|
|
// cmd/wasm/components.go: customElementClassSource returns a JavaScript class
|
|
// expression that mirrors the codegen bundle's closed-shadow custom element
|
|
// behaviour.
|
|
func customElementClassSource(tag, slot string) string {
|
|
className := tagToClassName(tag)
|
|
return "class " + className + " extends HTMLElement {" +
|
|
"#shadow;" +
|
|
"constructor(){super();this.#shadow=this.attachShadow({mode:\"closed\"});}" +
|
|
"connectedCallback(){this.#shadow.textContent=\"\";const slot=this.getAttribute(\"data-slot\")||" + jsStringLiteral(slot) + ";" +
|
|
"this.dispatchEvent(new CustomEvent(\"wc-ready\",{detail:{tag:" + jsStringLiteral(tag) + ",slot},bubbles:true,composed:true}));}" +
|
|
"render(html){const tpl=document.createElement(\"template\");tpl.insertAdjacentHTML(\"afterbegin\",html);" +
|
|
"this.#shadow.textContent=\"\";this.#shadow.appendChild(tpl.content.cloneNode(true));}" +
|
|
"}"
|
|
}
|