1
1
// Define types for JSX elements and children
2
2
type Signal < T > = {
3
- subscribe : ( callback : ( value : T ) => void ) => void ;
3
+ subscribe : ( callback : ( newValue : T , oldValue : T ) => void ) => void ;
4
4
value : T ;
5
5
} ;
6
6
type JSXChild =
@@ -18,17 +18,55 @@ type Component = (props: any) => JSXElement | Signal<any>;
18
18
function renderValueBasedOnType (
19
19
parent : HTMLElement | DocumentFragment ,
20
20
type : string ,
21
- value : any ,
21
+ newValue : any ,
22
+ oldValue : any ,
22
23
) {
23
24
// TODO: render based on value type being a DOM element
24
25
switch ( type ) {
25
26
case "number" :
26
27
case "string" :
27
28
case "boolean" :
28
- parent . appendChild ( document . createTextNode ( String ( value ) ) ) ;
29
+ const oldValueString = String ( oldValue ) ;
30
+ const newValueString = String ( newValue ) ;
31
+ const textNode = document . createTextNode ( newValueString ) ;
32
+ if ( parent && ! parent . firstChild ) {
33
+ parent . appendChild ( textNode ) ;
34
+ return ;
35
+ }
36
+ let replaced = false ;
37
+ Array . from ( parent . childNodes ) . forEach ( ( child ) => {
38
+ if ( child . textContent === oldValueString ) {
39
+ // console.log("replaced", oldValueString, newValueString);
40
+ parent . replaceChild ( textNode , child ) ;
41
+ replaced = true ;
42
+ }
43
+ } ) ;
44
+ if ( ! replaced ) {
45
+ // console.log("appendChild", newValueString);
46
+ parent . appendChild ( textNode ) ;
47
+ }
29
48
break ;
49
+ case "function" :
50
+ // handle iif
51
+ console . log ( "renderValueBasedOnType function" , newValue ) ;
52
+ const result = newValue ( ) ;
53
+ return renderValueBasedOnType ( parent , typeof result , result , oldValue ) ;
30
54
default :
31
- parent . appendChild ( value ) ;
55
+ if ( parent . firstElementChild === oldValue && parent . firstElementChild ) {
56
+ console . log ( "replace child" , newValue , oldValue ) ;
57
+ parent . replaceChild ( newValue , parent . firstElementChild ) ;
58
+ } else if ( parent . firstChild === oldValue && parent . firstChild ) {
59
+ console . log ( "replace child" , newValue , oldValue ) ;
60
+ parent . replaceChild ( newValue , parent . firstChild ) ;
61
+ } else if ( Array . isArray ( newValue ) ) {
62
+ console . log ( "appendChild array" , newValue ) ;
63
+ for ( const child of newValue ) {
64
+ appendChild ( parent , child ) ;
65
+ }
66
+ } else {
67
+ console . log ( "appendChild" , newValue ) ;
68
+ parent . appendChild ( newValue ) ;
69
+ }
32
70
}
33
71
}
34
72
@@ -49,11 +87,15 @@ export function jsx(
49
87
// Handle case where component returns a signal
50
88
if ( ( result as Signal < any > ) ?. subscribe ) {
51
89
const signal = result as Signal < any > ;
52
- const placeholder = document . createTextNode ( String ( signal . value ) ) ;
90
+ const value = signal . value ;
91
+ // const innerParent = document.createDocumentFragment();
92
+ const parent = document . createElement ( "div" ) ;
93
+ renderValueBasedOnType ( parent , typeof value , value , null ) ;
53
94
signal . subscribe ( ( newValue : any ) => {
54
- placeholder . textContent = String ( newValue ) ;
95
+ console . log ( "jsx.signal.subscribe" , newValue ) ;
96
+ renderValueBasedOnType ( parent , typeof newValue , newValue , value ) ;
55
97
} ) ;
56
- return placeholder as unknown as JSXElement ;
98
+ return parent as unknown as JSXElement ;
57
99
}
58
100
return result as JSXElement ;
59
101
}
@@ -114,10 +156,10 @@ function appendChild(
114
156
if ( value === undefined || value === null ) {
115
157
value = "" ;
116
158
}
117
- signal . subscribe ( ( newValue : any ) => {
118
- renderValueBasedOnType ( parent , typeof newValue , newValue ) ;
159
+ signal . subscribe ( ( newValue : any , oldValue : any ) => {
160
+ renderValueBasedOnType ( parent , typeof newValue , newValue , oldValue ) ;
119
161
} ) ;
120
- renderValueBasedOnType ( parent , typeof value , value ) ;
162
+ renderValueBasedOnType ( parent , typeof value , value , null ) ;
121
163
return ;
122
164
}
123
165
0 commit comments