66 lines
2 KiB
Go
66 lines
2 KiB
Go
// SPDX-Licence-Identifier: EUPL-1.2
|
|
package main
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var canonicalSlotOrder = []string{"H", "L", "C", "R", "F"}
|
|
|
|
// 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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// tagToClassName converts a kebab-case tag into a PascalCase class name.
|
|
// Example: nav-bar -> 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()
|
|
}
|
|
|
|
// jsStringLiteral returns a quoted JavaScript string literal.
|
|
func jsStringLiteral(s string) string {
|
|
return strconv.Quote(s)
|
|
}
|
|
|
|
// 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 {" +
|
|
"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}}));}" +
|
|
"render(html){const tpl=document.createElement(\"template\");tpl.insertAdjacentHTML(\"afterbegin\",html);" +
|
|
"this._shadow.textContent=\"\";this._shadow.appendChild(tpl.content.cloneNode(true));}" +
|
|
"}"
|
|
}
|