1- import type { RuntimeStoryboard } from "@next-core/types" ;
1+ import type {
2+ BrickConf ,
3+ BrickEventHandler ,
4+ BrickEventsMap ,
5+ BuiltinBrickEventHandler ,
6+ RouteConf ,
7+ RouteConfOfBricks ,
8+ RuntimeStoryboard ,
9+ } from "@next-core/types" ;
10+ import { isEvaluable } from "@next-core/cook" ;
11+ import { uniqueId } from "lodash" ;
212import { hooks } from "./Runtime.js" ;
313import { registerAppI18n } from "./registerAppI18n.js" ;
14+ import { isBuiltinHandler } from "./bindListeners.js" ;
415
516export async function fulfilStoryboard ( storyboard : RuntimeStoryboard ) {
617 if ( storyboard . $$fulfilled ) {
@@ -15,8 +26,196 @@ export async function fulfilStoryboard(storyboard: RuntimeStoryboard) {
1526async function doFulfilStoryboard ( storyboard : RuntimeStoryboard ) {
1627 await hooks ?. fulfilStoryboard ?.( storyboard ) ;
1728 registerAppI18n ( storyboard ) ;
29+ initializeSeguesForRoutes ( storyboard . routes ) ;
1830 Object . assign ( storyboard , {
1931 $$fulfilled : true ,
2032 $$fulfilling : null ,
2133 } ) ;
2234}
35+
36+ interface SegueConf {
37+ by : "drawer" | "modal" ;
38+ route ?: {
39+ path : string ;
40+ params : SegueRouteParam [ ] ;
41+ } ;
42+ eventsMapping ?: Record < string , string > ;
43+ events ?: BrickEventsMap ;
44+ }
45+
46+ interface SegueRouteParam {
47+ key : string ;
48+ value : string ;
49+ }
50+
51+ function initializeSeguesForRoutes ( routes : RouteConf [ ] ) {
52+ for ( const route of routes ) {
53+ if ( route . type !== "redirect" && route . type !== "routes" ) {
54+ initializeSeguesForBricks ( route . bricks , route ) ;
55+ }
56+ }
57+ }
58+
59+ function initializeSeguesForBricks (
60+ bricks : BrickConf [ ] ,
61+ routeParent : RouteConfOfBricks
62+ ) {
63+ for ( const brick of bricks ) {
64+ if ( brick . events ) {
65+ for ( const [ eventType , handlers ] of Object . entries ( brick . events ) ) {
66+ if ( Array . isArray ( handlers ) ) {
67+ handlers . forEach ( ( handler , index ) => {
68+ if ( isBuiltinHandler ( handler ) && handler . action === "segue.go" ) {
69+ replaceSegues ( handler , handlers , index , routeParent ) ;
70+ }
71+ } ) ;
72+ } else if (
73+ isBuiltinHandler ( handlers ) &&
74+ handlers . action === "segue.go"
75+ ) {
76+ replaceSegues ( handlers , brick . events , eventType , routeParent ) ;
77+ }
78+ }
79+ }
80+
81+ if ( brick . slots ) {
82+ for ( const slotConf of Object . values ( brick . slots ) ) {
83+ if ( slotConf . type === "routes" ) {
84+ initializeSeguesForRoutes ( slotConf . routes ) ;
85+ } else {
86+ initializeSeguesForBricks ( slotConf . bricks , routeParent ) ;
87+ }
88+ }
89+ } else if ( Array . isArray ( brick . children ) ) {
90+ initializeSeguesForBricks ( brick . children , routeParent ) ;
91+ }
92+ }
93+ }
94+
95+ function replaceSegues (
96+ handler : BuiltinBrickEventHandler ,
97+ handlers : BrickEventsMap | BrickEventHandler [ ] ,
98+ key : string | number ,
99+ routeParent : RouteConfOfBricks
100+ ) {
101+ let segueConf : SegueConf | undefined ;
102+ let segueTarget : string | undefined ;
103+ if (
104+ Array . isArray ( handler . args ) &&
105+ ( ( segueTarget = handler . args [ 0 ] as string ) ,
106+ typeof segueTarget === "string" ) &&
107+ ( segueConf = handler . args [ 1 ] as SegueConf )
108+ ) {
109+ switch ( segueConf . by ) {
110+ case "drawer" : {
111+ if ( segueConf . route ) {
112+ const { params, path } = segueConf . route ;
113+ const targetUrlExpr = path . replace ( / : ( \w + ) / g, ( _ , key ) => {
114+ const param = params . find ( ( param ) => param . key === key ) ;
115+ return param
116+ ? typeof param . value === "string" && isEvaluable ( param . value )
117+ ? `\${${ param . value . replace ( / ^ \s * < % [ ~ = ] ? \s | \s % > \s * $ / g, "" ) } }`
118+ : String ( param . value ) . replace ( / ` / g, "\\`" )
119+ : `\${PATH.${ key } }` ;
120+ } ) ;
121+ ( handlers as BrickEventsMap ) [ key ] = {
122+ action : "history.push" ,
123+ args : [ `<% \`${ targetUrlExpr } \` %>` ] ,
124+ } ;
125+ const drawerId = uniqueId ( "internal-segue-drawer-" ) ;
126+ const drawerTarget = `#${ drawerId } ` ;
127+ routeParent . bricks . push ( {
128+ brick : "eo-drawer" ,
129+ portal : true ,
130+ properties : {
131+ id : drawerId ,
132+ customTitle : "Detail" ,
133+ } ,
134+ events : {
135+ close : {
136+ action : "history.push" ,
137+ args : [
138+ `<% \`${ routeParent . path . replace ( / : ( \w ) + / g, "${PATH.$1}" ) } \` %>` ,
139+ ] ,
140+ } ,
141+ } ,
142+ slots : {
143+ "" : {
144+ type : "routes" ,
145+ routes : [
146+ {
147+ path,
148+ exact : true ,
149+ bricks : [
150+ {
151+ brick : segueTarget ,
152+ properties : Object . fromEntries (
153+ params . map ( ( param ) => [
154+ param . key ,
155+ `<% PATH.${ param . key } %>` ,
156+ ] )
157+ ) ,
158+ lifeCycle : {
159+ onMount : {
160+ target : drawerTarget ,
161+ method : "open" ,
162+ } ,
163+ onUnmount : {
164+ target : drawerTarget ,
165+ method : "close" ,
166+ } ,
167+ } ,
168+ } ,
169+ ] ,
170+ } ,
171+ ] ,
172+ } ,
173+ } ,
174+ } ) ;
175+ }
176+ break ;
177+ }
178+
179+ case "modal" : {
180+ const modalId = uniqueId ( "internal-segue-modal-" ) ;
181+ const modalTarget = `#${ modalId } ` ;
182+ const sceneId = uniqueId ( "internal-segue-scene-" ) ;
183+ const sceneTarget = `#${ sceneId } ` ;
184+ ( handlers as BrickEventsMap ) [ key ] = {
185+ target : modalTarget ,
186+ method : "open" ,
187+ } ;
188+ routeParent . bricks . push ( {
189+ brick : "eo-modal" ,
190+ portal : true ,
191+ properties : {
192+ id : modalId ,
193+ modalTitle : "Create" ,
194+ closeWhenConfirm : false ,
195+ } ,
196+ events : segueConf . eventsMapping
197+ ? Object . fromEntries (
198+ Object . entries ( segueConf . eventsMapping ) . map ( ( [ from , to ] ) => [
199+ from ,
200+ {
201+ target : sceneTarget ,
202+ method : to ,
203+ } ,
204+ ] )
205+ )
206+ : undefined ,
207+ children : [
208+ {
209+ brick : segueTarget ,
210+ properties : {
211+ id : sceneId ,
212+ } ,
213+ events : segueConf . events ,
214+ } ,
215+ ] ,
216+ } ) ;
217+ break ;
218+ }
219+ }
220+ }
221+ }
0 commit comments