diff --git a/apps/builder/app/builder/features/address-bar.stories.tsx b/apps/builder/app/builder/features/address-bar.stories.tsx index 601bd84cef29..0785fb9a4019 100644 --- a/apps/builder/app/builder/features/address-bar.stories.tsx +++ b/apps/builder/app/builder/features/address-bar.stories.tsx @@ -1,31 +1,15 @@ -import { computed } from "nanostores"; import { useStore } from "@nanostores/react"; import type { Meta, StoryFn } from "@storybook/react"; import { Box, Text, theme } from "@webstudio-is/design-system"; import { AddressBarPopover } from "./address-bar"; -import { - $dataSourceVariables, - $dataSources, - $pages, -} from "~/shared/nano-states"; +import { $dataSources, $pages } from "~/shared/nano-states"; import { registerContainers } from "~/shared/sync"; import { $awareness, $selectedPage } from "~/shared/awareness"; +import { $currentSystem } from "~/shared/system"; registerContainers(); -$dataSources.set( - new Map([ - [ - "systemId", - { - id: "systemId", - scopeInstanceId: "rootInstanceId", - name: "system", - type: "parameter", - }, - ], - ]) -); +$dataSources.set(new Map()); $pages.set({ folders: [ @@ -43,7 +27,6 @@ $pages.set({ title: "", meta: {}, rootInstanceId: "", - systemDataSourceId: "", }, pages: [ { @@ -53,23 +36,12 @@ $pages.set({ title: "", meta: {}, rootInstanceId: "rootInstanceId", - systemDataSourceId: "systemId", }, ], }); -const $selectedPageSystem = computed( - [$selectedPage, $dataSourceVariables], - (selectedPage, dataSourceVariables) => { - if (selectedPage?.systemDataSourceId === undefined) { - return {}; - } - return dataSourceVariables.get(selectedPage.systemDataSourceId); - } -); - const SystemInspect = () => { - const system = useStore($selectedPageSystem); + const system = useStore($currentSystem); return ( {JSON.stringify(system, null, 2)} @@ -77,16 +49,11 @@ const SystemInspect = () => { ); }; -const $selectedPageHistory = computed( - $selectedPage, - (page) => page?.history ?? [] -); - const HistoryInspect = () => { - const history = useStore($selectedPageHistory); + const page = useStore($selectedPage); return ( - {JSON.stringify(history, null, 2)} + {JSON.stringify(page?.history, null, 2)} ); }; diff --git a/apps/builder/app/builder/features/address-bar.tsx b/apps/builder/app/builder/features/address-bar.tsx index f186ff232a24..f34c50838397 100644 --- a/apps/builder/app/builder/features/address-bar.tsx +++ b/apps/builder/app/builder/features/address-bar.tsx @@ -31,23 +31,16 @@ import { findParentFolderByChildId, ROOT_FOLDER_ID, getPagePath, - type System, } from "@webstudio-is/sdk"; -import { - $dataSourceVariables, - $pages, - $publishedOrigin, - $selectedPageDefaultSystem, - updateSystem, -} from "~/shared/nano-states"; +import { $pages, $publishedOrigin } from "~/shared/nano-states"; import { compilePathnamePattern, isPathnamePattern, matchPathnamePattern, tokenizePathnamePattern, } from "~/builder/shared/url-pattern"; -import { savePathInHistory } from "~/shared/pages"; import { $selectedPage } from "~/shared/awareness"; +import { $currentSystem, updateCurrentSystem } from "~/shared/system"; const $selectedPagePath = computed([$selectedPage, $pages], (page, pages) => { if (pages === undefined || page === undefined) { @@ -62,19 +55,6 @@ const $selectedPagePath = computed([$selectedPage, $pages], (page, pages) => { .replace(/\/+/g, "/"); }); -const $selectedPagePathParams = computed( - [$selectedPageDefaultSystem, $selectedPage, $dataSourceVariables], - (defaultSystem, selectedPage, dataSourceVariables) => { - if (selectedPage?.systemDataSourceId === undefined) { - return defaultSystem.params; - } - const system = dataSourceVariables.get(selectedPage.systemDataSourceId) as - | undefined - | System; - return system?.params ?? defaultSystem.params; - } -); - const $selectedPageHistory = computed( $selectedPage, (page) => page?.history ?? [] @@ -278,7 +258,7 @@ const AddressBar = forwardRef< return history.filter((item) => matchPathnamePattern(path, item)); }, [history, path]); const [pathParams, setPathParams] = useState( - () => $selectedPagePathParams.get() ?? {} + () => $currentSystem.get().params ); const tokens = tokenizePathnamePattern(path); const compiledPath = compilePathnamePattern(tokens, pathParams); @@ -307,26 +287,11 @@ const AddressBar = forwardRef< return (
{ event.preventDefault(); const formData = new FormData(event.currentTarget); - const path = $selectedPagePath.get(); - const tokens = tokenizePathnamePattern(path); - // delete stale fields - const newParams: Record = {}; - for (const token of tokens) { - if (token.type === "param") { - newParams[token.name] = String(formData.get(token.name) ?? ""); - } - } - const page = $selectedPage.get(); - if (page === undefined) { - return; - } - updateSystem(page, { params: newParams }); - const compiledPath = compilePathnamePattern(tokens, newParams); - savePathInHistory(page.id, compiledPath); + const params = Object.fromEntries(formData) as Record; + updateCurrentSystem({ params }); if (errors.size === 0) { onSubmit(); } @@ -357,6 +322,7 @@ const AddressBar = forwardRef< key={index} name={token.name} fieldSizing="content" + autoComplete="off" css={{ minWidth: theme.spacing[15] }} color={errors.has(token.name) ? "error" : undefined} placeholder={token.name} diff --git a/apps/builder/app/builder/features/command-panel/command-panel.stories.tsx b/apps/builder/app/builder/features/command-panel/command-panel.stories.tsx index b8192f152cc0..ed2962997279 100644 --- a/apps/builder/app/builder/features/command-panel/command-panel.stories.tsx +++ b/apps/builder/app/builder/features/command-panel/command-panel.stories.tsx @@ -40,16 +40,12 @@ $breakpoints.set( ) ); -const pages = createDefaultPages({ - rootInstanceId: "", - systemDataSourceId: "", -}); +const pages = createDefaultPages({ rootInstanceId: "" }); pages.pages.push({ id: "page2", path: "", name: "Second Page", rootInstanceId: "", - systemDataSourceId: "", title: "", meta: {}, }); @@ -58,7 +54,6 @@ pages.pages.push({ path: "", name: "Thrid Page", rootInstanceId: "", - systemDataSourceId: "", title: "", meta: {}, }); diff --git a/apps/builder/app/builder/features/pages/page-settings.stories.tsx b/apps/builder/app/builder/features/pages/page-settings.stories.tsx index 279f07c4e014..68ea06950edd 100644 --- a/apps/builder/app/builder/features/pages/page-settings.stories.tsx +++ b/apps/builder/app/builder/features/pages/page-settings.stories.tsx @@ -37,10 +37,7 @@ $assets.set( ]) ); -const pages = createDefaultPages({ - rootInstanceId: "root-instance-id", - systemDataSourceId: "systemDataSourceId", -}); +const pages = createDefaultPages({ rootInstanceId: "root-instance-id" }); pages.meta = { siteName: "Project name", faviconAssetId: "imageId", @@ -53,7 +50,6 @@ pages.pages.push({ name: "page-name", meta: {}, rootInstanceId: "root-instance-id", - systemDataSourceId: "systemDataSourceId", }); const rootFolder = pages.folders.find(isRootFolder); rootFolder?.children.push("pageId"); diff --git a/apps/builder/app/builder/features/pages/page-utils.test.ts b/apps/builder/app/builder/features/pages/page-utils.test.ts index a47ae630e1e0..881fc48ca40c 100644 --- a/apps/builder/app/builder/features/pages/page-utils.test.ts +++ b/apps/builder/app/builder/features/pages/page-utils.test.ts @@ -26,6 +26,7 @@ import { } from "~/shared/nano-states"; import { registerContainers } from "~/shared/sync"; import { $awareness } from "~/shared/awareness"; +import { updateCurrentSystem } from "~/shared/system"; setEnv("*"); registerContainers(); @@ -525,10 +526,9 @@ test("page root scope should prefill default system variable value", () => { ], ]), }); - - $dataSourceVariables.set( - new Map([["systemId", { params: { slug: "my-post" }, search: {} }]]) - ); + updateCurrentSystem({ + params: { slug: "my-post" }, + }); expect($pageRootScope.get()).toEqual({ aliases: new Map([["$ws$dataSource$systemId", "system"]]), scope: { diff --git a/apps/builder/app/builder/features/project-settings/project-settings.stories.tsx b/apps/builder/app/builder/features/project-settings/project-settings.stories.tsx index a0587092ff8d..d6a6e3fa58a9 100644 --- a/apps/builder/app/builder/features/project-settings/project-settings.stories.tsx +++ b/apps/builder/app/builder/features/project-settings/project-settings.stories.tsx @@ -33,7 +33,6 @@ export const Redirects = () => { title: `"My Title"`, meta: {}, rootInstanceId: "body", - systemDataSourceId: "", }, pages: [], folders: [], diff --git a/apps/builder/app/builder/features/project-settings/utils.test.ts b/apps/builder/app/builder/features/project-settings/utils.test.ts index 192e442d5071..ffde0c340401 100644 --- a/apps/builder/app/builder/features/project-settings/utils.test.ts +++ b/apps/builder/app/builder/features/project-settings/utils.test.ts @@ -6,7 +6,6 @@ describe("getExistingRoutePaths", () => { test("gets all the route paths that exists in the project", () => { const pages = createDefaultPages({ rootInstanceId: "rootInstanceId", - systemDataSourceId: "systemDataSourceId", homePageId: "homePageId", }); @@ -16,7 +15,6 @@ describe("getExistingRoutePaths", () => { name: "Page", path: "/page", rootInstanceId: "rootInstanceId", - systemDataSourceId: "systemDataSourceId", title: `"Page"`, }); @@ -26,7 +24,6 @@ describe("getExistingRoutePaths", () => { name: "Blog", path: "/blog/:id", rootInstanceId: "rootInstanceId", - systemDataSourceId: "systemDataSourceId", title: `"Blog"`, }); diff --git a/apps/builder/app/builder/features/settings-panel/props-section/props-section.stories.tsx b/apps/builder/app/builder/features/settings-panel/props-section/props-section.stories.tsx index 4aa40ac50000..0fcde03c55df 100644 --- a/apps/builder/app/builder/features/settings-panel/props-section/props-section.stories.tsx +++ b/apps/builder/app/builder/features/settings-panel/props-section/props-section.stories.tsx @@ -26,15 +26,10 @@ const page = (name: string, path: string): Page => ({ path, meta: {}, rootInstanceId: unique(), - systemDataSourceId: unique(), }); $pages.set({ - ...createDefaultPages({ - rootInstanceId: unique(), - systemDataSourceId: unique(), - }), - + ...createDefaultPages({ rootInstanceId: unique() }), homePage: page("Home", "") as Page & { path: "" }, pages: [ page("About", "/about"), diff --git a/apps/builder/app/builder/features/settings-panel/variables-section.stories.tsx b/apps/builder/app/builder/features/settings-panel/variables-section.stories.tsx index 640baf8acef9..d5d5a1e04025 100644 --- a/apps/builder/app/builder/features/settings-panel/variables-section.stories.tsx +++ b/apps/builder/app/builder/features/settings-panel/variables-section.stories.tsx @@ -22,9 +22,7 @@ $instances.set( ["box", { id: "box", type: "instance", component: "Box", children: [] }], ]) ); -$pages.set( - createDefaultPages({ rootInstanceId: "box", systemDataSourceId: "system" }) -); +$pages.set(createDefaultPages({ rootInstanceId: "box" })); $awareness.set({ pageId: "home", instanceSelector: ["box"] }); export const VariablesSection: StoryObj = { diff --git a/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.stories.tsx b/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.stories.tsx index ae5663d322c0..d3cdeaffb99f 100644 --- a/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.stories.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.stories.tsx @@ -37,7 +37,6 @@ $pages.set( createDefaultPages({ homePageId: "homePageId", rootInstanceId: "box", - systemDataSourceId: "systemId", }) ); $awareness.set({ diff --git a/apps/builder/app/builder/features/style-panel/sections/position/inset-control.stories.tsx b/apps/builder/app/builder/features/style-panel/sections/position/inset-control.stories.tsx index 08dcbce32738..b7a0157823ee 100644 --- a/apps/builder/app/builder/features/style-panel/sections/position/inset-control.stories.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/position/inset-control.stories.tsx @@ -47,7 +47,6 @@ $pages.set( createDefaultPages({ homePageId: "homePageId", rootInstanceId: "box", - systemDataSourceId: "systemId", }) ); $awareness.set({ diff --git a/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.stories.tsx b/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.stories.tsx index 86adc70e5d72..bb6e5d7ea9ce 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.stories.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.stories.tsx @@ -56,7 +56,6 @@ $pages.set( createDefaultPages({ homePageId: "homePageId", rootInstanceId: "box", - systemDataSourceId: "systemId", }) ); $awareness.set({ diff --git a/apps/builder/app/builder/shared/commands.test.tsx b/apps/builder/app/builder/shared/commands.test.tsx index dee00a876f83..fdb601764f4e 100644 --- a/apps/builder/app/builder/shared/commands.test.tsx +++ b/apps/builder/app/builder/shared/commands.test.tsx @@ -15,7 +15,7 @@ registerContainers(); const metas = new Map(Object.entries(baseMetas)); $registeredComponentMetas.set(metas); -$pages.set(createDefaultPages({ rootInstanceId: "", systemDataSourceId: "" })); +$pages.set(createDefaultPages({ rootInstanceId: "" })); $awareness.set({ pageId: "" }); describe("deleteInstance", () => { diff --git a/apps/builder/app/canvas/features/text-editor/text-editor.stories.tsx b/apps/builder/app/canvas/features/text-editor/text-editor.stories.tsx index d2e058cdd662..605b692f09f4 100644 --- a/apps/builder/app/canvas/features/text-editor/text-editor.stories.tsx +++ b/apps/builder/app/canvas/features/text-editor/text-editor.stories.tsx @@ -223,7 +223,6 @@ export const CursorPositioningUpDown: StoryFn = () => { path: "", title: "", name: "", - systemDataSourceId: "", }, pages: [ { @@ -232,7 +231,6 @@ export const CursorPositioningUpDown: StoryFn = () => { path: "", title: "", name: "", - systemDataSourceId: "", meta: {}, }, ], diff --git a/apps/builder/app/canvas/interceptor.ts b/apps/builder/app/canvas/interceptor.ts index 9fd2ff2ed3a2..a4b6bf3a04ff 100644 --- a/apps/builder/app/canvas/interceptor.ts +++ b/apps/builder/app/canvas/interceptor.ts @@ -10,9 +10,8 @@ import { $isPreviewMode, $pages, $selectedPageHash, - updateSystem, } from "~/shared/nano-states"; -import { savePathInHistory } from "~/shared/pages"; +import { updateCurrentSystem } from "~/shared/system"; const isAbsoluteUrl = (href: string) => { try { @@ -62,10 +61,9 @@ const switchPageAndUpdateSystem = (href: string, formData?: FormData) => { const search = Object.fromEntries(pageHref.searchParams); $selectedPageHash.set({ hash: pageHref.hash }); selectPage(page.id); - updateSystem(page, { params, search }); - savePathInHistory(page.id, pageHref.pathname); - break; + updateCurrentSystem({ params, search }); } + break; } }; diff --git a/apps/builder/app/shared/copy-paste.test.tsx b/apps/builder/app/shared/copy-paste.test.tsx index 8a4a75d27eb8..2e4f16af18db 100644 --- a/apps/builder/app/shared/copy-paste.test.tsx +++ b/apps/builder/app/shared/copy-paste.test.tsx @@ -33,7 +33,7 @@ $project.set({ id: "current_project" } as Project); const createStub = (element: JSX.Element) => { const project = { - pages: createDefaultPages({ rootInstanceId: "", systemDataSourceId: "" }), + pages: createDefaultPages({ rootInstanceId: "" }), ...renderData(element), }; // global root instance is never stored in data diff --git a/apps/builder/app/shared/copy-paste/plugin-instance.test.ts b/apps/builder/app/shared/copy-paste/plugin-instance.test.ts index 446027f13cb1..65d91fd7daa7 100644 --- a/apps/builder/app/shared/copy-paste/plugin-instance.test.ts +++ b/apps/builder/app/shared/copy-paste/plugin-instance.test.ts @@ -42,7 +42,6 @@ $pages.set( createDefaultPages({ homePageId: "home-page", rootInstanceId: "body0", - systemDataSourceId: "", }) ); $awareness.set({ pageId: "home-page" }); diff --git a/apps/builder/app/shared/instance-utils.test.tsx b/apps/builder/app/shared/instance-utils.test.tsx index 4e4bb74ce176..0070bb9a35ee 100644 --- a/apps/builder/app/shared/instance-utils.test.tsx +++ b/apps/builder/app/shared/instance-utils.test.tsx @@ -62,7 +62,7 @@ import { $awareness, getInstancePath, selectInstance } from "./awareness"; enableMapSet(); registerContainers(); -$pages.set(createDefaultPages({ rootInstanceId: "", systemDataSourceId: "" })); +$pages.set(createDefaultPages({ rootInstanceId: "" })); const defaultMetasMap = new Map( Object.entries({ ...defaultMetas, ...coreMetas }) @@ -713,7 +713,7 @@ describe("reparent instance", () => { const getWebstudioDataStub = ( data?: Partial ): WebstudioData => ({ - pages: createDefaultPages({ rootInstanceId: "", systemDataSourceId: "" }), + pages: createDefaultPages({ rootInstanceId: "" }), assets: new Map(), dataSources: new Map(), resources: new Map(), @@ -1305,7 +1305,6 @@ describe("find closest insertable", () => { createDefaultPages({ homePageId: "homePageId", rootInstanceId: "", - systemDataSourceId: "", }) ); $awareness.set({ diff --git a/apps/builder/app/shared/nano-states/props.test.tsx b/apps/builder/app/shared/nano-states/props.test.tsx index 1d46279e4121..f20485ef0b45 100644 --- a/apps/builder/app/shared/nano-states/props.test.tsx +++ b/apps/builder/app/shared/nano-states/props.test.tsx @@ -27,7 +27,10 @@ import { Variable, ws, } from "@webstudio-is/template"; +import { updateCurrentSystem } from "../system"; +import { registerContainers } from "../sync"; +registerContainers(); setEnv("*"); const getIdValuePair = (item: T) => @@ -899,9 +902,9 @@ test("prefill default system variable value", () => { ], ]) ); - $dataSourceVariables.set( - new Map([[systemId, { params: { slug: "my-post" }, search: {} }]]) - ); + updateCurrentSystem({ + params: { slug: "my-post" }, + }); expect($variableValuesByInstanceSelector.get()).toEqual( new Map([ [JSON.stringify([ROOT_INSTANCE_ID]), new Map()], diff --git a/apps/builder/app/shared/nano-states/props.ts b/apps/builder/app/shared/nano-states/props.ts index 221af295a4ac..a7dbff763b4f 100644 --- a/apps/builder/app/shared/nano-states/props.ts +++ b/apps/builder/app/shared/nano-states/props.ts @@ -5,7 +5,6 @@ import type { Instance, Prop, ResourceRequest, - System, ImageAsset, } from "@webstudio-is/sdk"; import { @@ -31,16 +30,12 @@ import { import { $pages } from "./pages"; import type { InstanceSelector } from "../tree-utils"; import { restResourcesLoader } from "../router-utils"; -import { - $dataSourceVariables, - $resourceValues, - $selectedPageDefaultSystem, - mergeSystem, -} from "./variables"; +import { $dataSourceVariables, $resourceValues } from "./variables"; import { uploadingFileDataToAsset } from "~/builder/shared/assets/asset-utils"; import { fetch } from "~/shared/fetch.client"; import { $selectedPage, getInstanceKey } from "../awareness"; import { computeExpression } from "../data-variables"; +import { $currentSystem, $currentSystemVariableId } from "../system"; export const assetBaseUrl = "/cgi/asset/"; @@ -136,9 +131,9 @@ const $unscopedVariableValues = computed( $dataSourceVariables, $resourceValues, $selectedPage, - $selectedPageDefaultSystem, + $currentSystem, ], - (dataSources, dataSourceVariables, resourceValues, page, defaultSystem) => { + (dataSources, dataSourceVariables, resourceValues, page, system) => { const values = new Map(); for (const [dataSourceId, dataSource] of dataSources) { if (dataSource.type === "variable") { @@ -149,8 +144,9 @@ const $unscopedVariableValues = computed( } if (dataSource.type === "parameter") { let value = dataSourceVariables.get(dataSourceId); + // @todo support global system if (dataSource.id === page?.systemDataSourceId) { - value = mergeSystem(defaultSystem, value as undefined | System); + value = system; } values.set(dataSourceId, value); } @@ -162,11 +158,6 @@ const $unscopedVariableValues = computed( } ); -const $selectedPageSystemId = computed( - $selectedPage, - (page) => page?.systemDataSourceId -); - /** * similar to above but should not depend on resource values * because these values are used to load resources @@ -177,10 +168,10 @@ const $loaderVariableValues = computed( [ $dataSources, $dataSourceVariables, - $selectedPageSystemId, - $selectedPageDefaultSystem, + $currentSystemVariableId, + $currentSystem, ], - (dataSources, dataSourceVariables, systemId, defaultSystem) => { + (dataSources, dataSourceVariables, systemVariableId, system) => { const values = new Map(); for (const [dataSourceId, dataSource] of dataSources) { if (dataSource.type === "variable") { @@ -191,8 +182,8 @@ const $loaderVariableValues = computed( } if (dataSource.type === "parameter") { let value = dataSourceVariables.get(dataSourceId); - if (dataSource.id === systemId) { - value = mergeSystem(defaultSystem, value as undefined | System); + if (dataSource.id === systemVariableId) { + value = system; } values.set(dataSourceId, value); } @@ -386,7 +377,7 @@ export const $variableValuesByInstanceSelector = computed( $dataSources, $dataSourceVariables, $resourceValues, - $selectedPageDefaultSystem, + $currentSystem, ], ( instances, @@ -395,7 +386,7 @@ export const $variableValuesByInstanceSelector = computed( dataSources, dataSourceVariables, resourceValues, - defaultSystem + system ) => { const propsByInstanceId = mapGroupBy( props.values(), @@ -443,10 +434,7 @@ export const $variableValuesByInstanceSelector = computed( const value = dataSourceVariables.get(variable.id); variableValues.set(variable.id, value); if (variable.id === page.systemDataSourceId) { - variableValues.set( - variable.id, - mergeSystem(defaultSystem, value as undefined | System) - ); + variableValues.set(variable.id, system); } } if (variable.type === "resource") { diff --git a/apps/builder/app/shared/nano-states/variables.ts b/apps/builder/app/shared/nano-states/variables.ts index d78477022189..2c6fc0e6dfb9 100644 --- a/apps/builder/app/shared/nano-states/variables.ts +++ b/apps/builder/app/shared/nano-states/variables.ts @@ -1,80 +1,8 @@ -import { atom, computed } from "nanostores"; -import type { DataSource, Page, Resource, System } from "@webstudio-is/sdk"; -import { - matchPathnamePattern, - tokenizePathnamePattern, -} from "~/builder/shared/url-pattern"; -import { $publishedOrigin } from "./misc"; -import { $selectedPage } from "../awareness"; +import { atom } from "nanostores"; +import type { DataSource, Resource } from "@webstudio-is/sdk"; export const $dataSourceVariables = atom>( new Map() ); export const $resourceValues = atom(new Map()); - -const $selectedPagePath = computed($selectedPage, (page) => page?.path); - -const $selectedPageHistory = computed($selectedPage, (page) => page?.history); - -export const $selectedPageDefaultSystem = computed( - [$publishedOrigin, $selectedPagePath, $selectedPageHistory], - (origin, path, history) => { - const defaultSystem: System = { - params: {}, - search: {}, - origin, - }; - if (path) { - const tokens = tokenizePathnamePattern(path); - // try to match the first item in history to let user - // see the page without manually entering params - // or selecting them in address bar - const matchedParams = history - ? matchPathnamePattern(path, history[0]) - : undefined; - for (const token of tokens) { - if (token.type === "param") { - defaultSystem.params[token.name] = - matchedParams?.[token.name] ?? undefined; - } - } - } - return defaultSystem; - } -); - -export const mergeSystem = (left: System, right?: System): System => { - return { - ...left, - ...right, - params: { - ...left.params, - ...right?.params, - }, - search: { - ...left.search, - ...right?.search, - }, - }; -}; - -export const updateSystem = (page: Page, update: Partial) => { - if (page.systemDataSourceId === undefined) { - return; - } - const dataSourceVariables = new Map($dataSourceVariables.get()); - const system = dataSourceVariables.get(page.systemDataSourceId) as - | undefined - | System; - - const newSystem: System = { - search: {}, - params: {}, - origin: $publishedOrigin.get(), - ...system, - ...update, - }; - dataSourceVariables.set(page.systemDataSourceId, newSystem); - $dataSourceVariables.set(dataSourceVariables); -}; diff --git a/apps/builder/app/shared/pages/index.ts b/apps/builder/app/shared/pages/index.ts index 2ccb47c60474..d270d13b9cf2 100644 --- a/apps/builder/app/shared/pages/index.ts +++ b/apps/builder/app/shared/pages/index.ts @@ -1,2 +1 @@ export * from "./use-switch-page"; -export * from "./pages"; diff --git a/apps/builder/app/shared/pages/pages.ts b/apps/builder/app/shared/pages/pages.ts deleted file mode 100644 index 4c7c6bef5a37..000000000000 --- a/apps/builder/app/shared/pages/pages.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { findPageByIdOrPath } from "@webstudio-is/sdk"; -import { $pages } from "../nano-states"; -import { serverSyncStore } from "../sync"; - -/** - * put new path into the beginning of history - * and drop paths in the end when exceeded 20 - */ -export const savePathInHistory = (pageId: string, path: string) => { - serverSyncStore.createTransaction([$pages], (pages) => { - if (pages === undefined) { - return; - } - const page = findPageByIdOrPath(pageId, pages); - if (page === undefined) { - return; - } - const history = Array.from(page.history ?? []); - history.unshift(path); - page.history = Array.from(new Set(history)).slice(0, 20); - }); -}; diff --git a/apps/builder/app/shared/sync/sync-stores.ts b/apps/builder/app/shared/sync/sync-stores.ts index 64cb63d5ae60..d111dcaae4b0 100644 --- a/apps/builder/app/shared/sync/sync-stores.ts +++ b/apps/builder/app/shared/sync/sync-stores.ts @@ -50,7 +50,6 @@ import { $modifierKeys, } from "~/shared/nano-states"; import { $ephemeralStyles } from "~/canvas/stores"; -import { $awareness, $temporaryInstances } from "../awareness"; import { ImmerhinSyncObject, NanostoresSyncObject, @@ -59,6 +58,8 @@ import { type SyncEmitter, } from "../sync-client"; import { $canvasScrollbarSize } from "~/builder/shared/nano-states"; +import { $awareness, $temporaryInstances } from "../awareness"; +import { $systemDataByPage } from "../system"; enableMapSet(); // safari structuredClone fix @@ -165,6 +166,7 @@ export const createObjectPool = () => { ), new NanostoresSyncObject("registeredTemplates", $registeredTemplates), new NanostoresSyncObject("canvasScrollbarWidth", $canvasScrollbarSize), + new NanostoresSyncObject("systemDataByPage", $systemDataByPage), ]); }; diff --git a/apps/builder/app/shared/pages/pages.test.ts b/apps/builder/app/shared/system.test.ts similarity index 54% rename from apps/builder/app/shared/pages/pages.test.ts rename to apps/builder/app/shared/system.test.ts index 6d1205435dc4..4fa4cdf30a1d 100644 --- a/apps/builder/app/shared/pages/pages.test.ts +++ b/apps/builder/app/shared/system.test.ts @@ -2,7 +2,8 @@ import { describe, expect, test } from "vitest"; import type { Page, Pages } from "@webstudio-is/sdk"; import { $pages } from "~/shared/nano-states"; import { registerContainers } from "~/shared/sync"; -import { savePathInHistory } from "./pages"; +import { updateCurrentSystem } from "./system"; +import { selectPage } from "./awareness"; registerContainers(); @@ -22,7 +23,6 @@ const getInitialPages = (page: Page): Pages => ({ title: "", meta: {}, rootInstanceId: "", - systemDataSourceId: "", }, pages: [page], }); @@ -37,13 +37,22 @@ describe("history", () => { title: "", meta: {}, rootInstanceId: "", - systemDataSourceId: "", }) ); - savePathInHistory("dynamicId", "/path1"); - expect($pages.get()?.pages[0].history).toEqual(["/path1"]); - savePathInHistory("dynamicId", "/path2"); - expect($pages.get()?.pages[0].history).toEqual(["/path2", "/path1"]); + selectPage("dynamicId"); + updateCurrentSystem({ + params: { date: "my-date", slug: "my-slug" }, + }); + expect($pages.get()?.pages[0].history).toEqual([ + "/blog/my-date/post/my-slug", + ]); + updateCurrentSystem({ + params: { date: "another-date", slug: "another-slug" }, + }); + expect($pages.get()?.pages[0].history).toEqual([ + "/blog/another-date/post/another-slug", + "/blog/my-date/post/my-slug", + ]); }); test("move existing path to the start", () => { @@ -55,11 +64,19 @@ describe("history", () => { title: "", meta: {}, rootInstanceId: "", - systemDataSourceId: "", - history: ["/path2", "/path1"], + history: [ + "/blog/another-date/post/another-slug", + "/blog/my-date/post/my-slug", + ], }) ); - savePathInHistory("dynamicId", "/path1"); - expect($pages.get()?.pages[0].history).toEqual(["/path1", "/path2"]); + selectPage("dynamicId"); + updateCurrentSystem({ + params: { date: "my-date", slug: "my-slug" }, + }); + expect($pages.get()?.pages[0].history).toEqual([ + "/blog/my-date/post/my-slug", + "/blog/another-date/post/another-slug", + ]); }); }); diff --git a/apps/builder/app/shared/system.ts b/apps/builder/app/shared/system.ts new file mode 100644 index 000000000000..c7c38b1eeb8f --- /dev/null +++ b/apps/builder/app/shared/system.ts @@ -0,0 +1,101 @@ +import { atom, computed } from "nanostores"; +import { + findPageByIdOrPath, + type Page, + type System, + SYSTEM_VARIABLE_ID, +} from "@webstudio-is/sdk"; +import { + compilePathnamePattern, + matchPathnamePattern, + tokenizePathnamePattern, +} from "~/builder/shared/url-pattern"; +import { $selectedPage } from "./awareness"; +import { $pages, $publishedOrigin } from "./nano-states"; +import { serverSyncStore } from "./sync"; + +export const $currentSystemVariableId = computed( + $selectedPage, + // fallback to global system variable + (page) => page?.systemDataSourceId ?? SYSTEM_VARIABLE_ID +); + +export const $systemDataByPage = atom( + new Map>() +); + +const extractParams = (pattern: string, path?: string) => { + const params: System["params"] = {}; + const tokens = tokenizePathnamePattern(pattern); + // try to match the first item in history to let user + // see the page without manually entering params + // or selecting them in address bar + const matchedParams = path ? matchPathnamePattern(pattern, path) : undefined; + for (const token of tokens) { + if (token.type === "param") { + params[token.name] = matchedParams?.[token.name] ?? undefined; + } + } + return params; +}; + +export const $currentSystem = computed( + [$publishedOrigin, $selectedPage, $systemDataByPage], + (origin, page, systemByPage) => { + const system: System = { + search: {}, + params: {}, + origin, + }; + if (page === undefined) { + return system; + } + const systemData = systemByPage.get(page.id); + const extractedParams = extractParams(page.path, page.history?.[0]); + return { + search: { ...system.search, ...systemData?.search }, + params: { ...extractedParams, ...systemData?.params }, + origin, + }; + } +); + +const compilePath = (pattern: string, params: System["params"]) => { + const tokens = tokenizePathnamePattern(pattern); + return compilePathnamePattern(tokens, params); +}; + +/** + * put new path into the beginning of history + * and drop paths in the end when exceeded 20 + */ +const savePathInHistory = (pageId: string, path: string) => { + serverSyncStore.createTransaction([$pages], (pages) => { + if (pages === undefined) { + return; + } + const page = findPageByIdOrPath(pageId, pages); + if (page === undefined) { + return; + } + const history = Array.from(page.history ?? []); + history.unshift(path); + page.history = Array.from(new Set(history)).slice(0, 20); + }); +}; + +export const updateCurrentSystem = ( + update: Partial> +) => { + const page = $selectedPage.get(); + if (page === undefined) { + return; + } + const systemDataByPage = new Map($systemDataByPage.get()); + const systemData = systemDataByPage.get(page.id); + const search = update.search ?? systemData?.search ?? {}; + const params = update.params ?? systemData?.params ?? {}; + systemDataByPage.set(page.id, { search, params }); + $systemDataByPage.set(systemDataByPage); + savePathInHistory(page.id, compilePath(page.path, params)); +}; diff --git a/packages/project-build/src/shared/pages-utils.ts b/packages/project-build/src/shared/pages-utils.ts index 4089047691aa..7b7f43f3bdf9 100644 --- a/packages/project-build/src/shared/pages-utils.ts +++ b/packages/project-build/src/shared/pages-utils.ts @@ -22,7 +22,7 @@ export const createDefaultPages = ({ homePageId = nanoid(), }: { rootInstanceId: Instance["id"]; - systemDataSourceId: DataSource["id"]; + systemDataSourceId?: DataSource["id"]; homePageId?: string; }): Pages => { // This is a root folder that nobody can delete or going to be able to see.