1+ import { h , Pointer } from "hstd" ;
2+
3+ const
4+ WC_TAG_VALIDATOR = / ^ [ a - z ] (?: [ a - z 0 - 9 ] | [ a - z 0 - 9 ] [ \- \. ] [ a - z 0 - 9 ] ) * - (?: [ a - z 0 - 9 ] | [ a - z 0 - 9 ] [ \- \. ] [ a - z 0 - 9 ] ) * $ / ,
5+ createAttrState = ( ) => {
6+ const body = { } ;
7+ return [
8+ body ,
9+ new Proxy ( { } , {
10+ get ( _ , prop ) {
11+ return body [ prop ] ||= Pointer ( "" ) ;
12+ }
13+ } )
14+ ]
15+ } ,
16+ ATTR_BUF = Symbol ( ) ,
17+ IS_CONNECTED = Symbol ( ) ,
18+ CONNECT = Symbol ( )
19+ ;
20+
21+ const define = ( componentDeclarations : { [ key : string ] : ( ...any ) => ( NodeList | Promise < NodeList > | AsyncGenerator < NodeList > ) } ) : void => Object . entries ( componentDeclarations ) . forEach ( ( [ name , component ] ) => {
22+
23+ if ( ! WC_TAG_VALIDATOR . test ( name ) ) {
24+ if ( WC_TAG_VALIDATOR . test ( `hstd-${ name } ` ) ) {
25+ name = `hstd-${ name } ` ;
26+ } else {
27+ throw new SyntaxError ( `Failed to execute 'define': '${ name } ' is not a valid custom element name` )
28+ }
29+ } ;
30+
31+ let isInit = true ;
32+
33+ const
34+ initAttrBuf = createAttrState ( ) ,
35+ initNodeListBuf = h `${ component ( initAttrBuf [ 1 ] ) } ` ,
36+ initAttrKeys = Object . keys ( initAttrBuf [ 0 ] ) ,
37+ initAttrList = initAttrKeys . reduce ( ( acc , attr ) => {
38+ if ( attr . includes ( ":" ) ) throw new Error ( "Cannot include property that starts with ':', which is reserved for binding type identifier." ) ;
39+ acc . push ( attr , `${ attr } :number` , `${ attr } :boolean` ) ;
40+ return acc ;
41+ } , [ ] ) ;
42+ ;
43+
44+ if ( customElements . get ( name ) ) {
45+
46+ let altCount = - 1 ;
47+ while ( customElements . get ( `${ name } -${ ( ++ altCount ) . toString ( 36 ) } ` ) ) ;
48+ name += `-${ altCount . toString ( 36 ) } ` ;
49+ } ;
50+
51+ customElements . define ( name , class extends HTMLElement {
52+
53+ static get observedAttributes ( ) {
54+ return initAttrList ;
55+ }
56+
57+ constructor ( ) {
58+ super ( ) ;
59+ const thisRef = this ;
60+ const [ attrPtrList ] = thisRef [ ATTR_BUF ] = isInit ? ( isInit = false , initAttrBuf ) : createAttrState ( ) ;
61+ thisRef [ IS_CONNECTED ] = new Promise ( r => thisRef [ CONNECT ] = r ) ;
62+ Object . defineProperties ( thisRef , initAttrKeys . reduce ( ( acc , key ) => {
63+ const ptr = attrPtrList [ name ] ;
64+ acc [ key ] = {
65+ get ( ) {
66+ return ptr . $ ;
67+ } ,
68+ set ( newValue ) {
69+ return ptr . $ = newValue ;
70+ } ,
71+ configurable : false ,
72+ writable : false ,
73+ }
74+ return acc ;
75+ } , { } ) )
76+ }
77+
78+ async attributeChangedCallback ( name , _ , newValue ) {
79+ await this [ IS_CONNECTED ] ;
80+
81+ const temp = (
82+ name . endsWith ( ":number" ) ? Number
83+ : name . endsWith ( ":boolean" ) ? Boolean
84+ : String
85+ ) ;
86+
87+ this . removeAttribute ( name ) ;
88+
89+ name = name . includes ( ":" ) ? name . slice ( 0 , name . indexOf ( ":" ) ) : name ;
90+
91+ this . setAttribute ( name , newValue )
92+
93+ console . log ( this [ ATTR_BUF ] [ 0 ] [ name ] . $ = temp ( newValue ) ) ;
94+ }
95+
96+ connectedCallback ( ) {
97+ this . attachShadow ( { mode : "open" } ) ;
98+ this . shadowRoot . append ( ...( isInit
99+ ? initNodeListBuf
100+ : h `${ component ( this [ ATTR_BUF ] [ 1 ] ) } `
101+ ) ) ;
102+ this [ CONNECT ] ( ) ;
103+ }
104+
105+ } )
106+ } ) ;
107+
108+ export { define }
0 commit comments