mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-23 03:27:27 +09:00
feat: WIP realtime collaboration to builder (#5726)
https://github.com/webstudio-is/webstudio/issues/46
This commit is contained in:
1 parent
7cfaf5533b
commit
b006947a85
231 files changed
+8729
-2152
No files matched your search
@@ -27,8 +27,6 @@ import {
|
||||
$authPermit,
|
||||
$authToken,
|
||||
$isPreviewMode,
|
||||
$pages,
|
||||
$project,
|
||||
subscribeResources,
|
||||
$authTokenPermissions,
|
||||
$isDesignMode,
|
||||
@@ -38,6 +36,7 @@ import {
|
||||
$stagingUsername,
|
||||
$stagingPassword,
|
||||
} from "~/shared/nano-states";
|
||||
import { $project } from "~/shared/sync/data-stores";
|
||||
import { $settings, type Settings } from "./shared/client-settings";
|
||||
import { builderUrl, getCanvasUrl } from "~/shared/router-utils";
|
||||
import { BlockingAlerts } from "./features/blocking-alerts";
|
||||
@@ -52,6 +51,7 @@ import {
|
||||
$isCloneDialogOpen,
|
||||
$loadingState,
|
||||
} from "./shared/nano-states";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import { CloneProjectDialog } from "~/shared/clone-project";
|
||||
import type { TokenPermissions } from "@webstudio-is/authorization-token";
|
||||
import { useToastErrors } from "~/shared/error/toast-error";
|
||||
|
||||
@@ -7,7 +7,8 @@ import { TopbarLayout } from "~/builder/shared/topbar-layout";
|
||||
import { AddressBarPopover } from "./address-bar";
|
||||
import { $dataSources, $pages } from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $awareness, $selectedPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import { $currentSystem } from "~/shared/system";
|
||||
|
||||
registerContainers();
|
||||
@@ -67,7 +68,7 @@ export default {
|
||||
} satisfies Meta;
|
||||
|
||||
export const AddressBar: StoryFn = () => {
|
||||
$awareness.set({ pageId: "dynamicId" });
|
||||
$selectedPageId.set("dynamicId");
|
||||
return (
|
||||
<StorySection title="Address Bar">
|
||||
<TopbarLayout
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
matchPathnamePattern,
|
||||
tokenizePathnamePattern,
|
||||
} from "~/builder/shared/url-pattern";
|
||||
import { $selectedPage, $selectedPagePath } from "~/shared/awareness";
|
||||
import { $selectedPage, $selectedPagePath } from "~/shared/nano-states";
|
||||
import { $currentSystem, updateCurrentSystem } from "~/shared/system";
|
||||
|
||||
const $selectedPageHistory = computed(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StorySection } from "@webstudio-is/design-system";
|
||||
import { BreakpointsSelector as BreakpointsSelectorComponent } from "./breakpoints-selector";
|
||||
import { $breakpoints } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states/breakpoints";
|
||||
|
||||
const breakpointsMap = new Map([
|
||||
|
||||
@@ -15,8 +15,8 @@ import { CascadeIndicator } from "./cascade-indicator";
|
||||
import {
|
||||
$selectedBreakpoint,
|
||||
$selectedBreakpointId,
|
||||
$breakpoints,
|
||||
} from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { groupBreakpoints, isBaseBreakpoint } from "~/shared/breakpoints";
|
||||
import { setCanvasWidth } from "../../shared/calc-canvas-width";
|
||||
import { $canvasWidth } from "~/builder/shared/nano-states";
|
||||
|
||||
@@ -3,12 +3,10 @@ import { useEffect } from "react";
|
||||
import { initialBreakpoints, coreMetas } from "@webstudio-is/sdk";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import * as baseComponentMetas from "@webstudio-is/sdk-components-react/metas";
|
||||
import {
|
||||
$breakpoints,
|
||||
$pages,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { StorySection } from "@webstudio-is/design-system";
|
||||
import { CommandPanel as CommandPanelComponent } from "./command-panel";
|
||||
@@ -57,7 +55,7 @@ pages.pages.push({
|
||||
meta: {},
|
||||
});
|
||||
$pages.set(pages);
|
||||
$awareness.set({ pageId: pages.homePage.id });
|
||||
$selectedPageId.set(pages.homePage.id);
|
||||
|
||||
export const CommandPanel: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,10 +9,10 @@ import { computed } from "nanostores";
|
||||
import { compareMedia } from "@webstudio-is/css-engine";
|
||||
import type { Breakpoint } from "@webstudio-is/sdk";
|
||||
import {
|
||||
$breakpoints,
|
||||
$selectedBreakpoint,
|
||||
$selectedBreakpointId,
|
||||
} from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { closeCommandPanel, $isCommandPanelOpen } from "../command-state";
|
||||
import type { BaseOption } from "../shared/types";
|
||||
import { setCanvasWidth } from "~/builder/shared/calc-canvas-width";
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
insertWebstudioFragmentAt,
|
||||
} from "~/shared/instance-utils";
|
||||
import { humanizeString } from "~/shared/string-utils";
|
||||
import { $selectedPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import {
|
||||
getInstanceLabel,
|
||||
InstanceIcon,
|
||||
|
||||
@@ -13,12 +13,10 @@ import {
|
||||
import { matchSorter } from "match-sorter";
|
||||
import { computed } from "nanostores";
|
||||
import { elementComponent, tags } from "@webstudio-is/sdk";
|
||||
import {
|
||||
$instances,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $selectedInstancePath } from "~/shared/awareness";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import {
|
||||
getInstanceLabel,
|
||||
InstanceIcon,
|
||||
|
||||
@@ -17,12 +17,9 @@ import {
|
||||
useSelectedAction,
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { Instance, StyleSource } from "@webstudio-is/sdk";
|
||||
import {
|
||||
$styleSources,
|
||||
$styles,
|
||||
$breakpoints,
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { $styleSources } from "~/shared/sync/data-stores";
|
||||
import { $selectedStyleSources } from "~/shared/nano-states";
|
||||
import { $styles, $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { findDuplicateTokens } from "~/shared/style-source-utils";
|
||||
import { $styleSourceUsages } from "~/builder/shared/style-source-actions";
|
||||
import { InstanceList, showInstance } from "../shared/instance-list";
|
||||
|
||||
@@ -11,7 +11,11 @@ import { parseComponentName } from "@webstudio-is/sdk";
|
||||
import type { Instance } from "@webstudio-is/sdk";
|
||||
import { $instances, $pages } from "~/shared/sync/data-stores";
|
||||
import { getInstanceLabel } from "~/builder/shared/instance-label";
|
||||
import { $awareness, findAwarenessByInstanceId } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { findPageAndSelectorByInstanceId } from "~/shared/instance-utils";
|
||||
import { closeCommandPanel, $isCommandPanelOpen } from "../command-state";
|
||||
import type { BaseOption } from "../shared/types";
|
||||
import { setActiveSidebarPanel } from "~/builder/shared/nano-states";
|
||||
@@ -79,13 +83,14 @@ export const InstancesGroup = ({ options }: { options: InstanceOption[] }) => {
|
||||
const pages = $pages.get();
|
||||
const instances = $instances.get();
|
||||
if (pages && instances) {
|
||||
const awareness = findAwarenessByInstanceId(
|
||||
const awareness = findPageAndSelectorByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
instance.id
|
||||
);
|
||||
if (awareness) {
|
||||
$awareness.set(awareness);
|
||||
$selectedPageId.set(awareness.pageId);
|
||||
$selectedInstanceSelector.set(awareness.instanceSelector);
|
||||
setActiveSidebarPanel("auto");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import { computed } from "nanostores";
|
||||
import type { Page } from "@webstudio-is/sdk";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import { $editingPageId } from "~/shared/nano-states";
|
||||
import { $selectedPage, selectPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import { selectPage } from "~/shared/nano-states";
|
||||
import { setActiveSidebarPanel } from "~/builder/shared/nano-states";
|
||||
import { closeCommandPanel, $isCommandPanelOpen } from "../command-state";
|
||||
import type { BaseOption } from "../shared/types";
|
||||
|
||||
@@ -9,13 +9,11 @@ import {
|
||||
import { computed } from "nanostores";
|
||||
import { elementComponent, tags } from "@webstudio-is/sdk";
|
||||
import type { Instance } from "@webstudio-is/sdk";
|
||||
import {
|
||||
$instances,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import { insertWebstudioFragmentAt } from "~/shared/instance-utils";
|
||||
import { $selectedInstancePath } from "~/shared/awareness";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import { InstanceIcon } from "~/builder/shared/instance-label";
|
||||
import { isTreeSatisfyingContentModel } from "~/shared/content-model";
|
||||
import { closeCommandPanel, $isCommandPanelOpen } from "../command-state";
|
||||
|
||||
@@ -4,14 +4,12 @@ import * as baseMetas from "@webstudio-is/sdk-components-react/metas";
|
||||
import * as animationMetas from "@webstudio-is/sdk-components-animation/metas";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $, renderData } from "@webstudio-is/template";
|
||||
import {
|
||||
$instances,
|
||||
$pages,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $pages, $props } from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $awareness, selectInstance } from "~/shared/awareness";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { __testing__ } from "./wrap-group";
|
||||
|
||||
const { canWrapInstance } = __testing__;
|
||||
@@ -25,7 +23,7 @@ const metas = new Map(
|
||||
beforeEach(() => {
|
||||
$registeredComponentMetas.set(metas);
|
||||
$pages.set(createDefaultPages({ rootInstanceId: "" }));
|
||||
$awareness.set({ pageId: "" });
|
||||
$selectedPageId.set("");
|
||||
});
|
||||
|
||||
describe("canWrapInstance for components", () => {
|
||||
|
||||
@@ -19,12 +19,10 @@ import type {
|
||||
Props,
|
||||
WsComponentMeta,
|
||||
} from "@webstudio-is/sdk";
|
||||
import {
|
||||
$instances,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $selectedInstancePath } from "~/shared/awareness";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import {
|
||||
getInstanceLabel,
|
||||
InstanceIcon,
|
||||
|
||||
@@ -18,9 +18,12 @@ import { $instances, $pages } from "~/shared/sync/data-stores";
|
||||
import { getInstanceLabel } from "~/builder/shared/instance-label";
|
||||
import { buildInstancePath } from "~/shared/instance-utils";
|
||||
import { $commandContent } from "~/builder/features/command-panel/command-state";
|
||||
import { findAwarenessByInstanceId } from "~/shared/awareness";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import { findPageAndSelectorByInstanceId } from "~/shared/instance-utils";
|
||||
import { $activeInspectorPanel } from "~/builder/shared/nano-states";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { useAutoSelectFirstItem } from "./auto-select";
|
||||
import { InstancePathFooter } from "./instance-path-footer";
|
||||
|
||||
@@ -53,7 +56,11 @@ export const InstanceList = ({
|
||||
continue;
|
||||
}
|
||||
const path = buildInstancePath(instanceId, pages, instances);
|
||||
const awareness = findAwarenessByInstanceId(pages, instances, instanceId);
|
||||
const awareness = findPageAndSelectorByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
instanceId
|
||||
);
|
||||
const page = pages.pages.find((p) => p.id === awareness.pageId);
|
||||
usedInInstances.push({
|
||||
label: getInstanceLabel(instance),
|
||||
@@ -154,8 +161,13 @@ export const showInstance = (
|
||||
if (pagesData === undefined) {
|
||||
return;
|
||||
}
|
||||
const awareness = findAwarenessByInstanceId(pagesData, instances, instanceId);
|
||||
$awareness.set(awareness);
|
||||
const { pageId, instanceSelector } = findPageAndSelectorByInstanceId(
|
||||
pagesData,
|
||||
instances,
|
||||
instanceId
|
||||
);
|
||||
$selectedPageId.set(pageId);
|
||||
$selectedInstanceSelector.set(instanceSelector);
|
||||
if (panel !== undefined) {
|
||||
$activeInspectorPanel.set(panel);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
insertWebstudioFragmentAt,
|
||||
} from "~/shared/instance-utils";
|
||||
import type { Publish } from "~/shared/pubsub";
|
||||
import { $selectedPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import { mapGroupBy } from "~/shared/shim";
|
||||
import {
|
||||
getInstanceLabel,
|
||||
|
||||
@@ -3,7 +3,8 @@ import { useStore } from "@nanostores/react";
|
||||
import { ChevronRightIcon } from "@webstudio-is/icons";
|
||||
import { theme, Button, Flex, Text } from "@webstudio-is/design-system";
|
||||
import { $textEditingInstanceSelector } from "~/shared/nano-states";
|
||||
import { $selectedInstancePath, selectInstance } from "~/shared/awareness";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { getInstanceLabel } from "~/builder/shared/instance-label";
|
||||
|
||||
export const Breadcrumbs = () => {
|
||||
|
||||
@@ -35,7 +35,7 @@ import { builderApi } from "~/shared/builder-api";
|
||||
import { insertPageCopyMutable } from "~/shared/page-utils";
|
||||
import { Card } from "./card";
|
||||
import type { MarketplaceOverviewItem } from "~/shared/marketplace/types";
|
||||
import { selectPage } from "~/shared/awareness";
|
||||
import { selectPage } from "~/shared/nano-states";
|
||||
|
||||
const isBody = (instance: Instance) =>
|
||||
instance.component === "Body" ||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CollapsibleSection } from "~/builder/shared/collapsible-section";
|
||||
import { highlightCss } from "~/shared/code-highlight";
|
||||
import type { ComputedStyleDecl } from "~/shared/style-object-model";
|
||||
import { $computedStyleDeclarations } from "~/builder/features/style-panel/shared/model";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
|
||||
const preStyle = css(textVariants.mono, {
|
||||
margin: 0,
|
||||
|
||||
@@ -40,27 +40,26 @@ import {
|
||||
$blockChildOutline,
|
||||
$editingItemSelector,
|
||||
$hoveredInstanceSelector,
|
||||
$instances,
|
||||
$isContentMode,
|
||||
$props,
|
||||
$propsIndex,
|
||||
$propValuesByInstanceSelector,
|
||||
$registeredComponentMetas,
|
||||
$selectedInstanceSelector,
|
||||
getIndexedInstanceId,
|
||||
type ItemDropTarget,
|
||||
$propValuesByInstanceSelectorWithMemoryProps,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances, $props } from "~/shared/sync/data-stores";
|
||||
import { isDescendantOrSelf, type InstanceSelector } from "~/shared/tree-utils";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { reparentInstance, toggleInstanceShow } from "~/shared/instance-utils";
|
||||
import { emitCommand } from "~/builder/shared/commands";
|
||||
import { useContentEditable } from "~/shared/dom-hooks";
|
||||
import {
|
||||
$selectedInstanceSelector,
|
||||
$selectedPage,
|
||||
getInstanceKey,
|
||||
selectInstance,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import {
|
||||
findClosestContainer,
|
||||
isRichTextContent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { $pages } from "~/shared/nano-states/pages";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import { PageSettings as PageSettingsComponent } from "./page-settings";
|
||||
import {
|
||||
Grid,
|
||||
|
||||
@@ -68,15 +68,14 @@ import {
|
||||
} from "@webstudio-is/icons";
|
||||
import { useIds } from "~/shared/form-utils";
|
||||
import {
|
||||
$assets,
|
||||
$instances,
|
||||
$pages,
|
||||
$publishedOrigin,
|
||||
$project,
|
||||
$permissions,
|
||||
$isDesignMode,
|
||||
} from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { $project } from "~/shared/sync/data-stores";
|
||||
import { $openProjectSettings } from "~/shared/nano-states/project-settings";
|
||||
import { $instances, $pages } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
BindingControl,
|
||||
BindingPopover,
|
||||
@@ -92,7 +91,7 @@ import {
|
||||
validatePathnamePattern,
|
||||
} from "~/builder/shared/url-pattern";
|
||||
import { useUnmount } from "~/shared/hook-utils/use-mount";
|
||||
import { selectInstance } from "~/shared/awareness";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { computeExpression } from "~/shared/data-variables";
|
||||
import { $currentSystem } from "~/shared/system";
|
||||
import { Card } from "../marketplace/card";
|
||||
|
||||
@@ -23,15 +23,15 @@ import {
|
||||
reparentPageOrFolderMutable,
|
||||
deletePageMutable,
|
||||
} from "./page-utils";
|
||||
import { $dataSourceVariables } from "~/shared/nano-states";
|
||||
import {
|
||||
$dataSourceVariables,
|
||||
$dataSources,
|
||||
$pages,
|
||||
$project,
|
||||
$resources,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import { updateCurrentSystem } from "~/shared/system";
|
||||
import { $resourcesCache, getResourceKey } from "~/shared/resources";
|
||||
|
||||
@@ -464,7 +464,7 @@ test("page root scope should rely on selected page", () => {
|
||||
meta: {},
|
||||
});
|
||||
$pages.set(pages);
|
||||
$awareness.set({ pageId: "pageId" });
|
||||
$selectedPageId.set("pageId");
|
||||
$dataSources.set(
|
||||
toMap([
|
||||
{
|
||||
@@ -506,7 +506,7 @@ test("page root scope should use variable and resource values", () => {
|
||||
homePageId: "homePageId",
|
||||
})
|
||||
);
|
||||
$awareness.set({ pageId: "homePageId" });
|
||||
$selectedPageId.set("homePageId");
|
||||
$dataSources.set(
|
||||
toMap([
|
||||
{
|
||||
@@ -574,7 +574,7 @@ test("page root scope should provide page system variable value", () => {
|
||||
systemDataSourceId: "systemId",
|
||||
})
|
||||
);
|
||||
$awareness.set({ pageId: "homePageId" });
|
||||
$selectedPageId.set("homePageId");
|
||||
$dataSources.set(
|
||||
toMap([
|
||||
{
|
||||
|
||||
@@ -21,18 +21,16 @@ import {
|
||||
deleteInstanceMutable,
|
||||
updateWebstudioData,
|
||||
} from "~/shared/instance-utils";
|
||||
import {
|
||||
$dataSources,
|
||||
$pages,
|
||||
$variableValuesByInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $variableValuesByInstanceSelector } from "~/shared/nano-states";
|
||||
import { $dataSources } from "~/shared/sync/data-stores";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import { insertPageCopyMutable } from "~/shared/page-utils";
|
||||
import {
|
||||
$selectedPage,
|
||||
getInstanceKey,
|
||||
getInstancePath,
|
||||
selectPage,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { selectPage } from "~/shared/nano-states";
|
||||
|
||||
/**
|
||||
* When page or folder needs to be deleted or moved to a different parent,
|
||||
|
||||
@@ -36,8 +36,8 @@ import {
|
||||
$editingPageId,
|
||||
$isContentMode,
|
||||
$isDesignMode,
|
||||
$pages,
|
||||
} from "~/shared/nano-states";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
getAllChildrenAndSelf,
|
||||
reparentOrphansMutable,
|
||||
@@ -66,7 +66,8 @@ import {
|
||||
import { atom, computed } from "nanostores";
|
||||
import { isPathnamePattern } from "~/builder/shared/url-pattern";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { $selectedPage, selectPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import { selectPage } from "~/shared/nano-states";
|
||||
|
||||
const ItemSuffix = ({
|
||||
isParentSelected,
|
||||
|
||||
@@ -43,28 +43,25 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import { validateProjectDomain, type Project } from "@webstudio-is/project";
|
||||
import {
|
||||
$awareness,
|
||||
$selectedPagePath,
|
||||
findAwarenessByInstanceId,
|
||||
type Awareness,
|
||||
} from "~/shared/awareness";
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { findPageAndSelectorByInstanceId } from "~/shared/instance-utils";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import {
|
||||
$authTokenPermissions,
|
||||
$dataSources,
|
||||
$editingPageId,
|
||||
$instances,
|
||||
$pages,
|
||||
$project,
|
||||
$publishedOrigin,
|
||||
$permissions,
|
||||
$stagingUsername,
|
||||
$stagingPassword,
|
||||
$publisherHost,
|
||||
} from "~/shared/nano-states";
|
||||
import { $publisherHost } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$publishDialog,
|
||||
setActiveSidebarPanel,
|
||||
} from "../../shared/nano-states";
|
||||
import { $project } from "~/shared/sync/data-stores";
|
||||
import { Domains, PENDING_TIMEOUT, getPublishStatusAndText } from "./domains";
|
||||
import { CollapsibleDomainSection } from "./collapsible-domain-section";
|
||||
import {
|
||||
@@ -83,6 +80,7 @@ import { isPathnamePattern, type Templates } from "@webstudio-is/sdk";
|
||||
import { DomainCheckbox, domainToPublishName } from "./domain-checkbox";
|
||||
import { CopyToClipboard } from "~/shared/copy-to-clipboard";
|
||||
import { $openProjectSettings } from "~/shared/nano-states/project-settings";
|
||||
import { $dataSources, $instances, $pages } from "~/shared/sync/data-stores";
|
||||
import { RelativeTime } from "~/builder/shared/relative-time";
|
||||
import cmsUpgradeBanner from "~/shared/cms-upgrade-banner.svg?url";
|
||||
|
||||
@@ -327,7 +325,11 @@ const $restrictedFeatures = computed(
|
||||
const features = new Map<
|
||||
string,
|
||||
| undefined
|
||||
| { awareness?: Awareness; view?: "pageSettings"; info?: ReactNode }
|
||||
| {
|
||||
navigate?: { pageId: string; instanceSelector: string[] };
|
||||
view?: "pageSettings";
|
||||
info?: ReactNode;
|
||||
}
|
||||
>();
|
||||
if (pages === undefined) {
|
||||
return features;
|
||||
@@ -342,16 +344,16 @@ const $restrictedFeatures = computed(
|
||||
if (!permissions.allowDynamicData) {
|
||||
// pages with dynamic paths
|
||||
for (const page of [pages.homePage, ...pages.pages]) {
|
||||
const awareness = {
|
||||
const navigate = {
|
||||
pageId: page.id,
|
||||
instanceSelector: [page.rootInstanceId],
|
||||
};
|
||||
// allow catch all for 404 pages on free plan
|
||||
if (isPathnamePattern(page.path) && page.path !== "/*") {
|
||||
features.set("Dynamic path", { awareness, view: "pageSettings" });
|
||||
features.set("Dynamic path", { navigate, view: "pageSettings" });
|
||||
}
|
||||
if (page.meta.redirect && page.meta.redirect !== `""`) {
|
||||
features.set("Redirect", { awareness, view: "pageSettings" });
|
||||
features.set("Redirect", { navigate, view: "pageSettings" });
|
||||
}
|
||||
}
|
||||
// has resource variables
|
||||
@@ -359,7 +361,11 @@ const $restrictedFeatures = computed(
|
||||
if (dataSource.type === "resource") {
|
||||
const instanceId = dataSource.scopeInstanceId ?? "";
|
||||
features.set("Resource variable", {
|
||||
awareness: findAwarenessByInstanceId(pages, instances, instanceId),
|
||||
navigate: findPageAndSelectorByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
instanceId
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -410,7 +416,11 @@ const Publish = ({
|
||||
restrictedFeatures: Map<
|
||||
string,
|
||||
| undefined
|
||||
| { awareness?: Awareness; view?: "pageSettings"; info?: ReactNode }
|
||||
| {
|
||||
navigate?: { pageId: string; instanceSelector: string[] };
|
||||
view?: "pageSettings";
|
||||
info?: ReactNode;
|
||||
}
|
||||
>;
|
||||
}) => {
|
||||
const { maxDailyPublishesPerUser } = useStore($permissions);
|
||||
@@ -850,18 +860,21 @@ const UpgradeBanner = ({ hasCustomDomains }: { hasCustomDomains: boolean }) => {
|
||||
<Text variant="regularBold">Following Pro features are used:</Text>
|
||||
<Text as="ul" color="destructive" css={{ paddingLeft: "1em" }}>
|
||||
{Array.from(restrictedFeatures).map(
|
||||
([message, { awareness, view, info } = {}], index) => (
|
||||
([message, { navigate, view, info } = {}], index) => (
|
||||
<li key={index}>
|
||||
<Flex align="center" gap="1">
|
||||
{awareness ? (
|
||||
{navigate ? (
|
||||
<button
|
||||
className={buttonLinkClass}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
$awareness.set(awareness);
|
||||
$selectedPageId.set(navigate.pageId);
|
||||
$selectedInstanceSelector.set(
|
||||
navigate.instanceSelector
|
||||
);
|
||||
if (view === "pageSettings") {
|
||||
setActiveSidebarPanel("pages");
|
||||
$editingPageId.set(awareness.pageId);
|
||||
$editingPageId.set(navigate.pageId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -26,19 +26,16 @@ import {
|
||||
validatePrimitiveValue,
|
||||
type BindingVariant,
|
||||
} from "~/builder/shared/binding-popover";
|
||||
import {
|
||||
$dataSources,
|
||||
$props,
|
||||
$resources,
|
||||
$variableValuesByInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $variableValuesByInstanceSelector } from "~/shared/nano-states";
|
||||
import { $dataSources } from "~/shared/sync/data-stores";
|
||||
import { $props, $resources } from "~/shared/sync/data-stores";
|
||||
import { computeExpression } from "~/shared/data-variables";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import {
|
||||
$selectedInstance,
|
||||
$selectedInstanceKeyWithRoot,
|
||||
$selectedPage,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import {
|
||||
UrlField,
|
||||
MethodField,
|
||||
|
||||
@@ -4,14 +4,12 @@ import { useStore } from "@nanostores/react";
|
||||
import { Box, Combobox, Select, theme } from "@webstudio-is/design-system";
|
||||
import { elementsByTag } from "@webstudio-is/html-data";
|
||||
import { tags } from "@webstudio-is/sdk";
|
||||
import { $selectedInstance, $selectedInstancePath } from "~/shared/awareness";
|
||||
import { $selectedInstance, $selectedInstancePath } from "~/shared/nano-states";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { isTreeSatisfyingContentModel } from "~/shared/content-model";
|
||||
import {
|
||||
$instances,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import { type ControlProps, VerticalLayout } from "../shared";
|
||||
import { FieldLabel } from "../property-label";
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { AlertIcon } from "@webstudio-is/icons";
|
||||
import type { Prop } from "@webstudio-is/sdk";
|
||||
import { showAttribute } from "@webstudio-is/react-sdk";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$selectedInstanceInitialPropNames,
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import {
|
||||
type StyleSourceSelections,
|
||||
} from "@webstudio-is/sdk";
|
||||
|
||||
import { isBaseBreakpoint } from "~/shared/nano-states";
|
||||
import { isBaseBreakpoint } from "~/shared/breakpoints-utils";
|
||||
import { camelCaseProperty } from "@webstudio-is/css-data";
|
||||
|
||||
export const setListedCssProperty =
|
||||
|
||||
+2
-2
@@ -3,10 +3,10 @@ import { nanoid } from "nanoid";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
$hoveredInstanceSelector,
|
||||
$instances,
|
||||
$registeredComponentMetas,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { getInstanceStyleDecl } from "~/builder/features/style-panel/shared/model";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { toValue } from "@webstudio-is/css-engine";
|
||||
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, test, expect } from "vitest";
|
||||
import { matchMediaBreakpoints } from "./match-media-breakpoints";
|
||||
import type { IsEqual } from "type-fest";
|
||||
|
||||
describe("matchMediaBreakpoints", () => {
|
||||
it("returns undefined when values array is undefined", () => {
|
||||
test("returns undefined when values array is undefined", () => {
|
||||
const matchingBreakpointIds = ["mobile", "tablet", "desktop"];
|
||||
const matcher = matchMediaBreakpoints(matchingBreakpointIds);
|
||||
|
||||
expect(matcher(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined when no matching breakpoints are found", () => {
|
||||
test("returns undefined when no matching breakpoints are found", () => {
|
||||
const matchingBreakpointIds = ["mobile", "tablet", "desktop"];
|
||||
const values: Array<[string, number]> = [
|
||||
["other-breakpoint", 100],
|
||||
@@ -22,7 +22,7 @@ describe("matchMediaBreakpoints", () => {
|
||||
expect(matcher(values)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns the value of the last matching breakpoint", () => {
|
||||
test("returns the value of the last matching breakpoint", () => {
|
||||
const matchingBreakpointIds = ["mobile", "tablet"];
|
||||
const values: Array<[string, number]> = [
|
||||
["mobile", 320],
|
||||
@@ -35,7 +35,7 @@ describe("matchMediaBreakpoints", () => {
|
||||
expect(matcher(values)).toBe(768);
|
||||
});
|
||||
|
||||
it("preserves the value type", () => {
|
||||
test("preserves the value type", () => {
|
||||
const matchingBreakpointIds = ["mobile", "tablet", "desktop"];
|
||||
|
||||
const stringValues: Array<[string, string]> = [["mobile", "small"]];
|
||||
|
||||
+5
-9
@@ -9,15 +9,11 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import { PropsSection as PropsSectionComponent } from "./props-section";
|
||||
import { usePropsLogic } from "./use-props-logic";
|
||||
import {
|
||||
$assets,
|
||||
$instances,
|
||||
$pages,
|
||||
$props,
|
||||
registerComponentLibrary,
|
||||
} from "~/shared/nano-states";
|
||||
import { registerComponentLibrary } from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { $instances, $pages, $props } from "~/shared/sync/data-stores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
|
||||
let id = 0;
|
||||
const unique = () => `${++id}`;
|
||||
@@ -95,7 +91,7 @@ const rootInstance = addLinkableSections(
|
||||
["company", "employees"],
|
||||
$pages.get()?.pages[0]
|
||||
);
|
||||
$awareness.set({ pageId: $pages.get()?.homePage.id ?? "" });
|
||||
$selectedPageId.set($pages.get()?.homePage.id ?? "");
|
||||
|
||||
const instance: Instance = {
|
||||
id: instanceId,
|
||||
|
||||
@@ -26,15 +26,15 @@ import {
|
||||
import {
|
||||
$propValuesByInstanceSelector,
|
||||
$propsIndex,
|
||||
$props,
|
||||
$isDesignMode,
|
||||
$isContentMode,
|
||||
$memoryProps,
|
||||
$selectedBreakpoint,
|
||||
} from "~/shared/nano-states";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import { CollapsibleSectionWithAddButton } from "~/builder/shared/collapsible-section";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { $selectedInstance, $selectedInstanceKey } from "~/shared/awareness";
|
||||
import { $selectedInstance, $selectedInstanceKey } from "~/shared/nano-states";
|
||||
import { renderControl } from "../controls/combined";
|
||||
import { usePropsLogic, type PropAndMeta } from "./use-props-logic";
|
||||
import { AnimationSection } from "./animation/animation-section";
|
||||
|
||||
@@ -10,13 +10,13 @@ import {
|
||||
textContentAttribute,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import {
|
||||
$instances,
|
||||
$isContentMode,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $props } from "~/shared/sync/data-stores";
|
||||
import { isRichText } from "~/shared/content-model";
|
||||
import { $selectedInstance, $selectedInstancePath } from "~/shared/awareness";
|
||||
import { $selectedInstance, $selectedInstancePath } from "~/shared/nano-states";
|
||||
import {
|
||||
$selectedInstanceInitialPropNames,
|
||||
$selectedInstancePropsMetas,
|
||||
|
||||
@@ -47,11 +47,9 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import { TrashIcon, InfoCircleIcon, PlusIcon } from "@webstudio-is/icons";
|
||||
import { humanizeString } from "~/shared/string-utils";
|
||||
import {
|
||||
$dataSources,
|
||||
$resources,
|
||||
$variableValuesByInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $variableValuesByInstanceSelector } from "~/shared/nano-states";
|
||||
import { $dataSources } from "~/shared/sync/data-stores";
|
||||
import { $resources } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
BindingControl,
|
||||
BindingPopover,
|
||||
@@ -69,7 +67,7 @@ import {
|
||||
$selectedPage,
|
||||
getInstanceKey,
|
||||
type InstancePath,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { rebindTreeVariablesMutable } from "~/shared/data-variables";
|
||||
import { parseCurl, type CurlRequest } from "./curl";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { InputField } from "@webstudio-is/design-system";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { HorizontalLayout, Label, Row, useLocalValue } from "./shared";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import { getInstanceLabel } from "~/builder/shared/instance-label";
|
||||
|
||||
const saveLabel = (label: string, selectedInstance: Instance) => {
|
||||
|
||||
@@ -42,16 +42,16 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import {
|
||||
$dataSourceVariables,
|
||||
$dataSources,
|
||||
$registeredComponentMetas,
|
||||
$variableValuesByInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $dataSources } from "~/shared/sync/data-stores";
|
||||
import type { BindingVariant } from "~/builder/shared/binding-popover";
|
||||
import { humanizeString } from "~/shared/string-utils";
|
||||
import {
|
||||
$selectedInstance,
|
||||
$selectedInstanceKeyWithRoot,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { $instanceTags } from "../style-panel/shared/model";
|
||||
|
||||
export const showAttributeMeta: PropMeta = {
|
||||
|
||||
@@ -54,17 +54,15 @@ import {
|
||||
formatValue,
|
||||
} from "~/builder/shared/expression-editor";
|
||||
import {
|
||||
$dataSources,
|
||||
$resources,
|
||||
$permissions,
|
||||
$instances,
|
||||
$props,
|
||||
$variableValuesByInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $dataSources } from "~/shared/sync/data-stores";
|
||||
import { $resources, $instances, $props } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$selectedInstance,
|
||||
$selectedInstanceKeyWithRoot,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import {
|
||||
EditorContent,
|
||||
EditorDialog,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $pages, $instances } from "~/shared/sync/data-stores";
|
||||
import { $planFeatures } from "~/shared/nano-states";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import { VariablesSection as VariablesSectionComponent } from "./variables-section";
|
||||
|
||||
$planFeatures.set({
|
||||
@@ -24,7 +24,8 @@ $instances.set(
|
||||
])
|
||||
);
|
||||
$pages.set(createDefaultPages({ rootInstanceId: "box" }));
|
||||
$awareness.set({ pageId: "home", instanceSelector: ["box"] });
|
||||
$selectedPageId.set("home");
|
||||
// awareness not needed for stories - instanceSelector set via selectInstance if required
|
||||
|
||||
export const VariablesSection: StoryObj = {
|
||||
render: () => (
|
||||
|
||||
@@ -22,14 +22,14 @@ import {
|
||||
import { EllipsesIcon, PlusIcon } from "@webstudio-is/icons";
|
||||
import type { DataSource } from "@webstudio-is/sdk";
|
||||
import { findPageByIdOrPath } from "@webstudio-is/sdk";
|
||||
import { $variableValuesByInstanceSelector } from "~/shared/nano-states";
|
||||
import { $dataSources } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$dataSources,
|
||||
$instances,
|
||||
$pages,
|
||||
$props,
|
||||
$resources,
|
||||
$variableValuesByInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import {
|
||||
CollapsibleSectionRoot,
|
||||
useOpenState,
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
$selectedInstance,
|
||||
$selectedInstanceKeyWithRoot,
|
||||
$selectedPage,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import {
|
||||
deleteVariableMutable,
|
||||
|
||||
@@ -17,12 +17,9 @@ import {
|
||||
IconLink,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { humanizeString } from "~/shared/string-utils";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$registeredComponentMetas,
|
||||
$styleSources,
|
||||
} from "~/shared/nano-states";
|
||||
import { $registeredComponentMetas } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { $instances, $styleSources } from "~/shared/sync/data-stores";
|
||||
import type {
|
||||
ComputedStyleDecl,
|
||||
StyleValueSourceColor,
|
||||
@@ -31,7 +28,7 @@ import { useComputedStyles } from "./shared/model";
|
||||
import { useReadonly } from "./shared/readonly";
|
||||
import { StyleSourceBadge } from "./style-source";
|
||||
import { createBatchUpdate } from "./shared/use-style-data";
|
||||
import { $virtualInstances } from "~/shared/awareness";
|
||||
import { $virtualInstances } from "~/shared/nano-states";
|
||||
import { getInstanceLabel } from "~/builder/shared/instance-label";
|
||||
|
||||
const $isAltPressed = atom(false);
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useComputedStyles } from "../../shared/model";
|
||||
import { getDots } from "../../shared/style-section";
|
||||
import { CssEditor } from "../../../../shared/css-editor";
|
||||
import { $advancedStyleDeclarations } from "./stores";
|
||||
import { $selectedInstanceKey } from "~/shared/awareness";
|
||||
import { $selectedInstanceKey } from "~/shared/nano-states";
|
||||
import { useReadonly } from "../../shared/readonly";
|
||||
|
||||
// Only here to keep the same section module interface
|
||||
|
||||
@@ -2,7 +2,7 @@ import { computed } from "nanostores";
|
||||
import type { CssProperty } from "@webstudio-is/css-engine";
|
||||
import { ROOT_INSTANCE_ID } from "@webstudio-is/sdk";
|
||||
import { $settings } from "~/builder/shared/client-settings";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import type { ComputedStyleDecl } from "~/shared/style-object-model";
|
||||
import { $computedStyleDeclarations } from "../../shared/model";
|
||||
import { sections } from "../sections";
|
||||
|
||||
+9
-8
@@ -7,18 +7,21 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { getStyleDeclKey, type StyleDecl } from "@webstudio-is/sdk";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { Section } from "./backdrop-filter";
|
||||
|
||||
registerContainers();
|
||||
@@ -39,10 +42,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const blurBackdropFilter: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
|
||||
+9
-8
@@ -8,17 +8,20 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { BackgroundContent as BackgroundContentPanel } from "./background-content";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { useComputedStyleDecl } from "../../shared/model";
|
||||
import { setRepeatedStyleItem } from "../../shared/repeated-style";
|
||||
import { type StyleValue } from "@webstudio-is/css-engine";
|
||||
@@ -55,10 +58,8 @@ $styles.set(
|
||||
new Map([[getStyleDeclKey(defaultBackgroundImage), defaultBackgroundImage]])
|
||||
);
|
||||
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const BackgroundStory = ({ styleValue }: { styleValue: StyleValue }) => {
|
||||
const backgroundImage = useComputedStyleDecl("background-image");
|
||||
|
||||
+9
-8
@@ -8,15 +8,18 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { Section as SectionComponent } from "./backgrounds";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
|
||||
const backgroundImage: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
@@ -53,10 +56,8 @@ $instances.set(
|
||||
["box", { type: "instance", id: "box", component: "Box", children: [] }],
|
||||
])
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const SingleLayerVariant = () => {
|
||||
useEffect(() => {
|
||||
|
||||
@@ -7,18 +7,21 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { getStyleDeclKey, type StyleDecl } from "@webstudio-is/sdk";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { Section } from "./borders";
|
||||
|
||||
registerContainers();
|
||||
@@ -39,10 +42,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const solidBorderTop: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
|
||||
+9
-8
@@ -7,18 +7,21 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { getStyleDeclKey, type StyleDecl } from "@webstudio-is/sdk";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { Section } from "./box-shadows";
|
||||
|
||||
registerContainers();
|
||||
@@ -39,10 +42,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const outerShadow: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
|
||||
@@ -7,18 +7,21 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { getStyleDeclKey, type StyleDecl } from "@webstudio-is/sdk";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { Section } from "./filter";
|
||||
|
||||
registerContainers();
|
||||
@@ -39,10 +42,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const blurFilterStyle: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
|
||||
@@ -32,7 +32,8 @@ import { createBatchUpdate } from "../../shared/use-style-data";
|
||||
import { useReadonly } from "../../shared/readonly";
|
||||
import { AlignSelfControl } from "../shared/align-self";
|
||||
import { OrderControl } from "../shared/order";
|
||||
import { $selectedInstancePath, selectInstance } from "~/shared/awareness";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
|
||||
export const properties = [
|
||||
"flex-shrink",
|
||||
|
||||
@@ -42,7 +42,8 @@ import { useLocalValue } from "../../../settings-panel/shared";
|
||||
import { AlignSelfControl, JustifySelfControl } from "../shared/align-self";
|
||||
import { OrderControl } from "../shared/order";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { $selectedInstancePath, selectInstance } from "~/shared/awareness";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { $isStylePanelGridVisible } from "~/builder/shared/nano-states";
|
||||
import { ExternalLinkIcon } from "@webstudio-is/icons";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState, type JSX, type ReactNode } from "react";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { $selectedInstanceKey } from "~/shared/awareness";
|
||||
import { $selectedInstanceKey } from "~/shared/nano-states";
|
||||
import {
|
||||
theme,
|
||||
Box,
|
||||
|
||||
+4
-6
@@ -26,12 +26,10 @@ import {
|
||||
import { useComputedStyleDecl } from "../../../shared/model";
|
||||
import { createBatchUpdate } from "../../../shared/use-style-data";
|
||||
import { useReadonly } from "../../../shared/readonly";
|
||||
import {
|
||||
$breakpoints,
|
||||
$gridCellData,
|
||||
$instances,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $gridCellData } from "~/shared/nano-states";
|
||||
import { $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { DEFAULT_GRID_TRACK_COUNT } from "./constants";
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import {
|
||||
} from "@webstudio-is/css-data";
|
||||
import { PlusIcon, MinusIcon } from "@webstudio-is/icons";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import { useReadonly } from "../../../shared/readonly";
|
||||
import { useComputedStyleDecl } from "../../../shared/model";
|
||||
import { CssValueInputContainer } from "../../../shared/css-value-input";
|
||||
|
||||
@@ -7,18 +7,21 @@ import {
|
||||
theme,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { getStyleDeclKey, type StyleDecl } from "@webstudio-is/sdk";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { Section } from "./outline";
|
||||
|
||||
registerContainers();
|
||||
@@ -39,10 +42,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
const solidOutline: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
|
||||
+8
-7
@@ -4,15 +4,18 @@ import { getStyleDeclKey, StyleDecl } from "@webstudio-is/sdk";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { InsetControl } from "./inset-control";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
|
||||
const top: StyleDecl = {
|
||||
breakpointId: "base",
|
||||
@@ -88,10 +91,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
export const Inset = () => {
|
||||
return (
|
||||
|
||||
+9
-8
@@ -1,16 +1,19 @@
|
||||
import { Box, StorySection, theme } from "@webstudio-is/design-system";
|
||||
import { getStyleDeclKey, StyleDecl } from "@webstudio-is/sdk";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$pages,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { Section } from "./transitions";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
|
||||
const transitionProperty: StyleDecl = {
|
||||
@@ -53,10 +56,8 @@ $pages.set(
|
||||
rootInstanceId: "box",
|
||||
})
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "homePageId",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("homePageId");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
|
||||
export const Transitions = () => (
|
||||
<StorySection title="Transitions">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { map } from "nanostores";
|
||||
import { useCallback } from "react";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
|
||||
const instancesKv = map<Record<string, unknown>>({});
|
||||
|
||||
|
||||
@@ -22,15 +22,17 @@ import {
|
||||
} from "@webstudio-is/sdk";
|
||||
import { rootComponent } from "@webstudio-is/sdk";
|
||||
import {
|
||||
$breakpoints,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
$selectedBreakpoint,
|
||||
$selectedInstanceStates,
|
||||
$selectedOrLastStyleSourceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$props,
|
||||
$styles,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import {
|
||||
getComputedStyleDecl,
|
||||
getPresetStyleDeclKey,
|
||||
@@ -40,7 +42,7 @@ import {
|
||||
import {
|
||||
$selectedInstancePathWithRoot,
|
||||
type InstancePath,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import type { InstanceSelector } from "~/shared/tree-utils";
|
||||
|
||||
const $presetStyles = computed($registeredComponentMetas, (metas) => {
|
||||
|
||||
@@ -18,18 +18,21 @@ import {
|
||||
} from "./repeated-style";
|
||||
import { createComputedStyleDeclStore } from "./model";
|
||||
import { parseCssFragment } from "./css-fragment";
|
||||
import { $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$selectedBreakpointId,
|
||||
$styles,
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { setProperty } from "./use-style-data";
|
||||
import type { ComputedStyleDecl } from "~/shared/style-object-model";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
|
||||
setEnv("*");
|
||||
registerContainers();
|
||||
@@ -37,10 +40,8 @@ registerContainers();
|
||||
beforeEach(() => {
|
||||
$breakpoints.set(new Map([["base", { id: "base", label: "" }]]));
|
||||
$selectedBreakpointId.set("base");
|
||||
$awareness.set({
|
||||
pageId: "",
|
||||
instanceSelector: ["box"],
|
||||
});
|
||||
$selectedPageId.set("");
|
||||
$selectedInstanceSelector.set(["box"]);
|
||||
$instances.set(
|
||||
new Map([
|
||||
["box", { type: "instance", id: "box", component: "Box", children: [] }],
|
||||
|
||||
@@ -2,15 +2,20 @@ import { enableMapSet } from "immer";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$selectedBreakpointId,
|
||||
$selectedStyleSources,
|
||||
} from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$instances,
|
||||
$styleSourceSelections,
|
||||
$styleSources,
|
||||
$styles,
|
||||
} from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import { deleteProperty, setProperty } from "./use-style-data";
|
||||
import { getStyleDeclKey, type StyleDecl } from "@webstudio-is/sdk";
|
||||
|
||||
@@ -31,10 +36,8 @@ const setupSelection = (styleSource: {
|
||||
],
|
||||
])
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "",
|
||||
instanceSelector: ["body"],
|
||||
});
|
||||
$selectedPageId.set("");
|
||||
$selectedInstanceSelector.set(["body"]);
|
||||
$breakpoints.set(new Map([["base", { id: "base", label: "Base" }]]));
|
||||
$selectedBreakpointId.set("base");
|
||||
$styleSources.set(new Map([[styleSource.id, styleSource]]));
|
||||
|
||||
@@ -5,13 +5,15 @@ import {
|
||||
$selectedBreakpoint,
|
||||
$selectedOrLastStyleSourceSelector,
|
||||
$selectedStyleSource,
|
||||
} from "~/shared/nano-states";
|
||||
import {
|
||||
$styleSourceSelections,
|
||||
$styleSources,
|
||||
$styles,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { $ephemeralStyles } from "~/canvas/stores";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import { isStyleSourceLocked } from "~/shared/style-source-utils";
|
||||
|
||||
type StyleUpdate =
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from "~/shared/nano-states";
|
||||
import { isStyleSourceLocked } from "~/shared/style-source-utils";
|
||||
import { ReadonlyProvider } from "./shared/readonly";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import { CollapsibleProvider } from "~/builder/shared/collapsible-section";
|
||||
import {
|
||||
$settings,
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { enableMapSet } from "immer";
|
||||
import { expect, test, describe } from "vitest";
|
||||
import { registerContainers } from "~/shared/sync/sync-stores";
|
||||
import { $selectedStyleSources } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$instances,
|
||||
$selectedStyleSources,
|
||||
$styleSourceSelections,
|
||||
$styleSources,
|
||||
$styles,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { addStyleSourceToInstance, __testing__ } from "./style-source-section";
|
||||
import { $awareness } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedPageId,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
|
||||
const { duplicateStyleSource, getComponentStates } = __testing__;
|
||||
|
||||
@@ -154,10 +157,8 @@ test("add style source to instance", () => {
|
||||
],
|
||||
])
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "",
|
||||
instanceSelector: ["body"],
|
||||
});
|
||||
$selectedPageId.set("");
|
||||
$selectedInstanceSelector.set(["body"]);
|
||||
$styleSources.set(new Map([["local1", { id: "local1", type: "local" }]]));
|
||||
$styleSourceSelections.set(new Map());
|
||||
$selectedStyleSources.set(new Map());
|
||||
@@ -193,10 +194,8 @@ test("duplicate locked style source creates an unlocked copy", () => {
|
||||
],
|
||||
])
|
||||
);
|
||||
$awareness.set({
|
||||
pageId: "",
|
||||
instanceSelector: ["body"],
|
||||
});
|
||||
$selectedPageId.set("");
|
||||
$selectedInstanceSelector.set(["body"]);
|
||||
$styleSources.set(
|
||||
new Map([
|
||||
[
|
||||
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
getStyleDeclKey,
|
||||
} from "@webstudio-is/sdk";
|
||||
import { type ItemSource, StyleSourceInput } from "./style-source";
|
||||
import { type RenameStyleSourceError } from "~/shared/style-source-utils";
|
||||
import {
|
||||
renameStyleSource,
|
||||
type RenameStyleSourceError,
|
||||
deleteStyleSource,
|
||||
DeleteStyleSourceDialog,
|
||||
setStyleSourceLocked,
|
||||
@@ -28,15 +28,17 @@ import {
|
||||
$selectedOrLastStyleSourceSelector,
|
||||
$selectedStyleSources,
|
||||
$selectedStyleState,
|
||||
} from "~/shared/nano-states";
|
||||
import {
|
||||
$styleSourceSelections,
|
||||
$styleSources,
|
||||
$styles,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { removeByMutable } from "~/shared/array-utils";
|
||||
import { cloneStyles } from "~/shared/tree-utils";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { subscribe } from "~/shared/pubsub";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
import { $instanceTags } from "./shared/model";
|
||||
|
||||
// Declare command for this module
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import { OfflineIcon } from "@webstudio-is/icons";
|
||||
import { useEffect } from "react";
|
||||
import { $queueStatus } from "~/shared/sync/project-queue";
|
||||
import { $syncStatus } from "@webstudio-is/sync-client";
|
||||
|
||||
const $isOnline = atom(false);
|
||||
|
||||
@@ -24,13 +24,13 @@ const subscribeIsOnline = () => {
|
||||
};
|
||||
|
||||
export const SyncStatus = () => {
|
||||
const statusObject = useStore($queueStatus);
|
||||
const statusObject = useStore($syncStatus);
|
||||
const isOnline = useStore($isOnline);
|
||||
useEffect(subscribeIsOnline, []);
|
||||
|
||||
if (
|
||||
statusObject.status === "idle" ||
|
||||
statusObject.status === "running" ||
|
||||
statusObject.status === "syncing" ||
|
||||
statusObject.status === "recovering"
|
||||
) {
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -2,13 +2,13 @@ import { useStore } from "@nanostores/react";
|
||||
import { styled } from "@webstudio-is/design-system";
|
||||
|
||||
import {
|
||||
$instances,
|
||||
$modifierKeys,
|
||||
$textEditingInstanceSelector,
|
||||
$textEditorContextMenu,
|
||||
$textEditorContextMenuCommand,
|
||||
findTemplates,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { applyScale } from "./outline";
|
||||
import { $scale } from "~/builder/shared/nano-states";
|
||||
import { TemplatesMenu } from "./outline/block-instance-outline";
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { $instanceContextMenu } from "~/shared/nano-states";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { selectInstance } from "~/shared/awareness";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useStore } from "@nanostores/react";
|
||||
import { css } from "@webstudio-is/design-system";
|
||||
import { PlacementIndicator } from "@webstudio-is/design-system";
|
||||
import {
|
||||
$instances,
|
||||
$isPreviewMode,
|
||||
$dragAndDropState,
|
||||
$canvasToolsVisible,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
CollaborativeInstanceOutline,
|
||||
HoveredInstanceOutline,
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
import { BlockChildHoveredInstanceOutline } from "./outline/block-instance-outline";
|
||||
import { TextEditorContextMenu } from "./block-editor-context-menu";
|
||||
import { CanvasInstanceContextMenu } from "./canvas-instance-context-menu";
|
||||
import { CollaborativeCursors } from "./collaborative-cursors";
|
||||
|
||||
const containerStyle = css({
|
||||
position: "absolute",
|
||||
@@ -86,6 +87,7 @@ export const CanvasTools = () => {
|
||||
<>
|
||||
{isPreviewMode === false && (
|
||||
<>
|
||||
<CollaborativeCursors />
|
||||
{isStylePanelGridVisible && <GridGuides />}
|
||||
<SelectedInstanceOutline />
|
||||
<HoveredInstanceOutline />
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { __testing__ } from "./collaborative-cursors";
|
||||
|
||||
const {
|
||||
shouldSkipCollaborator,
|
||||
hasCursorPosition,
|
||||
computeCursorCoordinates,
|
||||
isWithinLayer,
|
||||
toRenderableCollaboratorCursor,
|
||||
} = __testing__;
|
||||
|
||||
describe("collaborative-cursors helpers", () => {
|
||||
test("shouldSkipCollaborator returns true only for different known pages", () => {
|
||||
expect(
|
||||
shouldSkipCollaborator({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-b",
|
||||
})
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldSkipCollaborator({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-a",
|
||||
})
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldSkipCollaborator({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: undefined,
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test("hasCursorPosition returns true when pointer position is set", () => {
|
||||
expect(hasCursorPosition(undefined)).toBe(false);
|
||||
expect(hasCursorPosition({ x: 10, y: 20, xRatio: 0.1, yRatio: 0.2 })).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("computeCursorCoordinates maps normalized coordinates using canvas rect", () => {
|
||||
const layerRect = { left: 0, top: 0, width: 1200, height: 800 };
|
||||
const canvasRect = { left: 100, top: 200, width: 800, height: 600 };
|
||||
|
||||
const result = computeCursorCoordinates({
|
||||
pointerPosition: { x: 0, y: 0, xRatio: 0.5, yRatio: 0.25 },
|
||||
layerRect,
|
||||
canvasRect,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ x: 500, y: 350 });
|
||||
});
|
||||
|
||||
test("computeCursorCoordinates falls back to layer rect when canvas rect is missing", () => {
|
||||
const layerRect = { left: 0, top: 0, width: 1000, height: 800 };
|
||||
|
||||
const result = computeCursorCoordinates({
|
||||
pointerPosition: { x: 0, y: 0, xRatio: 0.5, yRatio: 0.25 },
|
||||
layerRect,
|
||||
canvasRect: undefined,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ x: 500, y: 200 });
|
||||
});
|
||||
|
||||
test("computeCursorCoordinates uses ratios against layer rect when canvas rect is missing", () => {
|
||||
const layerRect = { left: 50, top: 100, width: 1000, height: 800 };
|
||||
|
||||
const result = computeCursorCoordinates({
|
||||
pointerPosition: { x: 350, y: 500, xRatio: 0.3, yRatio: 0.5 },
|
||||
layerRect,
|
||||
canvasRect: undefined,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ x: 300, y: 400 });
|
||||
});
|
||||
|
||||
test("isWithinLayer bounds-checks computed coordinates", () => {
|
||||
const layerRect = { left: 0, top: 0, width: 500, height: 400 };
|
||||
|
||||
expect(isWithinLayer({ x: 10, y: 10, layerRect })).toBe(true);
|
||||
expect(isWithinLayer({ x: -1, y: 10, layerRect })).toBe(false);
|
||||
expect(isWithinLayer({ x: 10, y: 401, layerRect })).toBe(false);
|
||||
expect(isWithinLayer({ x: 999, y: 999, layerRect: undefined })).toBe(true);
|
||||
});
|
||||
|
||||
test("toRenderableCollaboratorCursor returns coordinates only for visible collaborator cursor", () => {
|
||||
const layerRect = { left: 0, top: 0, width: 1200, height: 800 };
|
||||
const canvasRect = { left: 100, top: 200, width: 800, height: 600 };
|
||||
|
||||
expect(
|
||||
toRenderableCollaboratorCursor({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-b",
|
||||
pointerPosition: { x: 10, y: 20, xRatio: 0, yRatio: 0 },
|
||||
layerRect,
|
||||
canvasRect,
|
||||
})
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
toRenderableCollaboratorCursor({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-a",
|
||||
pointerPosition: undefined,
|
||||
layerRect,
|
||||
canvasRect,
|
||||
})
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
toRenderableCollaboratorCursor({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-a",
|
||||
pointerPosition: { x: 0, y: 0, xRatio: 0.5, yRatio: 0.25 },
|
||||
layerRect,
|
||||
canvasRect,
|
||||
})
|
||||
).toEqual({ x: 500, y: 350 });
|
||||
});
|
||||
|
||||
test("toRenderableCollaboratorCursor recalculates position when receiver canvas changes", () => {
|
||||
const layerRect = { left: 0, top: 0, width: 1400, height: 1000 };
|
||||
const normalized = { xRatio: 0.25, yRatio: 0.75 };
|
||||
|
||||
const firstReceiverCanvasRect = {
|
||||
left: 100,
|
||||
top: 100,
|
||||
width: 800,
|
||||
height: 600,
|
||||
};
|
||||
const secondReceiverCanvasRect = {
|
||||
left: 200,
|
||||
top: 150,
|
||||
width: 1200,
|
||||
height: 900,
|
||||
};
|
||||
|
||||
const firstRender = toRenderableCollaboratorCursor({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-a",
|
||||
pointerPosition: { x: 0, y: 0, ...normalized },
|
||||
layerRect,
|
||||
canvasRect: firstReceiverCanvasRect,
|
||||
});
|
||||
|
||||
const secondRender = toRenderableCollaboratorCursor({
|
||||
currentPageId: "page-a",
|
||||
collaboratorPageId: "page-a",
|
||||
pointerPosition: { x: 0, y: 0, ...normalized },
|
||||
layerRect,
|
||||
canvasRect: secondReceiverCanvasRect,
|
||||
});
|
||||
|
||||
expect(firstRender).toEqual({ x: 300, y: 550 });
|
||||
expect(secondRender).toEqual({ x: 500, y: 825 });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,245 @@
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { css, type Rect } from "@webstudio-is/design-system";
|
||||
import {
|
||||
$collaborators,
|
||||
assignCollaboratorColors,
|
||||
type CollaboratorInfo,
|
||||
} from "@webstudio-is/sync-client";
|
||||
import {
|
||||
$collaborativeInstanceSelector,
|
||||
$selectedPageId,
|
||||
} from "~/shared/nano-states";
|
||||
import { $canvasRect } from "~/builder/shared/nano-states";
|
||||
|
||||
const layerStyle = css({
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
pointerEvents: "none",
|
||||
});
|
||||
|
||||
const cursorStyle = css({
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
transformOrigin: "top left",
|
||||
transition: "transform 300ms ease-out",
|
||||
willChange: "transform",
|
||||
});
|
||||
|
||||
const shouldSkipCollaborator = ({
|
||||
currentPageId,
|
||||
collaboratorPageId,
|
||||
}: {
|
||||
currentPageId: string | undefined;
|
||||
collaboratorPageId: string | undefined;
|
||||
}) => {
|
||||
return (
|
||||
currentPageId !== undefined &&
|
||||
collaboratorPageId !== undefined &&
|
||||
collaboratorPageId !== currentPageId
|
||||
);
|
||||
};
|
||||
|
||||
const hasCursorPosition = (
|
||||
pointerPosition: CollaboratorInfo["pointerPosition"]
|
||||
) => {
|
||||
return pointerPosition !== undefined;
|
||||
};
|
||||
|
||||
const computeCursorCoordinates = ({
|
||||
pointerPosition,
|
||||
layerRect,
|
||||
canvasRect,
|
||||
}: {
|
||||
pointerPosition: CollaboratorInfo["pointerPosition"];
|
||||
layerRect: Rect | undefined;
|
||||
canvasRect: Rect | undefined;
|
||||
}) => {
|
||||
if (pointerPosition === undefined || layerRect === undefined) {
|
||||
return { x: 0, y: 0 };
|
||||
}
|
||||
if (canvasRect !== undefined) {
|
||||
return {
|
||||
x:
|
||||
canvasRect.left -
|
||||
layerRect.left +
|
||||
pointerPosition.xRatio * canvasRect.width,
|
||||
y:
|
||||
canvasRect.top -
|
||||
layerRect.top +
|
||||
pointerPosition.yRatio * canvasRect.height,
|
||||
};
|
||||
}
|
||||
return {
|
||||
x: pointerPosition.xRatio * layerRect.width,
|
||||
y: pointerPosition.yRatio * layerRect.height,
|
||||
};
|
||||
};
|
||||
|
||||
const isWithinLayer = ({
|
||||
x,
|
||||
y,
|
||||
layerRect,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
layerRect: Rect | undefined;
|
||||
}) => {
|
||||
if (layerRect === undefined) {
|
||||
return true;
|
||||
}
|
||||
return x >= 0 && x < layerRect.width && y >= 0 && y < layerRect.height;
|
||||
};
|
||||
|
||||
const toRenderableCollaboratorCursor = ({
|
||||
currentPageId,
|
||||
collaboratorPageId,
|
||||
pointerPosition,
|
||||
layerRect,
|
||||
canvasRect,
|
||||
}: {
|
||||
currentPageId: string | undefined;
|
||||
collaboratorPageId: string | undefined;
|
||||
pointerPosition: CollaboratorInfo["pointerPosition"];
|
||||
layerRect: Rect | undefined;
|
||||
canvasRect: Rect | undefined;
|
||||
}) => {
|
||||
if (shouldSkipCollaborator({ currentPageId, collaboratorPageId })) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasCursorPosition(pointerPosition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coordinates = computeCursorCoordinates({
|
||||
pointerPosition,
|
||||
layerRect,
|
||||
canvasRect,
|
||||
});
|
||||
|
||||
if (!isWithinLayer({ ...coordinates, layerRect })) {
|
||||
return;
|
||||
}
|
||||
|
||||
return coordinates;
|
||||
};
|
||||
|
||||
const areInstanceSelectorsEqual = (
|
||||
left: readonly string[] | undefined,
|
||||
right: readonly string[] | undefined
|
||||
) => {
|
||||
if (left === right) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
left === undefined ||
|
||||
right === undefined ||
|
||||
left.length !== right.length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return left.every((id, index) => id === right[index]);
|
||||
};
|
||||
|
||||
export const CollaborativeCursors = () => {
|
||||
const collaborators = useStore($collaborators);
|
||||
const canvasRect = useStore($canvasRect);
|
||||
const currentPageId = useStore($selectedPageId);
|
||||
const [layerElement, setLayerElement] = useState<
|
||||
HTMLDivElement | null | undefined
|
||||
>(undefined);
|
||||
const layerRect = layerElement?.getBoundingClientRect();
|
||||
const collaboratorColors = useMemo(
|
||||
() => assignCollaboratorColors(collaborators.keys()),
|
||||
[collaborators]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const collaborativeSelection = [...collaborators.values()].find(
|
||||
(collaborator) => {
|
||||
if (
|
||||
shouldSkipCollaborator({
|
||||
currentPageId,
|
||||
collaboratorPageId: collaborator.pageId,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
collaborator.selectedInstanceIds !== undefined &&
|
||||
collaborator.selectedInstanceIds.length > 0
|
||||
);
|
||||
}
|
||||
)?.selectedInstanceIds;
|
||||
|
||||
const currentCollaborativeSelection = $collaborativeInstanceSelector.get();
|
||||
if (
|
||||
areInstanceSelectorsEqual(
|
||||
currentCollaborativeSelection,
|
||||
collaborativeSelection
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$collaborativeInstanceSelector.set(collaborativeSelection);
|
||||
}, [collaborators, currentPageId]);
|
||||
|
||||
return (
|
||||
<div className={layerStyle()} ref={setLayerElement}>
|
||||
{[...collaborators.entries()].map(([clientId, collaborator]) => {
|
||||
const coordinates = toRenderableCollaboratorCursor({
|
||||
currentPageId,
|
||||
collaboratorPageId: collaborator.pageId,
|
||||
pointerPosition: collaborator.pointerPosition,
|
||||
layerRect,
|
||||
canvasRect,
|
||||
});
|
||||
|
||||
if (coordinates === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { x, y } = coordinates;
|
||||
|
||||
const color = collaboratorColors.get(clientId);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={clientId}
|
||||
className={cursorStyle()}
|
||||
style={{
|
||||
color,
|
||||
transform: `translate3d(${x}px, ${y}px, 0)`,
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="20"
|
||||
viewBox="0 0 14 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1 1.5L1 17.7L4.8 13.4L8 19L11 17.4L7.8 11.8L13 11.8L1 1.5Z"
|
||||
fill="currentColor"
|
||||
stroke="white"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const __testing__ = {
|
||||
shouldSkipCollaborator,
|
||||
hasCursorPosition,
|
||||
computeCursorCoordinates,
|
||||
isWithinLayer,
|
||||
toRenderableCollaboratorCursor,
|
||||
};
|
||||
+2
-2
@@ -23,13 +23,13 @@ import {
|
||||
$blockChildOutline,
|
||||
$hoveredInstanceOutline,
|
||||
$hoveredInstanceSelector,
|
||||
$instances,
|
||||
$isContentMode,
|
||||
$modifierKeys,
|
||||
findBlockSelector,
|
||||
findTemplates,
|
||||
type BlockChildOutline,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $clampingRect, $scale } from "~/builder/shared/nano-states";
|
||||
import type { InstanceSelector } from "~/shared/tree-utils";
|
||||
import {
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
} from "~/shared/instance-utils";
|
||||
import { skipInertHandlersAttribute } from "~/builder/shared/inert-handlers";
|
||||
import { useEffectEvent } from "~/shared/hook-utils/effect-event";
|
||||
import { getInstancePath } from "~/shared/awareness";
|
||||
import { getInstancePath } from "~/shared/nano-states";
|
||||
import { insertTemplateAt } from "./block-utils";
|
||||
import { Outline } from "./outline";
|
||||
import { applyScale } from "../apply-scale";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Instance, Instances } from "@webstudio-is/sdk";
|
||||
import { blockTemplateComponent } from "@webstudio-is/sdk";
|
||||
import { shallowEqual } from "shallow-equal";
|
||||
import { selectInstance } from "~/shared/awareness";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { builderApi } from "~/shared/builder-api";
|
||||
import { findAvailableVariables } from "~/shared/data-variables";
|
||||
import {
|
||||
@@ -14,13 +14,13 @@ import {
|
||||
detectFragmentTokenConflicts,
|
||||
} from "~/shared/instance-utils";
|
||||
import {
|
||||
$instances,
|
||||
$project,
|
||||
$registeredComponentMetas,
|
||||
$textEditingInstanceSelector,
|
||||
findBlockChildSelector,
|
||||
findBlockSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $project } from "~/shared/sync/data-stores";
|
||||
import type { DroppableTarget, InstanceSelector } from "~/shared/tree-utils";
|
||||
|
||||
const getInsertionIndex = (
|
||||
|
||||
+59
-2
@@ -1,16 +1,51 @@
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { $collaborativeInstanceRect } from "~/shared/nano-states";
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
$collaborators,
|
||||
assignCollaboratorColors,
|
||||
} from "@webstudio-is/sync-client";
|
||||
import {
|
||||
$collaborativeInstanceRect,
|
||||
$collaborativeInstanceSelector,
|
||||
$selectedPageId,
|
||||
} from "~/shared/nano-states";
|
||||
import { Outline } from "./outline";
|
||||
import { applyScale } from "../apply-scale";
|
||||
import { $scale, $clampingRect } from "~/builder/shared/nano-states";
|
||||
import { $ephemeralStyles } from "~/canvas/stores";
|
||||
|
||||
const areSelectorsEqual = (
|
||||
left: readonly string[] | undefined,
|
||||
right: readonly string[] | undefined
|
||||
) => {
|
||||
if (left === right) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
left === undefined ||
|
||||
right === undefined ||
|
||||
left.length !== right.length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return left.every((id, index) => id === right[index]);
|
||||
};
|
||||
|
||||
// Outline of an instance that is being edited by AI or a human collaborator.
|
||||
export const CollaborativeInstanceOutline = () => {
|
||||
const scale = useStore($scale);
|
||||
const instanceRect = useStore($collaborativeInstanceRect);
|
||||
const collaborativeInstanceSelector = useStore(
|
||||
$collaborativeInstanceSelector
|
||||
);
|
||||
const collaborators = useStore($collaborators);
|
||||
const currentPageId = useStore($selectedPageId);
|
||||
const ephemeralStyles = useStore($ephemeralStyles);
|
||||
const clampingRect = useStore($clampingRect);
|
||||
const collaboratorColors = useMemo(
|
||||
() => assignCollaboratorColors(collaborators.keys()),
|
||||
[collaborators]
|
||||
);
|
||||
|
||||
if (
|
||||
instanceRect === undefined ||
|
||||
@@ -22,11 +57,33 @@ export const CollaborativeInstanceOutline = () => {
|
||||
|
||||
const rect = applyScale(instanceRect, scale);
|
||||
|
||||
const selectedCollaborator = [...collaborators.entries()].find(
|
||||
([, collaborator]) => {
|
||||
if (
|
||||
currentPageId !== undefined &&
|
||||
collaborator.pageId !== undefined &&
|
||||
collaborator.pageId !== currentPageId
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return areSelectorsEqual(
|
||||
collaborator.selectedInstanceIds,
|
||||
collaborativeInstanceSelector
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const color =
|
||||
selectedCollaborator === undefined
|
||||
? undefined
|
||||
: collaboratorColors.get(selectedCollaborator[0]);
|
||||
|
||||
return (
|
||||
<Outline
|
||||
variant="collaboration"
|
||||
variant="default"
|
||||
rect={rect}
|
||||
clampingRect={clampingRect}
|
||||
color={color}
|
||||
></Outline>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -4,10 +4,10 @@ import {
|
||||
$blockChildOutline,
|
||||
$hoveredInstanceOutlineAndInstance,
|
||||
$hoveredInstanceSelector,
|
||||
$instances,
|
||||
$isContentMode,
|
||||
$textEditingInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $clampingRect, $scale } from "~/builder/shared/nano-states";
|
||||
import { findClosestSlot } from "~/shared/instance-utils";
|
||||
import { isDescendantOrSelf } from "~/shared/tree-utils";
|
||||
|
||||
@@ -92,6 +92,7 @@ type OutlineProps = {
|
||||
rect: Rect;
|
||||
clampingRect: Rect;
|
||||
variant?: "default" | "collaboration" | "slot";
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export const Outline = ({
|
||||
@@ -99,6 +100,7 @@ export const Outline = ({
|
||||
rect,
|
||||
clampingRect,
|
||||
variant,
|
||||
color,
|
||||
}: OutlineProps) => {
|
||||
const outlineRect = {
|
||||
top: Math.max(rect.top, clampingRect.top),
|
||||
@@ -112,6 +114,10 @@ export const Outline = ({
|
||||
Math.max(rect.left, clampingRect.left),
|
||||
};
|
||||
const dynamicStyle = useDynamicStyle(outlineRect);
|
||||
const style =
|
||||
color === undefined
|
||||
? dynamicStyle
|
||||
: { ...dynamicStyle, borderColor: color };
|
||||
if (outlineRect.width <= 0 || outlineRect.height <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -130,7 +136,7 @@ export const Outline = ({
|
||||
{propertyStyle}
|
||||
<div
|
||||
className={`${baseStyle()} ${baseOutlineStyle({ variant, isLeftClamped, isRightClamped, isBottomClamped, isTopClamped })}`}
|
||||
style={dynamicStyle}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
+3
-5
@@ -1,10 +1,8 @@
|
||||
import { useStore } from "@nanostores/react";
|
||||
import {
|
||||
$instances,
|
||||
$selectedInstanceOutlineAndInstance,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $selectedInstanceOutlineAndInstance } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
import { $textEditingInstanceSelector } from "~/shared/nano-states";
|
||||
import { $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
import { isDescendantOrSelf } from "~/shared/tree-utils";
|
||||
import { Outline } from "./outline";
|
||||
import { applyScale } from "../apply-scale";
|
||||
|
||||
@@ -9,11 +9,8 @@ import {
|
||||
import { useEffect, useRef } from "react";
|
||||
import { $canvasWidth } from "~/builder/shared/nano-states";
|
||||
import { minCanvasWidth } from "~/shared/breakpoints";
|
||||
import {
|
||||
$breakpoints,
|
||||
$isResizingCanvas,
|
||||
$selectedBreakpointId,
|
||||
} from "~/shared/nano-states";
|
||||
import { $isResizingCanvas, $selectedBreakpointId } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
|
||||
const handlesContainerStyle = css({
|
||||
position: "absolute",
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "~/builder/shared/nano-states";
|
||||
import { $textEditingInstanceSelector } from "~/shared/nano-states";
|
||||
import { CanvasTools } from "./canvas-tools";
|
||||
import { selectInstance } from "~/shared/awareness";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { ResizeHandles } from "./canvas-tools/resize-handles";
|
||||
import { MediaBadge } from "./canvas-tools/media-badge";
|
||||
import { useSetCanvasWidth } from "~/builder/shared/calc-canvas-width";
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
$selectedInstance,
|
||||
$selectedInstanceKey,
|
||||
$selectedPage,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { InstanceIcon, getInstanceLabel } from "./shared/instance-label";
|
||||
|
||||
const InstanceInfo = ({ instance }: { instance: Instance }) => {
|
||||
|
||||
@@ -51,22 +51,18 @@ import {
|
||||
} from "@webstudio-is/icons";
|
||||
import { hyphenateProperty } from "@webstudio-is/css-engine";
|
||||
import {
|
||||
$assets,
|
||||
$authPermit,
|
||||
$editingPageId,
|
||||
$instances,
|
||||
$pages,
|
||||
$permissions,
|
||||
$props,
|
||||
$styles,
|
||||
$styleSourceSelections,
|
||||
} from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { $styleSourceSelections } from "~/shared/sync/data-stores";
|
||||
import { $openProjectSettings } from "~/shared/nano-states/project-settings";
|
||||
import {
|
||||
$awareness,
|
||||
findAwarenessByInstanceId,
|
||||
selectPage,
|
||||
} from "~/shared/awareness";
|
||||
import { $styles } from "~/shared/sync/data-stores";
|
||||
import { $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
import { selectPage } from "~/shared/nano-states";
|
||||
import { findPageAndSelectorByInstanceId } from "~/shared/instance-utils";
|
||||
import { $selectedPageId } from "~/shared/nano-states";
|
||||
import { updateWebstudioData } from "~/shared/instance-utils";
|
||||
import { deleteAssets, replaceAsset } from "~/builder/shared/assets";
|
||||
import { validateFiles } from "~/builder/shared/assets/asset-upload";
|
||||
@@ -74,6 +70,7 @@ import {
|
||||
$activeInspectorPanel,
|
||||
setActiveSidebarPanel,
|
||||
} from "~/builder/shared/nano-states";
|
||||
import { $instances, $pages, $props } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
formatAssetName,
|
||||
parseAssetName,
|
||||
@@ -266,12 +263,14 @@ const AssetUsagesList = ({ usages }: { usages: AssetUsage[] }) => {
|
||||
if (!prop || !pages) {
|
||||
return;
|
||||
}
|
||||
const awareness = findAwarenessByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
prop.instanceId
|
||||
);
|
||||
$awareness.set(awareness);
|
||||
const { pageId, instanceSelector } =
|
||||
findPageAndSelectorByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
prop.instanceId
|
||||
);
|
||||
$selectedPageId.set(pageId);
|
||||
$selectedInstanceSelector.set(instanceSelector);
|
||||
setActiveSidebarPanel("auto");
|
||||
$activeInspectorPanel.set("settings");
|
||||
}}
|
||||
@@ -311,12 +310,14 @@ const AssetUsagesList = ({ usages }: { usages: AssetUsage[] }) => {
|
||||
if (!styleInstanceId || !pages) {
|
||||
return;
|
||||
}
|
||||
const awareness = findAwarenessByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
styleInstanceId
|
||||
);
|
||||
$awareness.set(awareness);
|
||||
const { pageId, instanceSelector } =
|
||||
findPageAndSelectorByInstanceId(
|
||||
pages,
|
||||
instances,
|
||||
styleInstanceId
|
||||
);
|
||||
$selectedPageId.set(pageId);
|
||||
$selectedInstanceSelector.set(instanceSelector);
|
||||
setActiveSidebarPanel("auto");
|
||||
$activeInspectorPanel.set("style");
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
VIDEO_EXTENSIONS,
|
||||
} from "@webstudio-is/sdk";
|
||||
import type { Asset, AllowedFileExtension } from "@webstudio-is/sdk";
|
||||
import { $assets } from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { useEffect } from "react";
|
||||
|
||||
// Create mock assets for every file type
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
Box,
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { Asset } from "@webstudio-is/sdk";
|
||||
import { $assets, $pages, $props, $styles } from "~/shared/nano-states";
|
||||
import { $assets, $pages, $props, $styles } from "~/shared/sync/data-stores";
|
||||
import { deleteAssets } from "~/builder/shared/assets";
|
||||
import { formatAssetName } from "~/builder/shared/assets/asset-utils";
|
||||
import { calculateUsagesByAssetId } from "./asset-info";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Asset } from "@webstudio-is/sdk";
|
||||
import { $assets } from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { onNextTransactionComplete } from "~/shared/sync/project-queue";
|
||||
import { invalidateAssets } from "~/shared/resources";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Asset } from "@webstudio-is/sdk";
|
||||
import { toast } from "@webstudio-is/design-system";
|
||||
import { $assets, $pages, $props, $styles } from "~/shared/nano-states";
|
||||
import { $assets, $pages, $props, $styles } from "~/shared/sync/data-stores";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { onNextTransactionComplete } from "~/shared/sync/project-queue";
|
||||
import { invalidateAssets } from "~/shared/resources";
|
||||
|
||||
@@ -9,12 +9,12 @@ import { restAssetsUploadPath, restAssetsPath } from "~/shared/router-utils";
|
||||
import { fetch } from "~/shared/fetch.client";
|
||||
import type { AssetActionResponse } from "~/builder/shared/assets";
|
||||
import {
|
||||
$assets,
|
||||
$authToken,
|
||||
$project,
|
||||
$uploadingFilesDataStore,
|
||||
type UploadingFileData,
|
||||
} from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { $project } from "~/shared/sync/data-stores";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { onNextTransactionComplete } from "~/shared/sync/project-queue";
|
||||
import { invalidateAssets } from "~/shared/resources";
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useMemo } from "react";
|
||||
import { computed } from "nanostores";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import type { Asset } from "@webstudio-is/sdk";
|
||||
import { $assets, $uploadingFilesDataStore } from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { $uploadingFilesDataStore } from "~/shared/nano-states";
|
||||
import type {
|
||||
AssetContainer,
|
||||
UploadedAssetContainer,
|
||||
|
||||
@@ -7,11 +7,8 @@ import {
|
||||
minCanvasWidth,
|
||||
} from "~/shared/breakpoints-utils";
|
||||
import { $workspaceRect, $canvasWidth } from "~/builder/shared/nano-states";
|
||||
import {
|
||||
$breakpoints,
|
||||
$isPreviewMode,
|
||||
$selectedBreakpoint,
|
||||
} from "~/shared/nano-states";
|
||||
import { $isPreviewMode, $selectedBreakpoint } from "~/shared/nano-states";
|
||||
import { $breakpoints } from "~/shared/sync/data-stores";
|
||||
|
||||
const defaultWidth = 320;
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
$isDesignMode,
|
||||
$isPreviewMode,
|
||||
toggleBuilderMode,
|
||||
$project,
|
||||
} from "~/shared/nano-states";
|
||||
import { $project } from "~/shared/sync/data-stores";
|
||||
|
||||
// Declare command for type safety
|
||||
declare module "~/shared/pubsub" {
|
||||
@@ -40,7 +40,8 @@ import {
|
||||
setActiveSidebarPanel,
|
||||
toggleActiveSidebarPanel,
|
||||
} from "./nano-states";
|
||||
import { $selectedInstancePath, selectInstance } from "~/shared/awareness";
|
||||
import { $selectedInstancePath } from "~/shared/nano-states";
|
||||
import { selectInstance } from "~/shared/nano-states";
|
||||
import { openCommandPanel } from "../features/command-panel";
|
||||
import { showWrapComponentsList } from "../features/command-panel/groups/wrap-group";
|
||||
import { showConvertComponentsList } from "../features/command-panel/groups/convert-group";
|
||||
|
||||
@@ -16,13 +16,13 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { DataSource, Instance } from "@webstudio-is/sdk";
|
||||
import { ROOT_INSTANCE_ID } from "@webstudio-is/sdk";
|
||||
import { $pages } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
$pages,
|
||||
$instances,
|
||||
$props,
|
||||
$dataSources,
|
||||
$resources,
|
||||
} from "~/shared/nano-states";
|
||||
} from "~/shared/sync/data-stores";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { findVariableUsagesByInstance } from "~/shared/data-variables";
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ import {
|
||||
$selectedInstancePath,
|
||||
$selectedPage,
|
||||
getInstanceKey,
|
||||
} from "~/shared/awareness";
|
||||
} from "~/shared/nano-states";
|
||||
import { canUnwrapInstance } from "~/shared/instance-utils";
|
||||
import { $propValuesByInstanceSelector } from "~/shared/nano-states";
|
||||
import { ROOT_INSTANCE_ID } from "@webstudio-is/sdk";
|
||||
import type { InstancePath } from "~/shared/awareness";
|
||||
import type { InstancePath } from "~/shared/nano-states";
|
||||
|
||||
const getMenuPermissions = (instancePath: InstancePath | undefined) => {
|
||||
const instanceId = instancePath?.[0]?.instance.id;
|
||||
|
||||
@@ -16,12 +16,11 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { Instance, StyleSource } from "@webstudio-is/sdk";
|
||||
import {
|
||||
$styleSources,
|
||||
$styleSourceSelections,
|
||||
$styles,
|
||||
$selectedStyleSources,
|
||||
$selectedStyleState,
|
||||
} from "~/shared/nano-states";
|
||||
import { $styleSources } from "~/shared/sync/data-stores";
|
||||
import { $styleSourceSelections, $styles } from "~/shared/sync/data-stores";
|
||||
import {
|
||||
deleteStyleSourceMutable,
|
||||
findUnusedTokens,
|
||||
@@ -32,10 +31,7 @@ import {
|
||||
type RenameStyleSourceError,
|
||||
} from "~/shared/style-source-utils";
|
||||
import { serverSyncStore } from "~/shared/sync/sync-stores";
|
||||
import { $selectedInstance } from "~/shared/awareness";
|
||||
|
||||
// Re-export the type for convenience
|
||||
export type { RenameStyleSourceError };
|
||||
import { $selectedInstance } from "~/shared/nano-states";
|
||||
|
||||
const $isDeleteUnusedTokensDialogOpen = atom(false);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
ShieldIcon,
|
||||
WebstudioIcon,
|
||||
} from "@webstudio-is/icons";
|
||||
import { $queueStatus } from "~/shared/sync/project-queue";
|
||||
import { $syncStatus } from "@webstudio-is/sync-client";
|
||||
import { $authPermit } from "~/shared/nano-states";
|
||||
import { SyncStatus } from "~/builder/features/sync-status";
|
||||
import { ViewMode } from "~/builder/features/view-mode";
|
||||
@@ -75,7 +75,7 @@ const SafeModeIndicator = () => (
|
||||
);
|
||||
|
||||
export const TopbarLayouts = () => {
|
||||
$queueStatus.set({ status: "failed" });
|
||||
$syncStatus.set({ status: "failed" });
|
||||
$authPermit.set("view");
|
||||
return (
|
||||
<StorySection title="Topbar Layouts">
|
||||
|
||||
@@ -21,7 +21,7 @@ import { AddressBarPopover } from "~/builder/features/address-bar";
|
||||
import { toggleActiveSidebarPanel } from "~/builder/shared/nano-states";
|
||||
import type { ReactNode } from "react";
|
||||
import { CloneButton } from "~/builder/features/clone";
|
||||
import { $selectedPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import { BuilderModeDropDown } from "~/builder/features/builder-mode";
|
||||
import { SafeModeButton } from "~/builder/features/safe-mode";
|
||||
import { NotificationPopover } from "~/shared/notifications/notification-popover";
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
tokenizePathnamePattern,
|
||||
validatePathnamePattern,
|
||||
} from "./url-pattern";
|
||||
import { VALID_URLPATTERN_PATHS } from "@webstudio-is/sdk/router-paths.test";
|
||||
import { VALID_URLPATTERN_PATHS } from "@webstudio-is/sdk/router-path-test-data";
|
||||
|
||||
/**
|
||||
* These tests use the shared test data from @webstudio-is/sdk to ensure
|
||||
|
||||
@@ -37,9 +37,6 @@ import {
|
||||
WebstudioComponentPreview,
|
||||
} from "./features/webstudio-component";
|
||||
import {
|
||||
$assets,
|
||||
$pages,
|
||||
$instances,
|
||||
registerComponentLibrary,
|
||||
$registeredComponents,
|
||||
subscribeComponentHooks,
|
||||
@@ -48,8 +45,9 @@ import {
|
||||
$isContentMode,
|
||||
subscribeModifierKeys,
|
||||
assetBaseUrl,
|
||||
$breakpoints,
|
||||
} from "~/shared/nano-states";
|
||||
import { $assets } from "~/shared/sync/data-stores";
|
||||
import { $pages, $instances, $breakpoints } from "~/shared/sync/data-stores";
|
||||
import { useDragAndDrop } from "./shared/use-drag-drop";
|
||||
import {
|
||||
initCopyPaste,
|
||||
@@ -69,13 +67,14 @@ import { subscribeFontLoadingDone } from "./shared/font-weight-support";
|
||||
import { subscribeSelected } from "./instance-selected";
|
||||
import { subscribeGridGuidesOnSelected } from "./grid-guide-utils";
|
||||
import { subscribeScrollNewInstanceIntoView } from "./shared/scroll-new-instance-into-view";
|
||||
import { $selectedPage } from "~/shared/awareness";
|
||||
import { $selectedPage } from "~/shared/nano-states";
|
||||
import { createInstanceElement } from "./elements";
|
||||
import { subscribeScrollbarSize } from "./scrollbar-width";
|
||||
import { compareMedia } from "@webstudio-is/css-engine";
|
||||
import { builderApi } from "~/shared/builder-api";
|
||||
import { useDebounceEffect } from "@webstudio-is/design-system";
|
||||
import { subscribeInstanceContextMenu } from "./instance-context-menu";
|
||||
import { startPointerTracking } from "~/shared/awareness";
|
||||
|
||||
registerContainers();
|
||||
|
||||
@@ -313,6 +312,8 @@ export const Canvas = () => {
|
||||
|
||||
useEffect(subscribeInflator, []);
|
||||
|
||||
useEffect(() => startPointerTracking(), []);
|
||||
|
||||
useHashLinkSync();
|
||||
|
||||
useEffect(subscribeInterceptedEvents, []);
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
type WebstudioComponentSystemProps,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import * as React from "react";
|
||||
import { $isDesignMode, $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
import { $isDesignMode } from "~/shared/nano-states";
|
||||
import { $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
|
||||
export const BlockTemplate = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
|
||||
@@ -8,12 +8,9 @@ import {
|
||||
} from "@webstudio-is/react-sdk";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
$instances,
|
||||
$isDesignMode,
|
||||
$isPreviewMode,
|
||||
$selectedInstanceSelector,
|
||||
} from "~/shared/nano-states";
|
||||
import { $isDesignMode, $isPreviewMode } from "~/shared/nano-states";
|
||||
import { $selectedInstanceSelector } from "~/shared/nano-states";
|
||||
import { $instances } from "~/shared/sync/data-stores";
|
||||
|
||||
export const Block = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
|
||||
Loaded 100 of 231 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user