diff --git a/.github/actions/publish/action.yaml b/.github/actions/publish/action.yaml index 24a129d324..930f3578c2 100644 --- a/.github/actions/publish/action.yaml +++ b/.github/actions/publish/action.yaml @@ -1,9 +1,16 @@ name: 'Release NPM package' description: 'Release NPM package' inputs: - package: - description: 'Package.json location (relative)' - default: 'package.json' + skip-build: + description: 'Skip build phase (useful for only configuration files)' + default: '' + filter: + description: 'Filter build phase by turbo' + default: '' + required: true + workdir: + description: 'Work directory for NPM publish' + default: '.' required: true token: description: 'NPM Token' @@ -16,13 +23,16 @@ runs: - uses: actions/setup-node@v3 with: node-version: 16 - - name: Install Dependencies + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies and build 🔧 run: yarn install shell: bash - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ inputs.token }} - dry-run: false - check-version: true - access: 'public' - package: ${{ inputs.package }} \ No newline at end of file + - name: Build 🔧 + if: ${{ inputs.skip-build == '' }} + run: yarn build --filter ${{ inputs.filter }} + shell: bash + - name: Publish package on NPM 📦 + run: npm publish -w ${{ inputs.workdir }} --access public + shell: bash + env: + NODE_AUTH_TOKEN: ${{ inputs.token }} \ No newline at end of file diff --git a/.github/workflows/asset-uploader.yml b/.github/workflows/asset-uploader.yml new file mode 100644 index 0000000000..6169db3850 --- /dev/null +++ b/.github/workflows/asset-uploader.yml @@ -0,0 +1,17 @@ +name: Asset Uploader +on: + push: + paths: + - "packages/asset-uploader/**/*" + - ".github/workflows/asset-uploader.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + workdir: ./packages/asset-uploader + filter: "@webstudio-is/asset-uploader" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/design-system.yml b/.github/workflows/design-system.yml index 109456bc75..b7b00db417 100644 --- a/.github/workflows/design-system.yml +++ b/.github/workflows/design-system.yml @@ -1,4 +1,4 @@ -name: Design System Publish +name: Design System on: push: paths: @@ -12,5 +12,6 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/publish with: - package: "./packages/design-system/package.json" + workdir: ./packages/design-system + filter: "@webstudio-is/design-system" token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/eslint-config-custom.yml b/.github/workflows/eslint-config-custom.yml index 3effb53420..86952ce555 100644 --- a/.github/workflows/eslint-config-custom.yml +++ b/.github/workflows/eslint-config-custom.yml @@ -1,4 +1,4 @@ -name: Eslint Config Publish +name: Eslint Config on: push: paths: @@ -12,5 +12,7 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/publish with: - package: "./packages/eslint-config-custom/package.json" + skip-build: true + workdir: ./packages/eslint-config-custom + filter: "@webstudio-is/eslint-config-custom" token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/http-client.yml b/.github/workflows/http-client.yml new file mode 100644 index 0000000000..6a7ef77b88 --- /dev/null +++ b/.github/workflows/http-client.yml @@ -0,0 +1,17 @@ +name: HTTP Client +on: + push: + paths: + - "packages/http-client/**/*" + - ".github/workflows/http-client.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + workdir: ./packages/http-client + filter: "@webstudio-is/http-client" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/icons.yml b/.github/workflows/icons.yml index 3deb808f0b..9a62e16ccc 100644 --- a/.github/workflows/icons.yml +++ b/.github/workflows/icons.yml @@ -1,4 +1,4 @@ -name: Icons Publish +name: Icons on: push: paths: @@ -12,5 +12,6 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/publish with: - package: "./packages/icons/package.json" + workdir: ./packages/icons + filter: "@webstudio-is/icons" token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/jest-config.yml b/.github/workflows/jest-config.yml index 2d5a1079cb..76d6fe8a56 100644 --- a/.github/workflows/jest-config.yml +++ b/.github/workflows/jest-config.yml @@ -1,4 +1,4 @@ -name: Jest Config Publish +name: Jest Config on: push: paths: @@ -12,5 +12,7 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/publish with: - package: "./packages/jest-config/package.json" + skip-build: true + workdir: ./packages/jest-config + filter: "@webstudio-is/jest-config" token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/react-sdk.yml b/.github/workflows/react-sdk.yml index 9a122a632b..8bf0dd349e 100644 --- a/.github/workflows/react-sdk.yml +++ b/.github/workflows/react-sdk.yml @@ -1,4 +1,4 @@ -name: SDK publish +name: React SDK on: push: paths: @@ -12,5 +12,6 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/publish with: - package: "./packages/react-sdk/package.json" + workdir: ./packages/react-sdk + filter: "@webstudio-is/react-sdk" token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/tsconfig.yml b/.github/workflows/tsconfig.yml index f9b6ed774e..276926a981 100644 --- a/.github/workflows/tsconfig.yml +++ b/.github/workflows/tsconfig.yml @@ -1,4 +1,4 @@ -name: Tsconfig Publish +name: Tsconfig on: push: paths: @@ -12,5 +12,7 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/publish with: - package: "./packages/tsconfig/package.json" + skip-build: true + workdir: ./packages/tsconfig + filter: "@webstudio-is/tsconfig" token: ${{ secrets.NPM_TOKEN }} diff --git a/apps/designer/app/canvas/canvas.tsx b/apps/designer/app/canvas/canvas.tsx index 9193e3ad60..9d18c7b7a8 100644 --- a/apps/designer/app/canvas/canvas.tsx +++ b/apps/designer/app/canvas/canvas.tsx @@ -7,9 +7,9 @@ import { type Tree, useAllUserProps, globalStyles, - useSubscribe, createElementsTree, } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useShortcuts } from "./shared/use-shortcuts"; import { usePopulateRootInstance, @@ -77,7 +77,7 @@ const useElementsTree = () => { const useSubscribePreviewMode = () => { const [isPreviewMode, setIsPreviewMode] = useState(false); - useSubscribe<"previewMode", boolean>("previewMode", setIsPreviewMode); + useSubscribe("previewMode", setIsPreviewMode); return isPreviewMode; }; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx index 673c6d8651..3e60be6cd3 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx @@ -1,6 +1,5 @@ import { type ChildrenUpdates, - publish, useAllUserProps, type Instance, } from "@webstudio-is/react-sdk"; @@ -10,6 +9,13 @@ import { LexicalComposer, ContentEditable } from "./lexical"; import { TreeViewPlugin } from "./plugins/tree-view-plugin"; import { config } from "./config"; import Editor from "./editor"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + insertInlineInstance: Instance; + } +} type ExampleTextEditorProps = { onChange: (state: ChildrenUpdates) => void; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx index 9b3857f056..addf7c8706 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx @@ -1,5 +1,6 @@ import { useEffect } from "react"; -import { useSubscribe, type Instance } from "@webstudio-is/react-sdk"; +import { type Instance } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { $createInstanceNode, InstanceNode } from "../nodes/node-instance"; import { createCommand, @@ -67,12 +68,9 @@ export const InstancePlugin = ({ children }: InstancePluginProps) => { }); }, [editor, children]); - useSubscribe<"insertInlineInstance", Instance>( - "insertInlineInstance", - (payload) => { - editor.dispatchCommand(INSERT_INSTANCE_COMMAND, payload); - } - ); + useSubscribe("insertInlineInstance", (payload) => { + editor.dispatchCommand(INSERT_INSTANCE_COMMAND, payload); + }); return null; }; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-toolbar-connector.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-toolbar-connector.tsx index 9e25af4341..7687c4af4a 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-toolbar-connector.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-toolbar-connector.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useRef } from "react"; -import { publish } from "@webstudio-is/react-sdk"; import { $isRangeSelection, @@ -8,6 +7,13 @@ import { COMMAND_PRIORITY_LOW, useLexicalComposerContext, } from "../lexical"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + selectionRect?: DOMRect; + } +} export const ToolbarConnectorPlugin = () => { const [editor] = useLexicalComposerContext(); @@ -16,7 +22,7 @@ export const ToolbarConnectorPlugin = () => { const clearSelectionRect = () => { if (lastSelectionRef.current) { // Undefined Rect will hide toolbar - publish<"selectionRect">({ type: "selectionRect", payload: undefined }); + publish({ type: "selectionRect", payload: undefined }); lastSelectionRef.current = undefined; } }; @@ -31,7 +37,7 @@ export const ToolbarConnectorPlugin = () => { if (isTextSelected) { const domRange = nativeSelection.getRangeAt(0); const rect = domRange.getBoundingClientRect(); - publish<"selectionRect", DOMRect>({ + publish({ type: "selectionRect", payload: rect, }); diff --git a/apps/designer/app/canvas/features/wrapper-component/use-css.tsx b/apps/designer/app/canvas/features/wrapper-component/use-css.tsx index 2f6d225d7f..30fd71a772 100644 --- a/apps/designer/app/canvas/features/wrapper-component/use-css.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/use-css.tsx @@ -3,9 +3,9 @@ import { type Instance, css as createCss, type CSS, - useSubscribe, toValue, } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { type StyleUpdates } from "~/shared/canvas-components"; type UseCssProps = { @@ -23,12 +23,9 @@ const usePreviewCss = ({ instance, css }: UseCssProps) => { StyleUpdates["updates"] | UpdatesReset >([]); - useSubscribe( - `previewStyle:${instance.id}`, - ({ updates }) => { - setPreviewCss(updates); - } - ); + useSubscribe(`previewStyle:${instance.id}`, ({ updates }) => { + setPreviewCss(updates); + }); // We are building a map for unsetting the ephemeral values we previously set for the preview useEffect(() => { diff --git a/apps/designer/app/canvas/shared/breakpoints.ts b/apps/designer/app/canvas/shared/breakpoints.ts index a4a70c7c7c..4dce724395 100644 --- a/apps/designer/app/canvas/shared/breakpoints.ts +++ b/apps/designer/app/canvas/shared/breakpoints.ts @@ -1,17 +1,14 @@ import { useEffect, useRef } from "react"; import store from "immerhin"; -import { - type Breakpoint, - useSubscribe, - setBreakpoints, - publish, -} from "@webstudio-is/react-sdk"; +import { type Breakpoint, setBreakpoints } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { deleteCssRulesByBreakpoint } from "~/shared/css-utils"; import { breakpointsContainer, rootInstanceContainer, useBreakpoints, } from "~/shared/nano-states"; +import { publish } from "~/shared/pubsub"; export const useInitializeBreakpoints = (breakpoints: Array) => { const [, setCurrentBreakpoints] = useBreakpoints(); @@ -27,7 +24,7 @@ export const useInitializeBreakpoints = (breakpoints: Array) => { const usePublishBreakpoints = () => { const [breakpoints] = useBreakpoints(); useEffect(() => { - publish<"loadBreakpoints", Array>({ + publish({ type: "loadBreakpoints", payload: breakpoints, }); @@ -35,46 +32,38 @@ const usePublishBreakpoints = () => { }; const useBreakpointChange = () => { - useSubscribe<"breakpointChange", Breakpoint>( - "breakpointChange", - (breakpoint) => { - store.createTransaction([breakpointsContainer], (breakpoints) => { - const foundBreakpoint = breakpoints.find( - ({ id }) => id == breakpoint.id - ); - if (foundBreakpoint) { - foundBreakpoint.label = breakpoint.label; - foundBreakpoint.minWidth = breakpoint.minWidth; - } else { - // Its a new breakpoint - breakpoints.push(breakpoint); - } + useSubscribe("breakpointChange", (breakpoint) => { + store.createTransaction([breakpointsContainer], (breakpoints) => { + const foundBreakpoint = breakpoints.find(({ id }) => id == breakpoint.id); + if (foundBreakpoint) { + foundBreakpoint.label = breakpoint.label; + foundBreakpoint.minWidth = breakpoint.minWidth; + } else { + // Its a new breakpoint + breakpoints.push(breakpoint); + } - setBreakpoints(breakpoints); - }); - } - ); + setBreakpoints(breakpoints); + }); + }); }; const useBreakpointDelete = () => { - useSubscribe<"breakpointDelete", Breakpoint>( - "breakpointDelete", - (breakpoint) => { - store.createTransaction( - [breakpointsContainer, rootInstanceContainer], - (breakpoints, rootInstance) => { - if (rootInstance === undefined) return; + useSubscribe("breakpointDelete", (breakpoint) => { + store.createTransaction( + [breakpointsContainer, rootInstanceContainer], + (breakpoints, rootInstance) => { + if (rootInstance === undefined) return; - const index = breakpoints.findIndex(({ id }) => id == breakpoint.id); - if (index !== -1) { - breakpoints.splice(index, 1); - } - - deleteCssRulesByBreakpoint(rootInstance, breakpoint.id); + const index = breakpoints.findIndex(({ id }) => id == breakpoint.id); + if (index !== -1) { + breakpoints.splice(index, 1); } - ); - } - ); + + deleteCssRulesByBreakpoint(rootInstance, breakpoint.id); + } + ); + }); }; export const useHandleBreakpoints = () => { diff --git a/apps/designer/app/canvas/shared/copy-paste.ts b/apps/designer/app/canvas/shared/copy-paste.ts index f3ac30c9d0..f29ec5cc44 100644 --- a/apps/designer/app/canvas/shared/copy-paste.ts +++ b/apps/designer/app/canvas/shared/copy-paste.ts @@ -2,11 +2,11 @@ import { type InstanceProps, type Instance, allUserPropsContainer, - publish, } from "@webstudio-is/react-sdk"; import { selectedInstanceContainer } from "./nano-states"; import { cloneInstance } from "~/shared/tree-utils"; import { cloneProps } from "~/shared/props-utils"; +import { publish } from "~/shared/pubsub"; let currentInstance: Instance | undefined; let currentProps: InstanceProps | undefined; @@ -23,7 +23,7 @@ export const paste = () => { const props = currentProps ? cloneProps(currentProps, { instanceId: instance.id }) : undefined; - publish<"insertInstance", { instance: Instance; props?: InstanceProps }>({ + publish({ type: "insertInstance", payload: { instance, diff --git a/apps/designer/app/canvas/shared/instance.ts b/apps/designer/app/canvas/shared/instance.ts index f0daa9b802..39a3420059 100644 --- a/apps/designer/app/canvas/shared/instance.ts +++ b/apps/designer/app/canvas/shared/instance.ts @@ -1,16 +1,14 @@ import { useCallback, useEffect, useMemo, useRef } from "react"; import ObjectId from "bson-objectid"; import { - type InstanceProps, type Instance, type Tree, components, allUserPropsContainer, getBrowserStyle, useAllUserProps, - useSubscribe, - publish, } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { deleteInstanceMutable, populateInstance, @@ -40,6 +38,18 @@ import { } from "~/shared/nano-states"; import { useMeasure } from "~/shared/dom-hooks"; import { findInstanceByElement } from "~/shared/dom-utils"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + hoveredInstanceRect: DOMRect; + hoverInstance?: HoveredInstanceData; + loadRootInstance?: Instance; + selectedInstanceRect: DOMRect; + selectInstance?: SelectedInstanceData; + textEditingInstanceId?: Instance["id"]; + } +} export const usePopulateRootInstance = (tree: Tree) => { // @todo ssr workaround for https://github.com/webstudio-is/webstudio-designer/issues/213 @@ -80,14 +90,7 @@ export const findInsertLocation = ( export const useInsertInstance = () => { const [selectedInstance, setSelectedInstance] = useSelectedInstance(); - useSubscribe< - "insertInstance", - { - instance: Instance; - dropTarget?: { parentId: Instance["id"]; position: number }; - props?: InstanceProps; - } - >("insertInstance", ({ instance, dropTarget, props }) => { + useSubscribe("insertInstance", ({ instance, dropTarget, props }) => { store.createTransaction( [rootInstanceContainer, allUserPropsContainer], (rootInstance, allUserProps) => { @@ -112,13 +115,7 @@ export const useInsertInstance = () => { export const useReparentInstance = () => { const [selectedInstance, setSelectedInstance] = useSelectedInstance(); - useSubscribe< - "reparentInstance", - { - instanceId: Instance["id"]; - dropTarget: { instanceId: Instance["id"]; position: number | "end" }; - } - >("reparentInstance", ({ instanceId, dropTarget }) => { + useSubscribe("reparentInstance", ({ instanceId, dropTarget }) => { store.createTransaction([rootInstanceContainer], (rootInstance) => { if (rootInstance === undefined) return; reparentInstanceMutable( @@ -144,40 +141,34 @@ export const useReparentInstance = () => { export const useDeleteInstance = () => { const [rootInstance] = useRootInstance(); const [selectedInstance, setSelectedInstance] = useSelectedInstance(); - useSubscribe<"deleteInstance", { id: Instance["id"] }>( - "deleteInstance", - ({ id }) => { - if (rootInstance !== undefined && selectedInstance !== undefined) { - // @todo tell user they can't delete root - if (id === rootInstance.id) { - return; - } + useSubscribe("deleteInstance", ({ id }) => { + if (rootInstance !== undefined && selectedInstance !== undefined) { + // @todo tell user they can't delete root + if (id === rootInstance.id) { + return; + } - const parentInstance = findParentInstance(rootInstance, id); - if (parentInstance !== undefined) { - const siblingInstance = findClosestSiblingInstance( - parentInstance, - id - ); - setSelectedInstance(siblingInstance || parentInstance); - } + const parentInstance = findParentInstance(rootInstance, id); + if (parentInstance !== undefined) { + const siblingInstance = findClosestSiblingInstance(parentInstance, id); + setSelectedInstance(siblingInstance || parentInstance); } - // @todo deleting instance should involve also deleting it's props - // If we don't delete them - they just live both on client and db - // Pros: - // - if we undo the deletion we don't need to undo the props deletion - // - in a multiplayer environment, some other user could have changed a prop while we have deleted the instance - // and then if we restore the instance, we would be restoring it with our props, potentially overwriting other users changes - // The way it is now it will actually still enable parallel deletion props editing and restoration. - // Contra: we are piling them up. - // Potentially we could also solve this by periodically removing unused props after while when instance was deleted - store.createTransaction([rootInstanceContainer], (rootInstance) => { - if (rootInstance !== undefined) { - deleteInstanceMutable(rootInstance, id); - } - }); } - ); + // @todo deleting instance should involve also deleting it's props + // If we don't delete them - they just live both on client and db + // Pros: + // - if we undo the deletion we don't need to undo the props deletion + // - in a multiplayer environment, some other user could have changed a prop while we have deleted the instance + // and then if we restore the instance, we would be restoring it with our props, potentially overwriting other users changes + // The way it is now it will actually still enable parallel deletion props editing and restoration. + // Contra: we are piling them up. + // Potentially we could also solve this by periodically removing unused props after while when instance was deleted + store.createTransaction([rootInstanceContainer], (rootInstance) => { + if (rootInstance !== undefined) { + deleteInstanceMutable(rootInstance, id); + } + }); + }); }; export const usePublishSelectedInstanceData = (treeId: Tree["id"]) => { @@ -210,7 +201,7 @@ export const usePublishSelectedInstanceData = (treeId: Tree["id"]) => { props, }; } - publish<"selectInstance", SelectedInstanceData>({ + publish({ type: "selectInstance", payload, }); @@ -227,7 +218,7 @@ export const usePublishHoveredInstanceData = () => { component: instance.component, } : undefined; - publish<"hoverInstance", HoveredInstanceData | undefined>({ + publish({ type: "hoverInstance", payload, }); @@ -237,7 +228,7 @@ export const usePublishHoveredInstanceData = () => { export const usePublishRootInstance = () => { const [rootInstance] = useRootInstance(); useEffect(() => { - publish<"loadRootInstance", Instance>({ + publish({ type: "loadRootInstance", payload: rootInstance, }); @@ -245,7 +236,7 @@ export const usePublishRootInstance = () => { }; const publishRect = (rect: DOMRect) => { - publish<"selectedInstanceRect", DOMRect>({ + publish({ type: "selectedInstanceRect", payload: rect, }); @@ -269,7 +260,7 @@ export const usePublishHoveredInstanceRect = () => { const [element] = useHoveredElement(); const publishRect = useCallback(() => { if (element === undefined) return; - publish<"hoveredInstanceRect", DOMRect>({ + publish({ type: "hoveredInstanceRect", payload: element.getBoundingClientRect(), }); @@ -321,7 +312,7 @@ export const useUnselectInstance = () => { export const usePublishTextEditingInstanceId = () => { const [editingInstanceId] = useTextEditingInstanceId(); useEffect(() => { - publish<"textEditingInstanceId", Instance["id"] | undefined>({ + publish({ type: "textEditingInstanceId", payload: editingInstanceId, }); diff --git a/apps/designer/app/canvas/shared/props.ts b/apps/designer/app/canvas/shared/props.ts index 1d1fd6e926..d011a1928d 100644 --- a/apps/designer/app/canvas/shared/props.ts +++ b/apps/designer/app/canvas/shared/props.ts @@ -1,9 +1,5 @@ -import { - type UserPropsUpdates, - useSubscribe, - allUserPropsContainer, - DeleteProp, -} from "@webstudio-is/react-sdk"; +import { allUserPropsContainer } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import store from "immerhin"; import { updateAllUserPropsMutable, @@ -11,16 +7,13 @@ import { } from "~/shared/props-utils"; export const useManageProps = () => { - useSubscribe<"updateProps", UserPropsUpdates>( - "updateProps", - (userPropsUpdates) => { - store.createTransaction([allUserPropsContainer], (allUserProps) => { - updateAllUserPropsMutable(allUserProps, userPropsUpdates); - }); - } - ); + useSubscribe("updateProps", (userPropsUpdates) => { + store.createTransaction([allUserPropsContainer], (allUserProps) => { + updateAllUserPropsMutable(allUserProps, userPropsUpdates); + }); + }); - useSubscribe<"deleteProp", DeleteProp>("deleteProp", (deleteProp) => { + useSubscribe("deleteProp", (deleteProp) => { store.createTransaction([allUserPropsContainer], (allUserProps) => { deletePropMutable(allUserProps, deleteProp); }); diff --git a/apps/designer/app/canvas/shared/style.ts b/apps/designer/app/canvas/shared/style.ts index 5fe49e1da4..013e0d99b8 100644 --- a/apps/designer/app/canvas/shared/style.ts +++ b/apps/designer/app/canvas/shared/style.ts @@ -1,25 +1,21 @@ import store from "immerhin"; -import { useSubscribe } from "@webstudio-is/react-sdk"; -import { type StyleUpdates } from "~/shared/canvas-components"; +import { useSubscribe } from "~/shared/pubsub"; import { setInstanceStyleMutable } from "~/shared/tree-utils"; import { useSelectedInstance } from "./nano-states"; import { rootInstanceContainer } from "~/shared/nano-states"; export const useUpdateStyle = () => { const [selectedInstance] = useSelectedInstance(); - useSubscribe<"updateStyle", StyleUpdates>( - "updateStyle", - ({ id, updates, breakpoint }) => { - // Only update styles if they match the selected instance - // It can potentially happen that we selected a difference instance right after we changed the style in style panel. - if (id !== selectedInstance?.id) return; + useSubscribe("updateStyle", ({ id, updates, breakpoint }) => { + // Only update styles if they match the selected instance + // It can potentially happen that we selected a difference instance right after we changed the style in style panel. + if (id !== selectedInstance?.id) return; - store.createTransaction([rootInstanceContainer], (rootInstance) => { - if (rootInstance === undefined) { - return; - } - setInstanceStyleMutable(rootInstance, id, updates, breakpoint); - }); - } - ); + store.createTransaction([rootInstanceContainer], (rootInstance) => { + if (rootInstance === undefined) { + return; + } + setInstanceStyleMutable(rootInstance, id, updates, breakpoint); + }); + }); }; diff --git a/apps/designer/app/canvas/shared/sync/queue.ts b/apps/designer/app/canvas/shared/sync/queue.ts index b39b94e1b7..684f371c15 100644 --- a/apps/designer/app/canvas/shared/sync/queue.ts +++ b/apps/designer/app/canvas/shared/sync/queue.ts @@ -1,5 +1,11 @@ import { type SyncStatus } from "~/shared/sync"; -import { publish } from "@webstudio-is/react-sdk"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + syncStatus: SyncStatus; + } +} // @todo because of limitation with nested models and saving tree as a Json type // we load that entire Json to manipulate and if a second request happens before the first one has written @@ -22,13 +28,13 @@ const dequeue = () => { const job = queue.shift(); if (job) { isInProgress = true; - publish<"syncStatus", SyncStatus>({ + publish({ type: "syncStatus", payload: "syncing", }); job().finally(() => { isInProgress = false; - publish<"syncStatus", SyncStatus>({ + publish({ type: "syncStatus", payload: "idle", }); diff --git a/apps/designer/app/canvas/shared/use-drag-drop.ts b/apps/designer/app/canvas/shared/use-drag-drop.ts index 346966cfdc..b178ac4285 100644 --- a/apps/designer/app/canvas/shared/use-drag-drop.ts +++ b/apps/designer/app/canvas/shared/use-drag-drop.ts @@ -21,13 +21,21 @@ import { useDrop, } from "@webstudio-is/design-system"; import { - publish, - useSubscribe, type Instance, components, BaseInstance, toBaseInstance, } from "@webstudio-is/react-sdk"; +import { publish, useSubscribe } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + dragEnd: DragEndPayload; + dragMove: DragMovePayload; + dragStart: DragStartPayload; + dropTargetChange: DropTargetChangePayload; + } +} export type DropTargetChangePayload = { rect: DropTarget["rect"]; @@ -48,9 +56,12 @@ export type DragEndPayload = { export type DragMovePayload = { canvasCoordinates: Point }; -const initialState = { - dropTarget: undefined as DropTarget | undefined, - dragItem: undefined as BaseInstance | undefined, +const initialState: { + dropTarget: DropTarget | undefined; + dragItem: BaseInstance | undefined; +} = { + dropTarget: undefined, + dragItem: undefined, }; export const useDragAndDrop = () => { @@ -131,7 +142,7 @@ export const useDragAndDrop = () => { onDropTargetChange(dropTarget) { state.current.dropTarget = dropTarget; - publish<"dropTargetChange", DropTargetChangePayload>({ + publish({ type: "dropTargetChange", payload: { rect: dropTarget.rect, @@ -194,7 +205,7 @@ export const useDragAndDrop = () => { autoScrollHandlers.setEnabled(true); dropHandlers.handleStart(); - publish<"dragStart", DragStartPayload>({ + publish({ type: "dragStart", payload: { origin: "canvas", @@ -210,7 +221,7 @@ export const useDragAndDrop = () => { dropHandlers.handleEnd({ isCanceled }); autoScrollHandlers.setEnabled(false); - publish<"dragEnd", DragEndPayload>({ + publish({ type: "dragEnd", payload: { origin: "canvas", isCanceled }, }); @@ -218,13 +229,7 @@ export const useDragAndDrop = () => { const { dropTarget, dragItem } = state.current; if (dropTarget && dragItem && isCanceled === false) { - publish< - "reparentInstance", - { - instanceId: Instance["id"]; - dropTarget: { instanceId: Instance["id"]; position: number }; - } - >({ + publish({ type: "reparentInstance", payload: { instanceId: dragItem.id, @@ -263,55 +268,40 @@ export const useDragAndDrop = () => { // Handle drag from the panel // ================================================================ - useSubscribe<"dragStart", DragStartPayload>( - "dragStart", - ({ origin, dragItem }) => { - if (origin === "panel") { - state.current.dragItem = dragItem; - autoScrollHandlers.setEnabled(true); - dropHandlers.handleStart(); - } + useSubscribe("dragStart", ({ origin, dragItem }) => { + if (origin === "panel") { + state.current.dragItem = dragItem; + autoScrollHandlers.setEnabled(true); + dropHandlers.handleStart(); } - ); + }); - useSubscribe<"dragMove", DragMovePayload>( - "dragMove", - ({ canvasCoordinates }) => { - dropHandlers.handleMove(canvasCoordinates); - autoScrollHandlers.handleMove(canvasCoordinates); - } - ); + useSubscribe("dragMove", ({ canvasCoordinates }) => { + dropHandlers.handleMove(canvasCoordinates); + autoScrollHandlers.handleMove(canvasCoordinates); + }); - useSubscribe<"dragEnd", DragEndPayload>( - "dragEnd", - ({ origin, isCanceled }) => { - if (origin === "panel") { - dropHandlers.handleEnd({ isCanceled }); - autoScrollHandlers.setEnabled(false); + useSubscribe("dragEnd", ({ origin, isCanceled }) => { + if (origin === "panel") { + dropHandlers.handleEnd({ isCanceled }); + autoScrollHandlers.setEnabled(false); - const { dropTarget, dragItem } = state.current; + const { dropTarget, dragItem } = state.current; - if (dropTarget && dragItem && isCanceled === false) { - publish< - "insertInstance", - { - instance: Instance; - dropTarget: { parentId: Instance["id"]; position: number }; - } - >({ - type: "insertInstance", - payload: { - instance: createInstance({ component: dragItem.component }), - dropTarget: { - parentId: dropTarget.data.id, - position: dropTarget.indexWithinChildren, - }, + if (dropTarget && dragItem && isCanceled === false) { + publish({ + type: "insertInstance", + payload: { + instance: createInstance({ component: dragItem.component }), + dropTarget: { + parentId: dropTarget.data.id, + position: dropTarget.indexWithinChildren, }, - }); - } - - state.current = { ...initialState }; + }, + }); } + + state.current = { ...initialState }; } - ); + }); }; diff --git a/apps/designer/app/canvas/shared/use-publish-scroll-state.ts b/apps/designer/app/canvas/shared/use-publish-scroll-state.ts index 74da482fe9..330acd8dd1 100644 --- a/apps/designer/app/canvas/shared/use-publish-scroll-state.ts +++ b/apps/designer/app/canvas/shared/use-publish-scroll-state.ts @@ -1,13 +1,19 @@ -import { publish } from "@webstudio-is/react-sdk"; import { useScrollState } from "~/shared/dom-hooks"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + scrollState: boolean; + } +} export const usePublishScrollState = () => { useScrollState({ onScrollStart() { - publish<"scrollState", boolean>({ type: "scrollState", payload: true }); + publish({ type: "scrollState", payload: true }); }, onScrollEnd() { - publish<"scrollState", boolean>({ + publish({ type: "scrollState", payload: false, }); diff --git a/apps/designer/app/canvas/shared/use-shortcuts.ts b/apps/designer/app/canvas/shared/use-shortcuts.ts index b41f1a83e7..31a5823a1d 100644 --- a/apps/designer/app/canvas/shared/use-shortcuts.ts +++ b/apps/designer/app/canvas/shared/use-shortcuts.ts @@ -1,35 +1,56 @@ import { useHotkeys } from "react-hotkeys-hook"; import store from "immerhin"; -import { - type Instance, - publish, - useSubscribe, - components, -} from "@webstudio-is/react-sdk"; +import { type Instance, components } from "@webstudio-is/react-sdk"; import { shortcuts, options } from "~/shared/shortcuts"; +import { publish, useSubscribe } from "~/shared/pubsub"; import { useSelectedInstance } from "./nano-states"; import { copy, paste } from "./copy-paste"; import { useTextEditingInstanceId } from "~/shared/nano-states"; +import { type SelectedInstanceData } from "~/shared/canvas-components"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + cancelCurrentDrag: undefined; + deleteInstance: { + id: Instance["id"]; + }; + openBreakpointsMenu: undefined; + selectBreakpointFromShortcut: number; + selectInstance?: SelectedInstanceData; + togglePreviewMode: undefined; + zoom: "zoomOut" | "zoomIn"; + } +} const inputTags = ["INPUT", "SELECT", "TEXTAREA"] as const; type HandlerEvent = { - key: string; + key?: string; preventDefault?: () => void; }; const togglePreviewMode = () => { - publish<"togglePreviewMode">({ type: "togglePreviewMode" }); + publish({ type: "togglePreviewMode" }); }; const publishSelectBreakpoint = ({ key }: HandlerEvent) => { + if (!key) { + throw new Error( + "`publishSelectBreakpoint` doesn't account for being called without a `key`" + ); + } publish({ type: "selectBreakpointFromShortcut", - payload: key, + payload: parseInt(key, 10), }); }; const publishZoom = (event: HandlerEvent) => { + if (!event.key) { + throw new Error( + "`publishZoom` doesn't account for being called without a `key`" + ); + } if (event.preventDefault !== undefined) event.preventDefault(); publish({ type: "zoom", @@ -53,7 +74,7 @@ export const useShortcuts = () => { if (selectedInstance === undefined) { return; } - publish<"deleteInstance", { id: Instance["id"] }>({ + publish({ type: "deleteInstance", payload: { id: selectedInstance.id }, }); @@ -89,7 +110,7 @@ export const useShortcuts = () => { return; } setSelectedInstance(undefined); - publish<"selectInstance">({ type: "selectInstance" }); + publish({ type: "selectInstance" }); }, { ...options, enableOnContentEditable: true, enableOnTags: [...inputTags] }, [selectedInstance, editingInstanceId] @@ -133,10 +154,7 @@ export const useShortcuts = () => { useHotkeys(shortcuts.esc, shortcutHandlerMap.esc, options, []); // Shortcuts from the parent window - useSubscribe<"shortcut", { name: keyof typeof shortcuts; key: string }>( - "shortcut", - ({ name, key }) => { - shortcutHandlerMap[name]({ key }); - } - ); + useSubscribe("shortcut", ({ name, key }) => { + shortcutHandlerMap[name]({ key }); + }); }; diff --git a/apps/designer/app/canvas/shared/use-track-selected-element.ts b/apps/designer/app/canvas/shared/use-track-selected-element.ts index 6ae40c31ec..d76f255ad4 100644 --- a/apps/designer/app/canvas/shared/use-track-selected-element.ts +++ b/apps/designer/app/canvas/shared/use-track-selected-element.ts @@ -1,10 +1,6 @@ import { useCallback, useEffect, useRef } from "react"; -import { - type Instance, - publish, - useSubscribe, - components, -} from "@webstudio-is/react-sdk"; +import { type Instance, components } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useSelectedElement, useSelectedInstance } from "./nano-states"; import { useRootInstance, @@ -18,6 +14,13 @@ import { getInstanceElementById, getInstanceIdFromElement, } from "~/shared/dom-utils"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + clickCanvas: undefined; + } +} const eventOptions = { passive: true, @@ -39,10 +42,7 @@ export const useTrackSelectedElement = () => { [setSelectedInstance, rootInstance] ); - useSubscribe<"selectInstanceById", Instance["id"]>( - "selectInstanceById", - selectInstance - ); + useSubscribe("selectInstanceById", selectInstance); // Focus and select the element when selected instance changes useEffect(() => { @@ -70,7 +70,7 @@ export const useTrackSelectedElement = () => { const handleClick = (event: MouseEvent) => { // Notify in general that document was clicked // e.g. to hide the side panel - publish<"clickCanvas">({ type: "clickCanvas" }); + publish({ type: "clickCanvas" }); let element = event.target as HTMLElement; // If we click on an element that is not a component, we search for a parent component. diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 426ec7fa36..586a7218ef 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -5,10 +5,6 @@ import { type Publish, } from "@webstudio-is/react-sdk"; import type { Config } from "~/config"; -import type { - HoveredInstanceData, - SelectedInstanceData, -} from "~/shared/canvas-components"; import { Box, Flex, Grid, type CSS } from "@webstudio-is/design-system"; import interStyles from "~/shared/font-faces/inter.css"; import { SidebarLeft } from "./features/sidebar-left"; @@ -32,7 +28,6 @@ import { CanvasIframe, } from "./features/workspace"; import { usePublishShortcuts } from "./shared/shortcuts"; -import { type SyncStatus } from "~/shared/sync"; import { useIsPreviewMode, useRootInstance, @@ -52,25 +47,22 @@ export const links = () => { const useSubscribeRootInstance = () => { const [, setValue] = useRootInstance(); - useSubscribe<"loadRootInstance">("loadRootInstance", setValue); + useSubscribe("loadRootInstance", setValue); }; const useSubscribeSelectedInstanceData = () => { const [, setValue] = useSelectedInstanceData(); - useSubscribe<"selectInstance", SelectedInstanceData>( - "selectInstance", - setValue - ); + useSubscribe("selectInstance", setValue); }; const useSubscribeHoveredInstanceData = () => { const [, setValue] = useHoveredInstanceData(); - useSubscribe<"hoverInstance", HoveredInstanceData>("hoverInstance", setValue); + useSubscribe("hoverInstance", setValue); }; const useSubscribeSyncStatus = () => { const [, setValue] = useSyncStatus(); - useSubscribe<"syncStatus", SyncStatus>("syncStatus", setValue); + useSubscribe("syncStatus", setValue); }; const useNavigatorLayout = () => { diff --git a/apps/designer/app/designer/features/breadcrumbs/breadcrumbs.tsx b/apps/designer/app/designer/features/breadcrumbs/breadcrumbs.tsx index 7cd7205792..ac2321e4b4 100644 --- a/apps/designer/app/designer/features/breadcrumbs/breadcrumbs.tsx +++ b/apps/designer/app/designer/features/breadcrumbs/breadcrumbs.tsx @@ -6,9 +6,16 @@ import { darkTheme, } from "@webstudio-is/design-system"; import { useSelectedInstancePath } from "../../shared/instance/use-selected-instance-path"; -import { type Publish, type Instance } from "@webstudio-is/react-sdk"; +import { type Instance } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { useSelectedInstanceData } from "~/designer/shared/nano-states"; +declare module "~/shared/pubsub" { + export interface PubsubMap { + selectInstanceById: Instance["id"]; + } +} + type BreadcrumbProps = { component: Instance["component"]; onClick: () => void; @@ -73,7 +80,7 @@ export const Breadcrumbs = ({ publish }: BreadcrumbsProps) => { key={instance.id} component={instance.component} onClick={() => { - publish<"selectInstanceById", Instance["id"]>({ + publish({ type: "selectInstanceById", payload: instance.id, }); diff --git a/apps/designer/app/designer/features/breakpoints/breakpoints-editor.tsx b/apps/designer/app/designer/features/breakpoints/breakpoints-editor.tsx index 08c1506b6b..096f62e2c8 100644 --- a/apps/designer/app/designer/features/breakpoints/breakpoints-editor.tsx +++ b/apps/designer/app/designer/features/breakpoints/breakpoints-editor.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import useDebounce from "react-use/lib/useDebounce"; -import { type Breakpoint, type Publish } from "@webstudio-is/react-sdk"; +import { type Breakpoint } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { Button, TextField, diff --git a/apps/designer/app/designer/features/breakpoints/breakpoints.tsx b/apps/designer/app/designer/features/breakpoints/breakpoints.tsx index cb0e452537..8e9dbc6c0d 100644 --- a/apps/designer/app/designer/features/breakpoints/breakpoints.tsx +++ b/apps/designer/app/designer/features/breakpoints/breakpoints.tsx @@ -1,9 +1,6 @@ import { useEffect, useState } from "react"; -import { - useSubscribe, - type Breakpoint, - type Publish, -} from "@webstudio-is/react-sdk"; +import { type Breakpoint } from "@webstudio-is/react-sdk"; +import { useSubscribe, type Publish } from "~/shared/pubsub"; import { DropdownMenu, DropdownMenuContent, @@ -29,6 +26,14 @@ import { import { ConfirmationDialog } from "./confirmation-dialog"; import { useBreakpoints } from "~/shared/nano-states"; +declare module "~/shared/pubsub" { + export interface PubsubMap { + breakpointDelete: Breakpoint; + breakpointChange: Breakpoint; + loadBreakpoints: Array; + } +} + type BreakpointSelectorItemProps = { breakpoint: Breakpoint; }; diff --git a/apps/designer/app/designer/features/breakpoints/use-subscribe-breakpoints.ts b/apps/designer/app/designer/features/breakpoints/use-subscribe-breakpoints.ts index 7501c9d9f5..d0ab8374ed 100644 --- a/apps/designer/app/designer/features/breakpoints/use-subscribe-breakpoints.ts +++ b/apps/designer/app/designer/features/breakpoints/use-subscribe-breakpoints.ts @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { Breakpoint, useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useSelectedBreakpoint } from "~/designer/shared/nano-states"; import { sort } from "~/shared/breakpoints"; import { useBreakpoints } from "~/shared/nano-states"; @@ -8,10 +8,7 @@ export const useSubscribeBreakpoints = () => { const [breakpoints, setBreakpoints] = useBreakpoints(); const [selectedBreakpoint, setSelectedBreakpoint] = useSelectedBreakpoint(); - useSubscribe<"loadBreakpoints", Array>( - "loadBreakpoints", - setBreakpoints - ); + useSubscribe("loadBreakpoints", setBreakpoints); useEffect(() => { // Set the initial selected breakpoint diff --git a/apps/designer/app/designer/features/breakpoints/use-subscribe-shortcuts.ts b/apps/designer/app/designer/features/breakpoints/use-subscribe-shortcuts.ts index 61ea3615b3..517d479434 100644 --- a/apps/designer/app/designer/features/breakpoints/use-subscribe-shortcuts.ts +++ b/apps/designer/app/designer/features/breakpoints/use-subscribe-shortcuts.ts @@ -1,4 +1,4 @@ -import { useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { sort } from "~/shared/breakpoints"; import { useBreakpoints } from "~/shared/nano-states"; import { useZoom, useSelectedBreakpoint } from "../../shared/nano-states"; @@ -7,20 +7,17 @@ import { minZoom } from "./zoom-setting"; export const useSubscribeSelectBreakpointFromShortcut = () => { const [breakpoints] = useBreakpoints(); const [, setSelectedBreakpoint] = useSelectedBreakpoint(); - useSubscribe<"selectBreakpointFromShortcut", number>( - "selectBreakpointFromShortcut", - (index) => { - const breakpoint = sort(breakpoints)[index - 1]; - if (breakpoint) setSelectedBreakpoint(breakpoint); - } - ); + useSubscribe("selectBreakpointFromShortcut", (index) => { + const breakpoint = sort(breakpoints)[index - 1]; + if (breakpoint) setSelectedBreakpoint(breakpoint); + }); }; const zoomStep = 20; export const useSubscribeZoomFromShortcut = () => { const [zoom, setZoom] = useZoom(); - useSubscribe<"zoom", "zoomIn" | "zoomOut">("zoom", (direction) => { + useSubscribe("zoom", (direction) => { if (direction === "zoomIn") { setZoom(Math.min(zoom + zoomStep, 100)); return; diff --git a/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts b/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts index e35ec8c238..24d9f98b0f 100644 --- a/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts +++ b/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts @@ -1,4 +1,4 @@ -import { useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useCallback, useEffect } from "react"; import { useSelectedBreakpoint, diff --git a/apps/designer/app/designer/features/inspector/inspector.tsx b/apps/designer/app/designer/features/inspector/inspector.tsx index 4eaa2e8ba0..e9908617b7 100644 --- a/apps/designer/app/designer/features/inspector/inspector.tsx +++ b/apps/designer/app/designer/features/inspector/inspector.tsx @@ -1,4 +1,4 @@ -import type { Publish } from "@webstudio-is/react-sdk"; +import type { Publish } from "~/shared/pubsub"; import { __DEPRECATED__Text, Tabs, diff --git a/apps/designer/app/designer/features/props-panel/props-panel.tsx b/apps/designer/app/designer/features/props-panel/props-panel.tsx index b2f2c1c925..90fbe0446c 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.tsx @@ -1,9 +1,9 @@ import { componentsMeta, type Instance, - type Publish, type UserProp, } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { Control } from "./control"; import { CollapsibleSection, ComponentInfo } from "~/designer/shared/inspector"; import type { SelectedInstanceData } from "~/shared/canvas-components"; diff --git a/apps/designer/app/designer/features/props-panel/use-props-logic.ts b/apps/designer/app/designer/features/props-panel/use-props-logic.ts index 886cc951e2..6a42d79758 100644 --- a/apps/designer/app/designer/features/props-panel/use-props-logic.ts +++ b/apps/designer/app/designer/features/props-panel/use-props-logic.ts @@ -1,9 +1,9 @@ import type { DeleteProp, - Publish, UserProp, UserPropsUpdates, } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { componentsMeta } from "@webstudio-is/react-sdk"; import ObjectId from "bson-objectid"; import produce from "immer"; @@ -12,6 +12,13 @@ import debounce from "lodash/debounce"; import { useRef, useState } from "react"; import type { SelectedInstanceData } from "~/shared/canvas-components"; +declare module "~/shared/pubsub" { + export interface PubsubMap { + deleteProp: DeleteProp; + updateProps: UserPropsUpdates; + } +} + export type handleChangePropType = ( id: UserProp["id"], field: "prop" | "value", @@ -87,7 +94,7 @@ export const usePropsLogic = ({ // @todo this may call the last callback after unmount // @todo we don't need to debounce change events on select for instance, so debounce is only needed on text inputs const updateProps = debounce((updates: UserPropsUpdates["updates"]) => { - publish<"updateProps", UserPropsUpdates>({ + publish({ type: "updateProps", payload: { treeId: selectedInstanceData.props.treeId, @@ -102,7 +109,7 @@ export const usePropsLogic = ({ }, 100); const deleteProp = (id: UserProp["id"]) => { - publish<"deleteProp", DeleteProp>({ + publish({ type: "deleteProp", payload: { instanceId: selectedInstanceData.id, diff --git a/apps/designer/app/designer/features/sidebar-left/lib/navigator/navigator.tsx b/apps/designer/app/designer/features/sidebar-left/lib/navigator/navigator.tsx index 2bfea63e17..c4431c40e9 100644 --- a/apps/designer/app/designer/features/sidebar-left/lib/navigator/navigator.tsx +++ b/apps/designer/app/designer/features/sidebar-left/lib/navigator/navigator.tsx @@ -1,11 +1,23 @@ import { Flex } from "@webstudio-is/design-system"; -import { type Instance, type Publish } from "@webstudio-is/react-sdk"; +import type { Instance } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { useCallback } from "react"; import { useSelectedInstanceData } from "~/designer/shared/nano-states"; import { useRootInstance } from "~/shared/nano-states"; import { Header } from "../header"; import { InstanceTree } from "~/designer/shared/tree"; +declare module "~/shared/pubsub" { + export interface PubsubMap { + selectInstanceById: Instance["id"]; + reparentInstance: { + instanceId: Instance["id"]; + dropTarget: { instanceId: Instance["id"]; position: number | "end" }; + }; + deleteInstance: { id: Instance["id"] }; + } +} + type NavigatorProps = { publish: Publish; isClosable?: boolean; @@ -18,7 +30,7 @@ export const Navigator = ({ publish, isClosable, onClose }: NavigatorProps) => { const handleSelect = useCallback( (instanceId: Instance["id"]) => { - publish<"selectInstanceById", Instance["id"]>({ + publish({ type: "selectInstanceById", payload: instanceId, }); @@ -26,16 +38,12 @@ export const Navigator = ({ publish, isClosable, onClose }: NavigatorProps) => { [publish] ); - type ReparentInstancePayload = { - instanceId: Instance["id"]; - dropTarget: { instanceId: Instance["id"]; position: number | "end" }; - }; const handleDragEnd = useCallback( (payload: { itemId: string; dropTarget: { itemId: string; position: number | "end" }; }) => { - publish<"reparentInstance", ReparentInstancePayload>({ + publish({ type: "reparentInstance", payload: { instanceId: payload.itemId, @@ -51,7 +59,7 @@ export const Navigator = ({ publish, isClosable, onClose }: NavigatorProps) => { const handleDelete = useCallback( (instanceId: Instance["id"]) => { - publish<"deleteInstance", { id: Instance["id"] }>({ + publish({ type: "deleteInstance", payload: { id: instanceId }, }); diff --git a/apps/designer/app/designer/features/sidebar-left/panels/components/components.tsx b/apps/designer/app/designer/features/sidebar-left/panels/components/components.tsx index cebe05f318..e273be4873 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/components/components.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/components/components.tsx @@ -1,11 +1,7 @@ import { type MouseEventHandler, useState } from "react"; import { createPortal } from "react-dom"; -import { - type Instance, - type Publish, - components, - useSubscribe, -} from "@webstudio-is/react-sdk"; +import { type Instance, components } from "@webstudio-is/react-sdk"; +import { useSubscribe, type Publish } from "~/shared/pubsub"; import { Flex } from "@webstudio-is/design-system"; import { useDrag, type Point } from "@webstudio-is/design-system"; import { PlusIcon } from "@webstudio-is/icons"; @@ -13,11 +9,6 @@ import { createInstance } from "~/shared/tree-utils"; import type { TabName } from "../../types"; import { ComponentThumb } from "./component-thumb"; import { useCanvasRect, useZoom } from "~/designer/shared/nano-states"; -import { - DragEndPayload, - DragMovePayload, - DragStartPayload, -} from "~/canvas/shared/use-drag-drop"; import { Header } from "../../lib/header"; const componentNames = ( @@ -112,7 +103,7 @@ export const TabContent = ({ publish, onSetActiveTab }: TabContentProps) => { }, onStart({ data: componentName }) { setDragComponent(componentName); - publish<"dragStart", DragStartPayload>({ + publish({ type: "dragStart", payload: { origin: "panel", @@ -122,14 +113,14 @@ export const TabContent = ({ publish, onSetActiveTab }: TabContentProps) => { }, onMove: (point) => { setPoint(point); - publish<"dragMove", DragMovePayload>({ + publish({ type: "dragMove", payload: { canvasCoordinates: toCanvasCoordinates(point) }, }); }, onEnd({ isCanceled }) { setDragComponent(undefined); - publish<"dragEnd", DragEndPayload>({ + publish({ type: "dragEnd", payload: { origin: "panel", isCanceled }, }); @@ -160,7 +151,7 @@ export const TabContent = ({ publish, onSetActiveTab }: TabContentProps) => { component={component} onClick={() => { onSetActiveTab("none"); - publish<"insertInstance", { instance: Instance }>({ + publish({ type: "insertInstance", payload: { instance: createInstance({ component }) }, }); diff --git a/apps/designer/app/designer/features/sidebar-left/panels/navigator/navigator.tsx b/apps/designer/app/designer/features/sidebar-left/panels/navigator/navigator.tsx index c4ac131963..ef720b7b3a 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/navigator/navigator.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/navigator/navigator.tsx @@ -1,4 +1,4 @@ -import { type Publish } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { ListNestedIcon } from "@webstudio-is/icons"; import { Navigator } from "../../lib/navigator"; import { TabName } from "../../types"; diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx index 802041fc55..3dcd139440 100644 --- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx +++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useSubscribe, type Publish } from "@webstudio-is/react-sdk"; +import { useSubscribe, type Publish } from "~/shared/pubsub"; import type { Asset } from "@webstudio-is/prisma-client"; import { Box, @@ -28,10 +28,10 @@ export const SidebarLeft = ({ publish, assets }: SidebarLeftProps) => { const { TabContent } = activeTab === "none" ? none : panels[activeTab]; const [clientSettings] = useClientSettings(); - useSubscribe<"clickCanvas">("clickCanvas", () => { + useSubscribe("clickCanvas", () => { setActiveTab("none"); }); - useSubscribe<"dragEnd">("dragEnd", () => { + useSubscribe("dragEnd", () => { setActiveTab("none"); }); diff --git a/apps/designer/app/designer/features/style-panel/style-panel.tsx b/apps/designer/app/designer/features/style-panel/style-panel.tsx index d56d456e01..820b8a7c12 100644 --- a/apps/designer/app/designer/features/style-panel/style-panel.tsx +++ b/apps/designer/app/designer/features/style-panel/style-panel.tsx @@ -1,4 +1,4 @@ -import type { Publish } from "@webstudio-is/react-sdk"; +import type { Publish } from "~/shared/pubsub"; import { willRender } from "~/designer/shared/breakpoints"; import { Box, Card, Paragraph } from "@webstudio-is/design-system"; import type { SelectedInstanceData } from "~/shared/canvas-components"; diff --git a/apps/designer/app/designer/features/style-panel/use-style-data.ts b/apps/designer/app/designer/features/style-panel/use-style-data.ts index 552b10bcbe..a25be8d3ec 100644 --- a/apps/designer/app/designer/features/style-panel/use-style-data.ts +++ b/apps/designer/app/designer/features/style-panel/use-style-data.ts @@ -3,13 +3,21 @@ import type { SelectedInstanceData, StyleUpdates, } from "~/shared/canvas-components"; -import { type StyleProperty, type Publish } from "@webstudio-is/react-sdk"; +import { Instance, type StyleProperty } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { useSelectedBreakpoint } from "../../shared/nano-states"; import { parseCssValue } from "./parse-css-value"; import { getInheritedStyle } from "./get-inherited-style"; import { getCssRuleForBreakpoint } from "./lib/utils/get-css-rule-for-breakpoint"; import { useRootInstance } from "~/shared/nano-states"; +declare module "~/shared/pubsub" { + export interface PubsubMap { + updateStyle: StyleUpdates; + [key: `previewStyle:${Instance["id"]}`]: StyleUpdates; + } +} + type UseStyleData = { publish: Publish; selectedInstanceData?: SelectedInstanceData; @@ -73,11 +81,11 @@ export const useStyleData = ({ ) { return; } - publish({ + publish({ type: type === "update" ? "updateStyle" - : `previewStyle:${selectedInstanceData.id}`, + : (`previewStyle:${selectedInstanceData.id}` as const), payload: { id: selectedInstanceData.id, updates, diff --git a/apps/designer/app/designer/features/topbar/menu/menu.tsx b/apps/designer/app/designer/features/topbar/menu/menu.tsx index 6cb5c48619..42d7136a6d 100644 --- a/apps/designer/app/designer/features/topbar/menu/menu.tsx +++ b/apps/designer/app/designer/features/topbar/menu/menu.tsx @@ -1,5 +1,5 @@ import { useNavigate } from "react-router-dom"; -import { type Publish } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { DropdownMenu, DropdownMenuContent, @@ -140,9 +140,9 @@ export const Menu = ({ config, publish }: MenuProps) => { { - publish<"shortcut", string>({ + publish({ type: "shortcut", - payload: "undo", + payload: { name: "undo" }, }); }} > @@ -152,9 +152,9 @@ export const Menu = ({ config, publish }: MenuProps) => { { - publish<"shortcut", string>({ + publish({ type: "shortcut", - payload: "redo", + payload: { name: "redo" }, }); }} > @@ -165,9 +165,9 @@ export const Menu = ({ config, publish }: MenuProps) => { { - publish<"shortcut", string>({ + publish({ type: "shortcut", - payload: "copy", + payload: { name: "copy" }, }); }} > @@ -177,9 +177,9 @@ export const Menu = ({ config, publish }: MenuProps) => { { - publish<"shortcut", string>({ + publish({ type: "shortcut", - payload: "paste", + payload: { name: "paste" }, }); }} > @@ -189,9 +189,9 @@ export const Menu = ({ config, publish }: MenuProps) => { { - publish<"shortcut", string>({ + publish({ type: "shortcut", - payload: "delete", + payload: { name: "delete" }, }); }} > @@ -238,7 +238,7 @@ export const Menu = ({ config, publish }: MenuProps) => { { - publish<"togglePreviewMode">({ + publish({ type: "togglePreviewMode", }); }} diff --git a/apps/designer/app/designer/features/topbar/preview.tsx b/apps/designer/app/designer/features/topbar/preview.tsx index 53c1158815..a51df4d722 100644 --- a/apps/designer/app/designer/features/topbar/preview.tsx +++ b/apps/designer/app/designer/features/topbar/preview.tsx @@ -1,8 +1,14 @@ -import { useSubscribe, type Publish } from "@webstudio-is/react-sdk"; +import { useSubscribe, type Publish } from "~/shared/pubsub"; import { EyeOpenIcon } from "@webstudio-is/icons"; import { SimpleToggle } from "@webstudio-is/design-system"; import { useIsPreviewMode } from "~/shared/nano-states"; +declare module "~/shared/pubsub" { + export interface PubsubMap { + previewMode: boolean; + } +} + type PreviewButtonProps = { publish: Publish; }; @@ -12,13 +18,13 @@ export const PreviewButton = ({ publish }: PreviewButtonProps) => { const setValue = (value: boolean) => { setIsPreviewMode(value); - publish<"previewMode", boolean>({ + publish({ type: "previewMode", payload: value, }); }; - useSubscribe<"togglePreviewMode">("togglePreviewMode", () => { + useSubscribe("togglePreviewMode", () => { setValue(isPreviewMode === false); }); diff --git a/apps/designer/app/designer/features/workspace/canvas-tools/canvas-tools.tsx b/apps/designer/app/designer/features/workspace/canvas-tools/canvas-tools.tsx index bf34ceaebf..9eff1144dd 100644 --- a/apps/designer/app/designer/features/workspace/canvas-tools/canvas-tools.tsx +++ b/apps/designer/app/designer/features/workspace/canvas-tools/canvas-tools.tsx @@ -1,4 +1,4 @@ -import { type Publish } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { Box } from "@webstudio-is/design-system"; import { PlacementIndicator } from "@webstudio-is/design-system"; import { diff --git a/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-editing-instance-id.ts b/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-editing-instance-id.ts index 95892904ce..6de05b56bd 100644 --- a/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-editing-instance-id.ts +++ b/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-editing-instance-id.ts @@ -1,10 +1,7 @@ -import { type Instance, useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useTextEditingInstanceId } from "~/shared/nano-states"; export const useSubscribeTextEditingInstanceId = () => { const [, setInstanceId] = useTextEditingInstanceId(); - useSubscribe<"textEditingInstanceId", Instance["id"] | undefined>( - "textEditingInstanceId", - setInstanceId - ); + useSubscribe("textEditingInstanceId", setInstanceId); }; diff --git a/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-instance-rect.ts b/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-instance-rect.ts index d83b97900e..874e0dedea 100644 --- a/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-instance-rect.ts +++ b/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-instance-rect.ts @@ -1,4 +1,4 @@ -import { useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useSelectedInstanceRect, useHoveredInstanceRect, @@ -6,13 +6,7 @@ import { export const useSubscribeInstanceRect = () => { const [, setSelectedRect] = useSelectedInstanceRect(); - useSubscribe<"selectedInstanceRect", DOMRect>( - "selectedInstanceRect", - setSelectedRect - ); + useSubscribe("selectedInstanceRect", setSelectedRect); const [, setHoveredRect] = useHoveredInstanceRect(); - useSubscribe<"hoveredInstanceRect", DOMRect>( - "hoveredInstanceRect", - setHoveredRect - ); + useSubscribe("hoveredInstanceRect", setHoveredRect); }; diff --git a/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-selection-rect.ts b/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-selection-rect.ts index 6a46994513..5963920e50 100644 --- a/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-selection-rect.ts +++ b/apps/designer/app/designer/features/workspace/canvas-tools/hooks/use-subscribe-selection-rect.ts @@ -1,7 +1,7 @@ -import { useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useSelectionRect } from "~/designer/shared/nano-states"; export const useSubscribeSelectionRect = () => { const [, setSelectionRect] = useSelectionRect(); - useSubscribe<"selectionRect", DOMRect>("selectionRect", setSelectionRect); + useSubscribe("selectionRect", setSelectionRect); }; diff --git a/apps/designer/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx b/apps/designer/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx index 8e1e206b76..6e3ed685d1 100644 --- a/apps/designer/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx +++ b/apps/designer/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx @@ -1,4 +1,4 @@ -import { type Instance, type Publish } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; import { useMemo, useState, type MouseEventHandler } from "react"; import { useSelectedInstanceData, @@ -105,7 +105,7 @@ export const TextToolbar = ({ publish }: TextToolbarProps) => { css={placement} onValueChange={(component) => { const instance = createInstance({ component }); - publish<"insertInlineInstance", Instance>({ + publish({ type: "insertInlineInstance", payload: instance, }); diff --git a/apps/designer/app/designer/features/workspace/workspace.tsx b/apps/designer/app/designer/features/workspace/workspace.tsx index 53c2581306..2be2c62316 100644 --- a/apps/designer/app/designer/features/workspace/workspace.tsx +++ b/apps/designer/app/designer/features/workspace/workspace.tsx @@ -1,7 +1,19 @@ import { Box, Flex, Toaster } from "@webstudio-is/design-system"; +import { type Instance, type InstanceProps } from "@webstudio-is/react-sdk"; import { useCanvasWidth, useZoom } from "~/designer/shared/nano-states"; import { CanvasTools } from "./canvas-tools"; -import { type Publish } from "@webstudio-is/react-sdk"; +import { type Publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + insertInstance: { + instance: Instance; + dropTarget?: { parentId: Instance["id"]; position: number }; + props?: InstanceProps; + }; + unselectInstance: undefined; + } +} const workspaceStyle = { flexGrow: 1, @@ -39,7 +51,7 @@ export const Workspace = ({ const [canvasWidth] = useCanvasWidth(); const handleWorkspaceClick = () => { - publish<"unselectInstance">({ type: "unselectInstance" }); + publish({ type: "unselectInstance" }); }; return ( diff --git a/apps/designer/app/designer/shared/shortcuts/use-publish-shortcuts.ts b/apps/designer/app/designer/shared/shortcuts/use-publish-shortcuts.ts index 2f6e6faef5..8ba8c3da72 100644 --- a/apps/designer/app/designer/shared/shortcuts/use-publish-shortcuts.ts +++ b/apps/designer/app/designer/shared/shortcuts/use-publish-shortcuts.ts @@ -1,6 +1,12 @@ import { useHotkeys } from "react-hotkeys-hook"; -import { type Publish } from "@webstudio-is/react-sdk"; import { shortcuts, options } from "~/shared/shortcuts"; +import { type Publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + shortcut: { name: keyof typeof shortcuts; key?: string }; + } +} const names = Object.keys(shortcuts) as Array; @@ -15,7 +21,7 @@ export const usePublishShortcuts = (publish: Publish) => { shortcuts[name], (event) => { event.preventDefault(); - publish<"shortcut", { name: string; key: string }>({ + publish({ type: "shortcut", payload: { name, key: event.key }, }); diff --git a/apps/designer/app/shared/error/error-message.tsx b/apps/designer/app/shared/error/error-message.tsx index 71c0e1aecd..daebed6b5f 100644 --- a/apps/designer/app/shared/error/error-message.tsx +++ b/apps/designer/app/shared/error/error-message.tsx @@ -1,5 +1,11 @@ -import { publish } from "@webstudio-is/react-sdk"; import { useEffect } from "react"; +import { publish } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + canvasWidth: number; + } +} export const ErrorMessage = ({ message }: { message: string }) => { useEffect(() => { diff --git a/apps/designer/app/shared/nano-states/use-subscribe-drag-drop-state.ts b/apps/designer/app/shared/nano-states/use-subscribe-drag-drop-state.ts index 2c53e4320b..d5cb3b57ea 100644 --- a/apps/designer/app/shared/nano-states/use-subscribe-drag-drop-state.ts +++ b/apps/designer/app/shared/nano-states/use-subscribe-drag-drop-state.ts @@ -1,31 +1,20 @@ -import { useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useDragAndDropState } from "./nano-states"; -import { - DropTargetChangePayload, - DragStartPayload, - DragEndPayload, -} from "~/canvas/shared/use-drag-drop"; export const useSubscribeDragAndDropState = () => { const [state, setState] = useDragAndDropState(); - useSubscribe<"dragStart", DragStartPayload>( - "dragStart", - ({ origin, dragItem }) => { - // It's possible that dropTargetChange comes before dragStart. - // So it's important to spread the current ...state here. - setState({ ...state, isDragging: true, origin, dragItem }); - } - ); + useSubscribe("dragStart", ({ origin, dragItem }) => { + // It's possible that dropTargetChange comes before dragStart. + // So it's important to spread the current ...state here. + setState({ ...state, isDragging: true, origin, dragItem }); + }); - useSubscribe<"dropTargetChange", DropTargetChangePayload>( - "dropTargetChange", - (dropTarget) => { - setState({ ...state, dropTarget }); - } - ); + useSubscribe("dropTargetChange", (dropTarget) => { + setState({ ...state, dropTarget }); + }); - useSubscribe<"dragEnd", DragEndPayload>("dragEnd", () => { + useSubscribe("dragEnd", () => { setState({ isDragging: false }); }); }; diff --git a/apps/designer/app/shared/nano-states/use-subscribe-scroll-state.ts b/apps/designer/app/shared/nano-states/use-subscribe-scroll-state.ts index 9ddc2f5857..dd4c1662d2 100644 --- a/apps/designer/app/shared/nano-states/use-subscribe-scroll-state.ts +++ b/apps/designer/app/shared/nano-states/use-subscribe-scroll-state.ts @@ -1,7 +1,7 @@ -import { useSubscribe } from "@webstudio-is/react-sdk"; +import { useSubscribe } from "~/shared/pubsub"; import { useIsScrolling } from "./nano-states"; export const useSubscribeScrollState = () => { const [, setIsScrolling] = useIsScrolling(); - useSubscribe<"scrollState", boolean>("scrollState", setIsScrolling); + useSubscribe("scrollState", setIsScrolling); }; diff --git a/apps/designer/app/shared/pubsub/index.ts b/apps/designer/app/shared/pubsub/index.ts new file mode 100644 index 0000000000..6a5e28f10d --- /dev/null +++ b/apps/designer/app/shared/pubsub/index.ts @@ -0,0 +1,9 @@ +import { createPubsub } from "@webstudio-is/react-sdk"; + +// this interface is meant to be augmented by the user +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PubsubMap {} + +export const { publish, usePublish, useSubscribe } = createPubsub(); +export type Publish = typeof publish; +export type UsePublish = typeof usePublish; diff --git a/apps/designer/app/shared/shortcuts/index.ts b/apps/designer/app/shared/shortcuts/index.ts index cb4d00ea41..bbae891ede 100644 --- a/apps/designer/app/shared/shortcuts/index.ts +++ b/apps/designer/app/shared/shortcuts/index.ts @@ -5,6 +5,7 @@ export const shortcuts = { preview: "cmd+shift+p, ctrl+shift+p", copy: "cmd+c, ctrl+c", paste: "cmd+v, ctrl+v", + delete: "backspace, delete", breakpointsMenu: "cmd+b, ctrl+b", breakpoint: Array.from(new Array(9)) .map((_, index) => `cmd+${index + 1}, ctrl+${index + 1}`) diff --git a/package.json b/package.json index cbcd14529f..839e816f11 100644 --- a/package.json +++ b/package.json @@ -31,5 +31,6 @@ "packageManager": "yarn@1.22.19", "lint-staged": { "*.{ts,tsx,js,json,css,md}": "prettier --write" - } + }, + "prettier": {} } diff --git a/packages/asset-uploader/package.json b/packages/asset-uploader/package.json index 875be06eba..4712576707 100644 --- a/packages/asset-uploader/package.json +++ b/packages/asset-uploader/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/asset-uploader", - "version": "0.0.1", + "version": "0.1.0", "description": "Webstudio Asset Uploader", "author": "Webstudio ", "homepage": "https://webstudio.is", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 78fe722bc5..c5c4ddf934 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/design-system", - "version": "0.1.0", + "version": "0.1.1", "description": "Webstudio Design System", "author": "Webstudio ", "homepage": "https://webstudio.is", diff --git a/packages/design-system/src/components/text.tsx b/packages/design-system/src/components/text.tsx index 065fdb33d3..5079353222 100644 --- a/packages/design-system/src/components/text.tsx +++ b/packages/design-system/src/components/text.tsx @@ -14,12 +14,12 @@ export const Text = styled("div", { variant: { regular: { fontWeight: 400, - fontSize: 12, + fontSize: "$1", letterSpacing: "0.005em", }, label: { fontWeight: 500, - fontSize: 12, + fontSize: "$1", letterSpacing: "0.005em", }, tiny: { @@ -29,19 +29,18 @@ export const Text = styled("div", { }, title: { fontWeight: 700, - fontSize: 12, + fontSize: "$1", letterSpacing: "0.01em", }, mono: { fontFamily: "$mono", fontWeight: 400, - fontSize: 12, + fontSize: "$1", textTransform: "uppercase", }, unit: { fontWeight: 500, fontSize: 10, - lineHeight: 12, textTransform: "uppercase", }, }, diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 30efd8e4c4..ea358ddc79 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/eslint-config-custom", - "version": "1.0.0", + "version": "1.0.1", "main": "index.js", "license": "MIT", "dependencies": { diff --git a/packages/icons/package.json b/packages/icons/package.json index 69a6fb16c7..e500e09f13 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/icons", - "version": "0.1.0", + "version": "0.1.1", "description": "Webstudio Icons", "author": "Webstudio ", "homepage": "https://webstudio.is", diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index 001d6d9a39..a7a9bc34ae 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/jest-config", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "dependencies": { "@types/jest": "^27.4.1", diff --git a/packages/prisma-client/package.json b/packages/prisma-client/package.json index 6a88f62ea2..4e32e240fd 100644 --- a/packages/prisma-client/package.json +++ b/packages/prisma-client/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/prisma-client", - "version": "0.0.1", + "version": "0.1.0", "description": "Webstudio Prisma layer", "author": "Webstudio ", "homepage": "https://webstudio.is", diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 5bf5020e21..eba833c4ca 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/react-sdk", - "version": "0.5.0", + "version": "0.5.1", "description": "Webstudio JavaScript / TypeScript API", "author": "Webstudio ", "homepage": "https://webstudio.is", diff --git a/packages/react-sdk/src/pubsub/create.ts b/packages/react-sdk/src/pubsub/create.ts new file mode 100644 index 0000000000..a99f456507 --- /dev/null +++ b/packages/react-sdk/src/pubsub/create.ts @@ -0,0 +1,70 @@ +import mitt from "mitt"; +import { useCallback, useEffect, useRef } from "react"; + +export const createPubsub = () => { + type Action = + undefined extends PublishMap[Type] + ? { type: Type; payload?: PublishMap[Type] } + : { type: Type; payload: PublishMap[Type] }; + + // `mitt` has a somewhat annoying overload for `*` type that makes it hard to wrap in a generic context + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const emitter = mitt>(); + + if (typeof window === "object") { + window.addEventListener( + "message", + (event: MessageEvent) => { + // @todo this has no type safety built in, could be anything from any source. + // we could potentially maintain a list of valid event types at runtime + // at the very least we could add a brand property or something to our events + if (typeof event.data?.type === "string") { + emitter.emit(event.data.type, event.data.payload); + } + }, + false + ); + } + + return { + /** + * To publish a postMessage event on the current window and parent window from the iframe. + */ + publish(action: Action) { + window.parent.postMessage(action, "*"); + window.postMessage(action, "*"); + }, + + /** + * To publish a postMessage event on the iframe and parent window from the parent window. + */ + usePublish() { + const iframeRef = useRef(null); + const publishCallback = useCallback( + (action: Action) => { + const element = iframeRef.current; + if (element?.contentWindow == null) return; + element.contentWindow.postMessage(action, "*"); + window.postMessage(action, "*"); + }, + [iframeRef] + ); + return [publishCallback, iframeRef] as const; + }, + + /** + * To subscribe a message event on the current window. + */ + useSubscribe( + type: Type, + onAction: (payload: PublishMap[Type]) => void + ) { + useEffect(() => { + emitter.on(type, onAction); + return () => { + emitter.off(type, onAction); + }; + }, [type, onAction]); + }, + }; +}; diff --git a/packages/react-sdk/src/pubsub/index.ts b/packages/react-sdk/src/pubsub/index.ts index 6e1ebe38b9..c6262006e3 100644 --- a/packages/react-sdk/src/pubsub/index.ts +++ b/packages/react-sdk/src/pubsub/index.ts @@ -1,2 +1 @@ -export * from "./publish"; -export * from "./subscribe"; +export * from "./create"; diff --git a/packages/react-sdk/src/pubsub/publish.ts b/packages/react-sdk/src/pubsub/publish.ts deleted file mode 100644 index 8eda413e4b..0000000000 --- a/packages/react-sdk/src/pubsub/publish.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useRef, useCallback, MutableRefObject } from "react"; - -type Action = { - type: Type; - payload?: Payload; -}; - -export type Publish = ( - action: Action -) => void; - -type UsePublish = [Publish, MutableRefObject]; - -/** - * To publish a postMessage event on the iframe and parent window from the parent window. - */ -export const usePublish = (): UsePublish => { - const iframeRef = useRef(null); - const publishCallback: Publish = useCallback( - (action) => { - const element = iframeRef.current; - if (element?.contentWindow == null) return; - element.contentWindow.postMessage(action, "*"); - window.postMessage(action, "*"); - }, - [iframeRef] - ); - return [publishCallback, iframeRef]; -}; - -/** - * To publish a postMessage event on the current window and parent window from the iframe. - */ -export const publish = ( - action: Action -) => { - window.parent.postMessage(action, "*"); - window.postMessage(action, "*"); -}; diff --git a/packages/react-sdk/src/pubsub/subscribe.ts b/packages/react-sdk/src/pubsub/subscribe.ts deleted file mode 100644 index 03f79d83bf..0000000000 --- a/packages/react-sdk/src/pubsub/subscribe.ts +++ /dev/null @@ -1,40 +0,0 @@ -import mitt from "mitt"; -import { useEffect } from "react"; - -// @todo do we want to have a central map of all events/data? -// Pro - you can't use a wrong event with a wrong data -// Contra - if we have hundreds of events, this list will become a central thing to change, -// may result in maintenance downsides and merge conflicts, because it will have to import each data type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Events = Record; - -export const emitter = mitt(); - -if (typeof window === "object") { - window.addEventListener( - "message", - (event: MessageEvent) => { - // @todo this has no type safety built in, could be anything from any source. - // we could potentially maintain a list of valid event types, but see prior contra points. - if (typeof event.data?.type === "string") { - emitter.emit(event.data.type, event.data.payload); - } - }, - false - ); -} - -/** - * To subscribe a message event on the current window. - */ -export const useSubscribe = ( - type: Type, - onAction: (payload: Payload) => void -) => { - useEffect(() => { - emitter.on(type, onAction); - return () => { - emitter.off(type, onAction); - }; - }, [type, onAction]); -}; diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 56f2bd444d..a96b254ccd 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/tsconfig", - "version": "1.0.0", + "version": "1.0.1", "private": false, "main": "index.js", "files": [