Skip to content

feat(): support segues #4510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: v3
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/runtime/src/internal/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ async function legacyRenderBrick(
dataSource: brickIf,
// `permissionsPreCheck` maybe required before computing `if`.
permissionsPreCheck,
iid: brickConf.iid,
slots: {
"": {
type: "bricks",
Expand Down
216 changes: 215 additions & 1 deletion packages/runtime/src/internal/fulfilStoryboard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import type { RuntimeStoryboard } from "@next-core/types";
import type {
BrickConf,
BrickEventHandler,
BrickEventsMap,
BuiltinBrickEventHandler,
RouteConfOfBricks,
RuntimeStoryboard,
Storyboard,
} from "@next-core/types";
import { isEvaluable } from "@next-core/cook";
import { hasOwnProperty } from "@next-core/utils/general";
import {
parseStoryboard,
traverse,
parseEvents,
type StoryboardNodeEvent,
type StoryboardNodeRoute,
} from "@next-core/utils/storyboard";
import { uniqueId } from "lodash";
import { hooks } from "./Runtime.js";
import { registerAppI18n } from "./registerAppI18n.js";
import { isBuiltinHandler, isCustomHandler } from "./bindListeners.js";

export async function fulfilStoryboard(storyboard: RuntimeStoryboard) {
if (storyboard.$$fulfilled) {
Expand All @@ -15,8 +34,203 @@
async function doFulfilStoryboard(storyboard: RuntimeStoryboard) {
await hooks?.fulfilStoryboard?.(storyboard);
registerAppI18n(storyboard);
// initializeSeguesForRoutes(storyboard.routes);
initializeSegues(storyboard);
Object.assign(storyboard, {
$$fulfilled: true,
$$fulfilling: null,
});
}

type SceneConf = Pick<BrickConf, "properties" | "events">;

interface SegueConf {
type: "drawer" | "modal";
route?: {
path: string;
params: Record<string, string>;
};
modal?: SceneConf;
scene?: SceneConf;
}

function initializeSegues(storyboard: Storyboard) {
const ast = parseStoryboard(storyboard);

traverse(ast, (node, nodePath) => {
switch (node.type) {
case "EventHandler":
if (isBuiltinHandler(node.raw) && node.raw.action === "segue.go") {
const parent = nodePath[nodePath.length - 1] as StoryboardNodeEvent;
const routeParent = (

Check warning on line 65 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L64-L65

Added lines #L64 - L65 were not covered by tests
nodePath.findLast(
(node) => node.type === "Route" && node.raw.type === "bricks"
) as StoryboardNodeRoute
).raw as RouteConfOfBricks;
if (typeof node.rawKey === "number") {
replaceSegues(

Check warning on line 71 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L71

Added line #L71 was not covered by tests
node.raw,
parent.rawContainer[parent.rawKey] as BrickEventHandler[],
node.rawKey,
routeParent
);
} else {
replaceSegues(

Check warning on line 78 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L78

Added line #L78 was not covered by tests
node.raw,
parent.rawContainer,
parent.rawKey,
routeParent
);
}
}
break;
}
});
}

function replaceSegues(

Check warning on line 91 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L91

Added line #L91 was not covered by tests
handler: BuiltinBrickEventHandler,
handlers: BrickEventsMap | BrickEventHandler[],
key: string | number,
routeParent: RouteConfOfBricks
) {
let segueConf: SegueConf | undefined;
let segueTarget: string | undefined;
if (
Array.isArray(handler.args) &&
((segueTarget = handler.args[0] as string),
typeof segueTarget === "string") &&
(segueConf = handler.args[1] as SegueConf)
) {
switch (segueConf.type) {
case "drawer": {
if (segueConf.route) {
const { params, path } = segueConf.route;
const targetUrlExpr = path.replace(/:(\w+)/g, (_, k) => {
const hasParam = hasOwnProperty(params, k);

Check warning on line 110 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L108-L110

Added lines #L108 - L110 were not covered by tests
const paramValue = hasParam ? params[k] : undefined;
return hasParam
? typeof paramValue === "string" && isEvaluable(paramValue)
? `\${${paramValue.replace(/^\s*<%[~=]?\s|\s%>\s*$/g, "")}}`
: String(paramValue).replace(/[`\\]/g, "\\$&")
: `\${PATH.${k}}`;
});
(handlers as BrickEventsMap)[key] = {

Check warning on line 118 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L118

Added line #L118 was not covered by tests
action: "history.push",
args: [`<% \`${targetUrlExpr}\` %>`],
};
const drawerId = uniqueId("internal-segue-drawer-");
const drawerTarget = `#${drawerId}`;
routeParent.bricks.push({

Check warning on line 124 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L122-L124

Added lines #L122 - L124 were not covered by tests
brick: "eo-drawer",
iid: drawerId,
portal: true,
properties: {
id: drawerId,
customTitle: "Detail",
},
events: {
close: {
action: "history.push",
args: [
`<% \`${routeParent.path.replace(/:(\w+)/g, "${PATH.$1}")}\` %>`,
],
},
},
slots: {
"": {
type: "routes",
routes: [
{
path,
exact: true,
bricks: [
{
brick: segueTarget,
properties: Object.fromEntries(
Object.keys(params).map((k) => [k, `<% PATH.${k} %>`])

Check warning on line 151 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L151

Added line #L151 was not covered by tests
),
lifeCycle: {
onMount: {
target: drawerTarget,
method: "open",
},
onUnmount: {
target: drawerTarget,
method: "close",
},
},
},
],
},
],
},
},
});
}
break;

Check warning on line 171 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L171

Added line #L171 was not covered by tests
}

case "modal": {
const modalId = uniqueId("internal-segue-modal-");
const modalTarget = `#${modalId}`;
const sceneId = uniqueId("internal-segue-scene-");
const sceneTarget = `#${sceneId}`;
(handlers as BrickEventsMap)[key] = {

Check warning on line 179 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L175-L179

Added lines #L175 - L179 were not covered by tests
target: modalTarget,
method: "open",
};

const replacements = new Map<string, string>([

Check warning on line 184 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L184

Added line #L184 was not covered by tests
["_modal", modalTarget],
["_scene", sceneTarget],
]);
replaceSceneTarget(segueConf.modal?.events, replacements);
replaceSceneTarget(segueConf.scene?.events, replacements);

Check warning on line 189 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L188-L189

Added lines #L188 - L189 were not covered by tests

routeParent.bricks.push({

Check warning on line 191 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L191

Added line #L191 was not covered by tests
brick: "eo-modal",
iid: modalId,
portal: true,
properties: {
closeWhenConfirm: false,
...segueConf.modal?.properties,
id: modalId,
},
events: segueConf.modal?.events,
children: [
{
brick: segueTarget,
iid: sceneId,
properties: {
...segueConf.scene?.properties,
id: sceneId,
},
events: segueConf.scene?.events,
},
],
});
break;

Check warning on line 213 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L213

Added line #L213 was not covered by tests
}
}
}
}

function replaceSceneTarget(

Check warning on line 219 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L219

Added line #L219 was not covered by tests
events: BrickEventsMap | undefined,
replacements: Map<string, string>
) {
const ast = parseEvents(events);

Check warning on line 223 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L223

Added line #L223 was not covered by tests

traverse(ast, (node) => {

Check warning on line 225 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L225

Added line #L225 was not covered by tests
switch (node.type) {
case "EventHandler":
if (isCustomHandler(node.raw) && typeof node.raw.target === "string") {
const replacement = replacements.get(node.raw.target);

Check warning on line 229 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L229

Added line #L229 was not covered by tests
if (replacement !== undefined) {
node.raw.target = replacement;

Check warning on line 231 in packages/runtime/src/internal/fulfilStoryboard.ts

View check run for this annotation

Codecov / codecov/patch

packages/runtime/src/internal/fulfilStoryboard.ts#L231

Added line #L231 was not covered by tests
}
}
}
});
}
1 change: 1 addition & 0 deletions packages/types/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ export interface BuiltinBrickEventHandler {
| "history.unblock"

// Segues
| "segue.go"
// | "segue.push"
// | "segue.replace"

Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/storyboard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export {
/** @deprecated import it from "@next-core/utils/general" instead */
unwrapProvider,
};
export * from "./parser/index.js";
3 changes: 3 additions & 0 deletions packages/utils/src/storyboard/parser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { type ParseOptions, parseStoryboard, parseEvents } from "./parser.js";
export * from "./interfaces.js";
export * from "./traverse.js";
Loading
Loading