Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8faf5ad
feat(plugin-history-sync): Default history setup option for rich deep…
ENvironmentSet Jul 25, 2025
c5d841a
feat(plugin-history-sync): Let base history setup be cancelled when o…
ENvironmentSet Jul 25, 2025
92fc07d
fix(future): Remove reduant resume event
ENvironmentSet Jul 25, 2025
7c42db1
feat(core): Emit activity pop effect even if it was popped immediately
ENvironmentSet Jul 29, 2025
c1bf18a
chore: remove unused imports
ENvironmentSet Jul 30, 2025
98b9f9c
feat: Drop step support
Aug 11, 2025
103deba
fix
Aug 11, 2025
0d994c9
Revert "fix"
Aug 11, 2025
951f0aa
Revert "feat: Drop step support"
Aug 11, 2025
cab9ee8
support step
Aug 11, 2025
6f33fbb
feat: Lazy load activity component support
Aug 12, 2025
f96e2f0
polish interface
Aug 12, 2025
a357660
fix loading
Aug 12, 2025
e1166bf
fix
Aug 12, 2025
b2fd581
Revert "feat(core): Emit activity pop effect even if it was popped im…
Aug 13, 2025
0bf3f7c
fix type
Aug 13, 2025
0e4f70f
format
Aug 13, 2025
80f62d6
refactor
Aug 13, 2025
606a0e6
simpl
Aug 14, 2025
8b41197
add changesets
Aug 14, 2025
f1b42e5
demo update
Aug 14, 2025
31f162a
revert
Aug 14, 2025
ce419a3
prevent jaggering transition
Aug 14, 2025
bc778d3
fix
Aug 14, 2025
3336488
shell
Aug 17, 2025
24fcaa8
fix
Aug 18, 2025
4955f92
remove unused file
Aug 19, 2025
7640fbe
Revert "remove unused file"
Aug 19, 2025
e567ce6
structured activity
Aug 19, 2025
18fb62a
types
Aug 19, 2025
befc6cf
refactor
Aug 19, 2025
54ea05c
update demo
Aug 19, 2025
bd21f3b
update changesets
Aug 19, 2025
590c363
update demo
Aug 19, 2025
7d020c8
update demo
Aug 19, 2025
18ca614
use tokens
Aug 19, 2025
ca0b682
fix typo
Aug 19, 2025
0503c0c
Fix
Aug 19, 2025
b41e76d
improts
Aug 19, 2025
dacdc83
fix order
Aug 19, 2025
3f4d962
fix
Aug 19, 2025
97f143b
add cs
Aug 19, 2025
5881665
ove
Aug 19, 2025
f85871e
fix
Aug 19, 2025
1e09b70
fix
Aug 20, 2025
8a29a8b
fix
Aug 20, 2025
793f1f9
fix
Aug 20, 2025
b174cd9
fix
Aug 20, 2025
462eae9
Merge branch 'main' into deep-link-default-nav-context
ENvironmentSet Aug 20, 2025
658ccd5
fix
Aug 22, 2025
9e66305
with constructors
Aug 22, 2025
e547585
check on init
Sep 2, 2025
6bb8ccd
add links
Sep 5, 2025
1b9ea70
fix
Sep 5, 2025
9d4b11a
lol
Sep 5, 2025
d65e0c6
yayay
Sep 5, 2025
881d470
reorder
Sep 5, 2025
180da10
rr
Sep 5, 2025
4711c2e
memo
Sep 5, 2025
da7bcd1
Merge branch 'main' into deep-link-default-nav-context
ENvironmentSet Sep 5, 2025
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
8 changes: 8 additions & 0 deletions .changeset/chubby-pianos-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@stackflow/react": minor
---

`StructuredActivityComponentType` is added.

Structured activity components are components modeling activity view while exposing major features of an activity.
Allowing developers easily customize user experience of an activity view, it even allow stackflow to perform various kinds of optimizations.
5 changes: 5 additions & 0 deletions .changeset/gold-groups-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackflow/plugin-preload": patch
---

Sync with type constraint changes in stackflow/react
5 changes: 5 additions & 0 deletions .changeset/new-pandas-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackflow/plugin-history-sync": minor
---

Add `defaultHistory` route option to pre-seed stack for better deep link experiences
65 changes: 0 additions & 65 deletions demo/src/activities/Article.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { vars } from "@seed-design/design-token";
import { style } from "@vanilla-extract/css";

import { f } from "../styles";
import { f } from "../../styles";

export const container = style([
f.posAbsFull,
Expand Down
51 changes: 51 additions & 0 deletions demo/src/activities/Article/Article.content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
content,
useActivityParams,
useLoaderData,
} from "@stackflow/react/future";
import { LazyLoadImage } from "react-lazy-load-image-component";
import ArticleCard from "../../components/ArticleCard";
import ArticleProfile from "../../components/ArticleProfile";
import * as css from "./Article.content.css";
import type { articleLoader } from "./Article.loader";

const ArticleContent = content<"Article">(() => {
const { title } = useActivityParams<"Article">();
const { imageUrl, recommenderCards } = useLoaderData<typeof articleLoader>();

return (
<div className={css.container}>
<div className={css.image}>
<div className={css.imageInner}>
<LazyLoadImage
src={imageUrl}
effect="opacity"
width="100%"
height="100%"
/>
</div>
</div>
<ArticleProfile />
<div className={css.content}>
<div className={css.title}>{title}</div>
<div className={css.subtitle}>Baby & Kids ∙ 3 days ago</div>
<div className={css.body}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean
elementum sit sem ullamcorper urna, lacinia eu tortor, mattis.
Venenatis ut cursus amet in.
</div>
<div className={css.subtitle}>1 chats ∙ 2 favorites ∙ 212 views</div>
</div>
<div className={css.section}>
<div className={css.sectionTitle}>Other Items by Emila </div>
<div className={css.recommenderGrid}>
{recommenderCards.map((card) => (
<ArticleCard key={card.articleId} {...card} />
))}
</div>
</div>
</div>
);
});

export default ArticleContent;
8 changes: 8 additions & 0 deletions demo/src/activities/Article/Article.layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { layout } from "@stackflow/react/future";
import Layout from "../../components/Layout";

const ArticleLayout = layout<"Article">(({ params: { title }, children }) => {
return <Layout appBar={{ title }}>{children}</Layout>;
});

export default ArticleLayout;
9 changes: 9 additions & 0 deletions demo/src/activities/Article/Article.loading.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { style } from "@vanilla-extract/css";

export const container = style({
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100%",
width: "100%",
});
14 changes: 14 additions & 0 deletions demo/src/activities/Article/Article.loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { loading } from "@stackflow/react/future";
import LoadingSpinner from "../../components/LoadingSpinner";

import * as css from "./Article.loading.css";

const ArticleLoading = loading<"Article">(() => {
return (
<div className={css.container}>
<LoadingSpinner />
</div>
);
});

export default ArticleLoading;
18 changes: 18 additions & 0 deletions demo/src/activities/Article/Article.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { structuredActivityComponent } from "@stackflow/react/future";
import ArticleLayout from "./Article.layout";
import ArticleLoading from "./Article.loading";

declare module "@stackflow/config" {
interface Register {
Article: {
articleId: number;
title?: string;
};
}
}

export const Article = structuredActivityComponent<"Article">({
content: () => import("./Article.content"),
layout: ArticleLayout,
loading: ArticleLoading,
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { style } from "@vanilla-extract/css";

import { f } from "../styles";
import { f } from "../../styles";

export const wrapper = style([f.posAbsFull, f.flexColumn, f.rootLineHeight]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { ActivityComponentType } from "@stackflow/react/future";
import { useLoaderData } from "@stackflow/react/future";

import IconBell from "../assets/IconBell";
import IconExpandMore from "../assets/IconExpandMore";
import IconSearch from "../assets/IconSearch";
import BottomTab from "../components/BottomTab";
import FeedCard from "../components/FeedCard";
import Layout from "../components/Layout";
import IconBell from "../../assets/IconBell";
import IconExpandMore from "../../assets/IconExpandMore";
import IconSearch from "../../assets/IconSearch";
import BottomTab from "../../components/BottomTab";
import FeedCard from "../../components/FeedCard";
import Layout from "../../components/Layout";
import * as css from "./Main.css";
import type { mainLoader } from "./Main.loader";

Expand Down
16 changes: 16 additions & 0 deletions demo/src/components/LoadingSpinner.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { vars } from "@seed-design/design-token";
import { keyframes, style } from "@vanilla-extract/css";

const spin = keyframes({
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" },
});

export const spinner = style({
width: 48,
height: 48,
border: `4px solid ${vars.$scale.color.gray600}`,
borderTop: `4px solid ${vars.$scale.color.gray900}`,
borderRadius: "50%",
animation: `${spin} 1s linear infinite`,
});
9 changes: 9 additions & 0 deletions demo/src/components/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { FC } from "react";

import * as css from "./LoadingSpinner.css";

const LoadingSpinner: FC = () => {
return <div className={css.spinner} />;
};

export default LoadingSpinner;
7 changes: 4 additions & 3 deletions demo/src/stackflow/Stack.ts → demo/src/stackflow/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { vars } from "@seed-design/design-token";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
import { historySyncPlugin } from "@stackflow/plugin-history-sync";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { lazy, stackflow } from "@stackflow/react/future";
import Main from "../activities/Main";
import { stackflow } from "@stackflow/react/future";
import { Article } from "../activities/Article/Article";
import Main from "../activities/Main/Main";
import { config } from "./stackflow.config";

export const { Stack, actions } = stackflow({
config,
components: {
Main,
Article: lazy(() => import("../activities/Article")),
Article,
},
plugins: [
basicRendererPlugin(),
Expand Down
10 changes: 8 additions & 2 deletions demo/src/stackflow/stackflow.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineConfig } from "@stackflow/config";
import { articleLoader } from "../activities/Article.loader";
import { mainLoader } from "../activities/Main.loader";
import { articleLoader } from "../activities/Article/Article.loader";
import { mainLoader } from "../activities/Main/Main.loader";

export const config = defineConfig({
activities: [
Expand All @@ -17,6 +17,12 @@ export const config = defineConfig({
articleId: Number(params.articleId),
title: params.title,
}),
defaultHistory: () => [
{
activityName: "Main",
activityParams: {},
},
],
},
loader: articleLoader,
},
Expand Down
4 changes: 2 additions & 2 deletions demo/src/stackflow/stackflow.docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { vars } from "@seed-design/design-token";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { stackflow } from "@stackflow/react/future";
import Article from "../activities/Article";
import Main from "../activities/Main";
import { Article } from "../activities/Article/Article";
import Main from "../activities/Main/Main";
import { config } from "./stackflow.config";

export const { Stack } = stackflow({
Expand Down
16 changes: 16 additions & 0 deletions extensions/plugin-history-sync/src/RouteLike.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import type {
RegisteredActivityName,
RegisteredActivityParamTypes,
} from "@stackflow/config";
import type { ActivityComponentType } from "@stackflow/react";

export type Route<ComponentType> = {
path: string;
decode?: (
params: Record<string, string>,
) => ComponentType extends ActivityComponentType<infer U> ? U : {};
defaultHistory?: (params: Record<string, string>) => HistoryEntry[];
};

export type HistoryEntry = {
[K in RegisteredActivityName]: {
activityName: K;
activityParams: RegisteredActivityParamTypes[K];
additionalSteps?: {
stepParams: RegisteredActivityParamTypes[K];
hasZIndex?: boolean;
}[];
};
}[RegisteredActivityName];

export type RouteLike<ComponentType> =
| string
| string[]
Expand Down
Loading
Loading