mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 21:15:18 +09:00
Merge branch 'main' of github.com:webstudio-is/webstudio-designer into redesign-assets
This commit is contained in:
commit
d54b56e299
68 files changed
+564
-492
No files matched your search
@@ -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 }}
|
||||
- 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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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<boolean>(false);
|
||||
useSubscribe<"previewMode", boolean>("previewMode", setIsPreviewMode);
|
||||
useSubscribe("previewMode", setIsPreviewMode);
|
||||
return isPreviewMode;
|
||||
};
|
||||
|
||||
|
||||
+7
-1
@@ -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;
|
||||
|
||||
+5
-7
@@ -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<Instance>(INSERT_INSTANCE_COMMAND, payload);
|
||||
}
|
||||
);
|
||||
useSubscribe("insertInlineInstance", (payload) => {
|
||||
editor.dispatchCommand<Instance>(INSERT_INSTANCE_COMMAND, payload);
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
+9
-3
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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<string, StyleUpdates>(
|
||||
`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(() => {
|
||||
|
||||
@@ -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<Breakpoint>) => {
|
||||
const [, setCurrentBreakpoints] = useBreakpoints();
|
||||
@@ -27,7 +24,7 @@ export const useInitializeBreakpoints = (breakpoints: Array<Breakpoint>) => {
|
||||
const usePublishBreakpoints = () => {
|
||||
const [breakpoints] = useBreakpoints();
|
||||
useEffect(() => {
|
||||
publish<"loadBreakpoints", Array<Breakpoint>>({
|
||||
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 = () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
@@ -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<null>["rect"];
|
||||
@@ -48,9 +56,12 @@ export type DragEndPayload = {
|
||||
|
||||
export type DragMovePayload = { canvasCoordinates: Point };
|
||||
|
||||
const initialState = {
|
||||
dropTarget: undefined as DropTarget<Instance> | undefined,
|
||||
dragItem: undefined as BaseInstance | undefined,
|
||||
const initialState: {
|
||||
dropTarget: DropTarget<Instance> | 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 };
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Breakpoint>;
|
||||
}
|
||||
}
|
||||
|
||||
type BreakpointSelectorItemProps = {
|
||||
breakpoint: Breakpoint;
|
||||
};
|
||||
|
||||
@@ -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<Breakpoint>>(
|
||||
"loadBreakpoints",
|
||||
setBreakpoints
|
||||
);
|
||||
useSubscribe("loadBreakpoints", setBreakpoints);
|
||||
|
||||
useEffect(() => {
|
||||
// Set the initial selected breakpoint
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useSubscribe } from "@webstudio-is/react-sdk";
|
||||
import { useSubscribe } from "~/shared/pubsub";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import {
|
||||
useSelectedBreakpoint,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Publish } from "@webstudio-is/react-sdk";
|
||||
import type { Publish } from "~/shared/pubsub";
|
||||
import {
|
||||
__DEPRECATED__Text,
|
||||
Tabs,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
|
||||
@@ -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 }) },
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<string, StyleUpdates>({
|
||||
publish({
|
||||
type:
|
||||
type === "update"
|
||||
? "updateStyle"
|
||||
: `previewStyle:${selectedInstanceData.id}`,
|
||||
: (`previewStyle:${selectedInstanceData.id}` as const),
|
||||
payload: {
|
||||
id: selectedInstanceData.id,
|
||||
updates,
|
||||
|
||||
@@ -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) => {
|
||||
<DropdownMenuItem
|
||||
css={menuItemCss}
|
||||
onSelect={() => {
|
||||
publish<"shortcut", string>({
|
||||
publish({
|
||||
type: "shortcut",
|
||||
payload: "undo",
|
||||
payload: { name: "undo" },
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -152,9 +152,9 @@ export const Menu = ({ config, publish }: MenuProps) => {
|
||||
<DropdownMenuItem
|
||||
css={menuItemCss}
|
||||
onSelect={() => {
|
||||
publish<"shortcut", string>({
|
||||
publish({
|
||||
type: "shortcut",
|
||||
payload: "redo",
|
||||
payload: { name: "redo" },
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -165,9 +165,9 @@ export const Menu = ({ config, publish }: MenuProps) => {
|
||||
<DropdownMenuItem
|
||||
css={menuItemCss}
|
||||
onSelect={() => {
|
||||
publish<"shortcut", string>({
|
||||
publish({
|
||||
type: "shortcut",
|
||||
payload: "copy",
|
||||
payload: { name: "copy" },
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -177,9 +177,9 @@ export const Menu = ({ config, publish }: MenuProps) => {
|
||||
<DropdownMenuItem
|
||||
css={menuItemCss}
|
||||
onSelect={() => {
|
||||
publish<"shortcut", string>({
|
||||
publish({
|
||||
type: "shortcut",
|
||||
payload: "paste",
|
||||
payload: { name: "paste" },
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -189,9 +189,9 @@ export const Menu = ({ config, publish }: MenuProps) => {
|
||||
<DropdownMenuItem
|
||||
css={menuItemCss}
|
||||
onSelect={() => {
|
||||
publish<"shortcut", string>({
|
||||
publish({
|
||||
type: "shortcut",
|
||||
payload: "delete",
|
||||
payload: { name: "delete" },
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -238,7 +238,7 @@ export const Menu = ({ config, publish }: MenuProps) => {
|
||||
<DropdownMenuItem
|
||||
css={menuItemCss}
|
||||
onSelect={() => {
|
||||
publish<"togglePreviewMode">({
|
||||
publish({
|
||||
type: "togglePreviewMode",
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-5
@@ -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);
|
||||
};
|
||||
+3
-9
@@ -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);
|
||||
};
|
||||
+2
-2
@@ -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);
|
||||
};
|
||||
+2
-2
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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<keyof typeof shortcuts>;
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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<PubsubMap>();
|
||||
export type Publish = typeof publish;
|
||||
export type UsePublish = typeof usePublish;
|
||||
@@ -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}`)
|
||||
|
||||
+2
-1
@@ -31,5 +31,6 @@
|
||||
"packageManager": "yarn@1.22.19",
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,json,css,md}": "prettier --write"
|
||||
}
|
||||
},
|
||||
"prettier": {}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/asset-uploader",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"description": "Webstudio Asset Uploader",
|
||||
"author": "Webstudio <github@webstudio.is>",
|
||||
"homepage": "https://webstudio.is",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/design-system",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "Webstudio Design System",
|
||||
"author": "Webstudio <github@webstudio.is>",
|
||||
"homepage": "https://webstudio.is",
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/eslint-config-custom",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/icons",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "Webstudio Icons",
|
||||
"author": "Webstudio <github@webstudio.is>",
|
||||
"homepage": "https://webstudio.is",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/prisma-client",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"description": "Webstudio Prisma layer",
|
||||
"author": "Webstudio <github@webstudio.is>",
|
||||
"homepage": "https://webstudio.is",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/react-sdk",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"description": "Webstudio JavaScript / TypeScript API",
|
||||
"author": "Webstudio <github@webstudio.is>",
|
||||
"homepage": "https://webstudio.is",
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import mitt from "mitt";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
export const createPubsub = <PublishMap>() => {
|
||||
type Action<Type extends keyof PublishMap> =
|
||||
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<Record<any, any>>();
|
||||
|
||||
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<Type extends keyof PublishMap>(action: Action<Type>) {
|
||||
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<HTMLIFrameElement | null>(null);
|
||||
const publishCallback = useCallback(
|
||||
<Type extends keyof PublishMap>(action: Action<Type>) => {
|
||||
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 extends keyof PublishMap>(
|
||||
type: Type,
|
||||
onAction: (payload: PublishMap[Type]) => void
|
||||
) {
|
||||
useEffect(() => {
|
||||
emitter.on(type, onAction);
|
||||
return () => {
|
||||
emitter.off(type, onAction);
|
||||
};
|
||||
}, [type, onAction]);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,2 +1 @@
|
||||
export * from "./publish";
|
||||
export * from "./subscribe";
|
||||
export * from "./create";
|
||||
@@ -1,39 +0,0 @@
|
||||
import { useRef, useCallback, MutableRefObject } from "react";
|
||||
|
||||
type Action<Type, Payload> = {
|
||||
type: Type;
|
||||
payload?: Payload;
|
||||
};
|
||||
|
||||
export type Publish = <Type, Payload = undefined>(
|
||||
action: Action<Type, Payload>
|
||||
) => void;
|
||||
|
||||
type UsePublish = [Publish, MutableRefObject<HTMLIFrameElement | null>];
|
||||
|
||||
/**
|
||||
* To publish a postMessage event on the iframe and parent window from the parent window.
|
||||
*/
|
||||
export const usePublish = (): UsePublish => {
|
||||
const iframeRef = useRef<HTMLIFrameElement>(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 = <Type extends string, Payload = undefined>(
|
||||
action: Action<Type, Payload>
|
||||
) => {
|
||||
window.parent.postMessage(action, "*");
|
||||
window.postMessage(action, "*");
|
||||
};
|
||||
@@ -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<string, any>;
|
||||
|
||||
export const emitter = mitt<Events>();
|
||||
|
||||
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 extends string, Payload = undefined>(
|
||||
type: Type,
|
||||
onAction: (payload: Payload) => void
|
||||
) => {
|
||||
useEffect(() => {
|
||||
emitter.on(type, onAction);
|
||||
return () => {
|
||||
emitter.off(type, onAction);
|
||||
};
|
||||
}, [type, onAction]);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webstudio-is/tsconfig",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"private": false,
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
||||
Reference in new issue
Block a user