Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .changeset/young-rules-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@stackflow/plugin-history-sync": patch
"@stackflow/react": patch
"@stackflow/link": patch
"@stackflow/config": patch
---

fix: add `decode()` interface to `Config` and support `path: string[]`
5 changes: 4 additions & 1 deletion config/src/ActivityDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { ActivityLoader } from "./ActivityLoader";
import type { RegisteredActivityName } from "./RegisteredActivityName";

export interface ActivityDefinition<ActivityName extends string> {
export interface ActivityDefinition<
ActivityName extends RegisteredActivityName,
> {
name: ActivityName;
loader?: ActivityLoader<any>;
}
3 changes: 2 additions & 1 deletion config/src/Config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { ActivityDefinition } from "./ActivityDefinition";
import type { ConfigDefinition } from "./ConfigDefinition";
import type { RegisteredActivityName } from "./RegisteredActivityName";

export interface Config<T extends ActivityDefinition<string>>
export interface Config<T extends ActivityDefinition<RegisteredActivityName>>
extends ConfigDefinition<T> {
decorate<
K extends Exclude<keyof Config<T>, keyof ConfigDefinition<T> | "decorate">,
Expand Down
5 changes: 4 additions & 1 deletion config/src/ConfigDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { ActivityDefinition } from "./ActivityDefinition";
import type { RegisteredActivityName } from "./RegisteredActivityName";

export interface ConfigDefinition<T extends ActivityDefinition<string>> {
export interface ConfigDefinition<
T extends ActivityDefinition<RegisteredActivityName>,
> {
activities: T[];
transitionDuration: number;
initialActivity?: () => T["name"];
Expand Down
3 changes: 2 additions & 1 deletion config/src/defineConfig.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { ActivityDefinition } from "./ActivityDefinition";
import type { Config } from "./Config";
import type { ConfigDefinition } from "./ConfigDefinition";
import type { RegisteredActivityName } from "./RegisteredActivityName";

export function defineConfig<
ActivityName extends string,
ActivityName extends RegisteredActivityName,
Activity extends ActivityDefinition<ActivityName>,
>(configDefinition: ConfigDefinition<Activity>): Config<Activity> {
const config: Config<Activity> = {
Expand Down
20 changes: 10 additions & 10 deletions demo/src/activities/Article.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,52 @@ export function articleLoader({ params }: ActivityLoaderArgs<"Article">) {

const recommenderCards = [
{
articleId: "25140667",
articleId: 25140667,
price: 41,
title: "Ran",
},
{
articleId: "60547101",
articleId: 60547101,
price: 24,
title: "Rest",
},
{
articleId: "34751776",
articleId: 34751776,
price: 42,
title: "Those",
},
{
articleId: "04114554",
articleId: 4114554,
price: 12,
title: "Beauty",
},
{
articleId: "81339443",
articleId: 81339443,
price: 3,
title: "Mighty",
},
{
articleId: "44738871",
articleId: 44738871,
price: 1,
title: "Afternoon",
},
{
articleId: "57388513",
articleId: 57388513,
price: 31,
title: "Brown",
},
{
articleId: "60883443",
articleId: 60883443,
price: 49,
title: "Musical",
},
{
articleId: "00932094",
articleId: 932094,
price: 26,
title: "Occasionally",
},
{
articleId: "10749683",
articleId: 10749683,
price: 35,
title: "Having",
},
Expand Down
2 changes: 1 addition & 1 deletion demo/src/activities/Article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { articleLoader } from "./Article.loader";
declare module "@stackflow/config" {
interface Register {
Article: {
articleId: string;
articleId: number;
title?: string;
};
}
Expand Down
20 changes: 10 additions & 10 deletions demo/src/activities/Main.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,70 @@ export function mainLoader() {
return {
cards: [
{
articleId: "02542470",
articleId: 2542470,
price: 41,
title: "Master",
region: "Nagevan",
daysAgo: 4,
},
{
articleId: "11257089",
articleId: 11257089,
price: 24,
title: "Wild",
region: "Inguima",
daysAgo: 4,
},
{
articleId: "08407137",
articleId: 8407137,
price: 42,
title: "Universe",
region: "Litenego",
daysAgo: 4,
},
{
articleId: "32979422",
articleId: 32979422,
price: 12,
title: "Private",
region: "Umumtaw",
daysAgo: 6,
},
{
articleId: "37998208",
articleId: 37998208,
price: 3,
title: "Harbor",
region: "Gubdidgi",
daysAgo: 3,
},
{
articleId: "01695878",
articleId: 1695878,
price: 1,
title: "Valuable",
region: "Jumjelewu",
daysAgo: 1,
},
{
articleId: "09792471",
articleId: 9792471,
price: 31,
title: "Also",
region: "Salhega",
daysAgo: 1,
},
{
articleId: "23939055",
articleId: 23939055,
price: 49,
title: "Ever",
region: "Jaifuup",
daysAgo: 9,
},
{
articleId: "94689745",
articleId: 94689745,
price: 26,
title: "Production",
region: "Idcipwel",
daysAgo: 3,
},
{
articleId: "49322156",
articleId: 49322156,
price: 35,
title: "Chest",
region: "Ajapaktar",
Expand Down
4 changes: 2 additions & 2 deletions demo/src/components/ArticleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { LazyLoadImage } from "react-lazy-load-image-component";
import * as css from "./ArticleCard.css";

interface ArticleCardProps {
articleId: string;
articleId: number;
title: string;
price: number;
}
Expand All @@ -17,7 +17,7 @@ const ArticleCard: React.FC<ArticleCardProps> = ({
return (
<Link
activityName="Article"
activityParams={{ articleId: String(articleId), title }}
activityParams={{ articleId, title }}
className={css.container}
>
<div className={css.thumbnail}>
Expand Down
4 changes: 2 additions & 2 deletions demo/src/components/FeedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { LazyLoadImage } from "react-lazy-load-image-component";
import * as css from "./FeedCard.css";

interface FeedCardProps {
articleId: string;
articleId: number;
title: string;
region: string;
price: number;
Expand All @@ -22,7 +22,7 @@ const FeedCard: React.FC<FeedCardProps> = ({

const onClick = () => {
push("Article", {
articleId: String(articleId),
articleId,
title,
});
};
Expand Down
10 changes: 8 additions & 2 deletions demo/src/stackflow/stackflow.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ export const config = defineConfig({
activities: [
{
name: "Main",
path: "/",
route: "/",
loader: mainLoader,
},
{
name: "Article",
path: "/articles/:articleId",
route: {
path: "/articles/:articleId",
decode: (params) => ({
articleId: Number(params.articleId),
title: params.title,
}),
},
loader: articleLoader,
},
],
Expand Down
19 changes: 11 additions & 8 deletions extensions/link/src/future/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import type {
InferActivityParams,
RegisteredActivityName,
} from "@stackflow/config";
import type { Route } from "@stackflow/plugin-history-sync";
import { useConfig, useFlow } from "@stackflow/react/future";
import { useMemo } from "react";
import { omit } from "./omit";

function toRoute<T>(route: string | Route<T>): Route<T> {
return typeof route === "string" ? { path: route, decode: undefined } : route;
}

type AnchorProps = Omit<
React.DetailedHTMLProps<
React.AnchorHTMLAttributes<HTMLAnchorElement>,
Expand All @@ -32,19 +37,17 @@ export function Link<K extends RegisteredActivityName>(props: LinkProps<K>) {
const href = useMemo(() => {
const match = config.activities.find((r) => r.name === props.activityName);

if (
!match ||
!match.path ||
typeof match.path !== "string" ||
!config.historySync
) {
if (!match || !match.route || !config.historySync) {
return undefined;
}

const { path } = match;
const { path, decode } = Array.isArray(match.route)
? toRoute(match.route[0])
: toRoute(match.route);

const { makeTemplate, urlPatternOptions } = config.historySync;

const template = makeTemplate({ path }, urlPatternOptions);
const template = makeTemplate({ path, decode }, urlPatternOptions);

return template.fill(props.activityParams);
}, [config, props.activityName, props.activityParams]);
Expand Down
10 changes: 7 additions & 3 deletions extensions/plugin-history-sync/src/RouteLike.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { ActivityComponentType } from "@stackflow/react";

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

export type RouteLike<T> = string | string[] | Route<T> | Route<T>[];
export type RouteLike<ComponentType> =
| string
| string[]
| Route<ComponentType>
| Route<ComponentType>[];
20 changes: 11 additions & 9 deletions extensions/plugin-history-sync/src/historySyncPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { ActivityDefinition, Config } from "@stackflow/config";
import type {
ActivityDefinition,
Config,
RegisteredActivityName,
} from "@stackflow/config";
import { id, makeEvent } from "@stackflow/core";
import type { StackflowReactPlugin } from "@stackflow/react";
import type { ActivityComponentType } from "@stackflow/react/future";
import type { History, Listener } from "history";
import { createBrowserHistory, createMemoryHistory } from "history";
import { HistoryQueueProvider } from "./HistoryQueueContext";
Expand All @@ -23,11 +28,11 @@ type ConfigHistorySync = {
};

declare module "@stackflow/config" {
interface ActivityDefinition<ActivityName extends string> {
path: string;
interface ActivityDefinition<ActivityName extends RegisteredActivityName> {
route: RouteLike<ActivityComponentType<RegisteredActivityName>>;
}

interface Config<T extends ActivityDefinition<string>> {
interface Config<T extends ActivityDefinition<RegisteredActivityName>> {
historySync?: ConfigHistorySync;
}
}
Expand All @@ -39,7 +44,7 @@ type HistorySyncPluginOptions<T, K extends Extract<keyof T, string>> = (
};
}
| {
config: Config<ActivityDefinition<string>>;
config: Config<ActivityDefinition<RegisteredActivityName>>;
}
) & {
fallbackActivity: (args: { initialContext: any }) => K;
Expand Down Expand Up @@ -72,10 +77,7 @@ export function historySyncPlugin<
"routes" in options
? options.routes
: options.config.activities.reduce(
(acc, a) => ({
...acc,
[a.name]: a.path,
}),
(acc, a) => ({ ...acc, [a.name]: a.route }),
{},
);

Expand Down
1 change: 1 addition & 0 deletions extensions/plugin-history-sync/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { useHistoryTick } from "./HistoryQueueContext";
export * from "./historySyncPlugin";
export { makeTemplate, UrlPatternOptions } from "./makeTemplate";
export { useRoutes } from "./RoutesContext";
export { Route, RouteLike } from "./RouteLike";
Loading
Loading