22import {
33 Cell ,
44 cell ,
5- createCell ,
5+ Default ,
66 derive ,
77 h ,
88 handler ,
@@ -11,59 +11,36 @@ import {
1111 NAME ,
1212 navigateTo ,
1313 recipe ,
14+ toSchema ,
1415 UI ,
1516} from "commontools" ;
1617
17- // the simple charm (to which we'll store references within a cell)
18- const SimpleRecipe = recipe ( "Simple Recipe" , ( ) => ( {
19- [ NAME ] : "Some Simple Recipe" ,
20- [ UI ] : < div > Some Simple Recipe </ div > ,
21- } ) ) ;
18+ type Charm = {
19+ [ NAME ] : string ;
20+ [ UI ] : string ;
21+ [ key : string ] : any ;
22+ } ;
2223
23- // Create a cell to store an array of charms
24- const createCellRef = lift (
25- {
26- type : "object" ,
27- properties : {
28- isInitialized : { type : "boolean" , default : false , asCell : true } ,
29- storedCellRef : { type : "object" , asCell : true } ,
30- } ,
31- } ,
32- undefined ,
33- ( { isInitialized, storedCellRef } ) => {
34- if ( ! isInitialized . get ( ) ) {
35- console . log ( "Creating cellRef - first time" ) ;
36- const newCellRef = createCell ( undefined , "charmsArray" ) ;
37- newCellRef . set ( [ ] ) ;
38- storedCellRef . set ( newCellRef ) ;
39- isInitialized . set ( true ) ;
40- return {
41- cellRef : newCellRef ,
42- } ;
43- } else {
44- console . log ( "cellRef already initialized" ) ;
45- }
46- // If already initialized, return the stored cellRef
47- return {
48- cellRef : storedCellRef ,
49- } ;
50- } ,
51- ) ;
24+ // Define interfaces for type safety
25+ interface AddCharmState {
26+ charm : any ;
27+ cellRef : Cell < Charm [ ] > ;
28+ isInitialized : Cell < boolean > ;
29+ }
30+ const AddCharmSchema = toSchema < AddCharmState > ( ) ;
5231
53- // Add a charm to the array and navigate to it
54- // we get a new isInitialized passed in for each
55- // charm we add to the list. this makes sure
56- // we only try to add the charm once to the list
57- // and we only call navigateTo once
32+ // Simple charm that will be instantiated multiple times
33+ const SimpleRecipe = recipe < { id : string } > ( "Simple Recipe" , ( { id } ) => ( {
34+ [ NAME ] : derive ( id , ( idValue ) => `SimpleRecipe: ${ idValue } ` ) ,
35+ [ UI ] : < div > Simple Recipe id { id } </ div > ,
36+ } ) ) ;
37+
38+ // Lift that adds a charm to the array and navigates to it.
39+ // The isInitialized flag prevents duplicate additions:
40+ // - Without it: lift runs → adds to array → array changes → lift runs again → duplicate
41+ // - With it: lift runs once → sets isInitialized → subsequent runs skip
5842const addCharmAndNavigate = lift (
59- {
60- type : "object" ,
61- properties : {
62- charm : { type : "object" } ,
63- cellRef : { type : "array" , asCell : true } ,
64- isInitialized : { type : "boolean" , asCell : true } ,
65- } ,
66- } ,
43+ AddCharmSchema ,
6744 undefined ,
6845 ( { charm, cellRef, isInitialized } ) => {
6946 if ( ! isInitialized . get ( ) ) {
@@ -79,68 +56,74 @@ const addCharmAndNavigate = lift(
7956 } ,
8057) ;
8158
82- // Create a new SimpleRecipe and add it to the array
83- const createSimpleRecipe = handler < unknown , { cellRef : Cell < any [ ] > } > (
59+ // Handler that creates a new charm instance and adds it to the array.
60+ // Each invocation creates its own isInitialized cell for tracking.
61+ const createSimpleRecipe = handler < unknown , { cellRef : Cell < Charm [ ] > } > (
8462 ( _ , { cellRef } ) => {
8563 // Create isInitialized cell for this charm addition
8664 const isInitialized = cell ( false ) ;
8765
88- // Create the charm
89- const charm = SimpleRecipe ( { } ) ;
66+ // Create a random 5-digit ID
67+ const randomId = Math . floor ( 10000 + Math . random ( ) * 90000 ) . toString ( ) ;
68+
69+ // Create the charm with unique ID
70+ const charm = SimpleRecipe ( { id : randomId } ) ;
9071
9172 // Store the charm in the array and navigate
9273 return addCharmAndNavigate ( { charm, cellRef, isInitialized } ) ;
9374 } ,
9475) ;
9576
9677// Handler to navigate to a specific charm from the list
97- const goToCharm = handler < unknown , { charm : any } > (
78+ const goToCharm = handler < unknown , { charm : Charm } > (
9879 ( _ , { charm } ) => {
9980 console . log ( "goToCharm clicked" ) ;
10081 return navigateTo ( charm ) ;
10182 } ,
10283) ;
10384
104- // create the named cell inside the recipe body, so we do it just once
105- export default recipe ( "Charms Launcher" , ( ) => {
106- // cell to store array of charms we created
107- const { cellRef } = createCellRef ( {
108- isInitialized : cell ( false ) ,
109- storedCellRef : cell ( ) ,
110- } ) ;
85+ // Recipe input/output type
86+ type RecipeInOutput = {
87+ cellRef : Default < Charm [ ] , [ ] > ;
88+ } ;
11189
112- return {
113- [ NAME ] : "Charms Launcher" ,
114- [ UI ] : (
115- < div >
116- < h3 > Stored Charms:</ h3 >
117- { ifElse (
118- ! cellRef ?. length ,
119- < div > No charms created yet</ div > ,
120- < ul >
121- { cellRef . map ( ( charm : any , index : number ) => (
122- < li >
123- < ct-button
124- onClick = { goToCharm ( { charm } ) }
125- >
126- Go to Charm { derive ( index , ( i ) => i + 1 ) }
127- </ ct-button >
128- < span >
129- Charm { derive ( index , ( i ) => i + 1 ) } :{ " " }
130- { charm [ NAME ] || "Unnamed" }
131- </ span >
132- </ li >
133- ) ) }
134- </ ul > ,
135- ) }
90+ // Main recipe that manages an array of charm references
91+ export default recipe < RecipeInOutput , RecipeInOutput > (
92+ "Charms Launcher" ,
93+ ( { cellRef } ) => {
94+ return {
95+ [ NAME ] : "Charms Launcher" ,
96+ [ UI ] : (
97+ < div >
98+ < h3 > Stored Charms:</ h3 >
99+ { ifElse (
100+ ! cellRef ?. length ,
101+ < div > No charms created yet</ div > ,
102+ < ul >
103+ { cellRef . map ( ( charm : any , index : number ) => (
104+ < li >
105+ < ct-button
106+ onClick = { goToCharm ( { charm } ) }
107+ >
108+ Go to Charm { derive ( index , ( i ) => i + 1 ) }
109+ </ ct-button >
110+ < span >
111+ Charm { derive ( index , ( i ) => i + 1 ) } :{ " " }
112+ { charm [ NAME ] || "Unnamed" }
113+ </ span >
114+ </ li >
115+ ) ) }
116+ </ ul > ,
117+ ) }
136118
137- < ct-button
138- onClick = { createSimpleRecipe ( { cellRef } ) }
139- >
140- Create New Charm
141- </ ct-button >
142- </ div >
143- ) ,
144- cellRef,
145- } ;
146- } ) ;
119+ < ct-button
120+ onClick = { createSimpleRecipe ( { cellRef } ) }
121+ >
122+ Create New Charm
123+ </ ct-button >
124+ </ div >
125+ ) ,
126+ cellRef,
127+ } ;
128+ } ,
129+ ) ;
0 commit comments