From 3abb54a8750edaaa4b8ecb658e40bc95625857fb Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Fri, 13 Jan 2023 15:13:40 +0300 Subject: [PATCH] Migrate to nanostores (#779) Ref https://github.com/nanostores/nanostores Nanostores is another open source implementation of reactive states. Supports a lot of frameworks and provides useful utilities like computed stores. Api is slightly different ``` store.get(): Value store.set(value: Value) ``` `useStore` resolves only value so I temporary added custom `useValue` to not rewrite whole project. Since we gonna use computed atoms it makes more sense to invoke useStore where atom value is used instead of reusable hook. --- apps/designer/app/canvas/canvas.tsx | 6 +- .../wrapper-component/wrapper-component.tsx | 2 +- apps/designer/app/canvas/shared/instance.ts | 17 ++---- .../designer/app/canvas/shared/nano-states.ts | 12 ++-- .../shared/client-settings/settings.ts | 13 +++-- .../shared/inspector/collapsible-section.tsx | 9 +-- .../app/designer/shared/nano-states/index.ts | 46 +++++++-------- .../app/shared/nano-states/nano-states.ts | 49 ++++++++-------- apps/designer/app/shared/sync/sync-stores.ts | 4 +- apps/designer/package.json | 5 +- apps/designer/remix.config.js | 8 ++- packages/project/src/db/tree.ts | 2 +- packages/react-sdk/package.json | 4 +- .../src/user-props/all-user-props.ts | 9 +-- .../src/user-props/use-user-props-asset.ts | 2 +- .../src/user-props/use-user-props.ts | 2 +- pnpm-lock.yaml | 56 ++++++++++++------- 17 files changed, 136 insertions(+), 110 deletions(-) diff --git a/apps/designer/app/canvas/canvas.tsx b/apps/designer/app/canvas/canvas.tsx index 528d0fd89f..49fb8b9fdb 100644 --- a/apps/designer/app/canvas/canvas.tsx +++ b/apps/designer/app/canvas/canvas.tsx @@ -17,7 +17,6 @@ import { useShortcuts } from "./shared/use-shortcuts"; import { useDeleteInstance, useInsertInstance, - usePopulateRootInstance, usePublishSelectedInstanceData, usePublishTextEditingInstanceId, useReparentInstance, @@ -38,6 +37,7 @@ import { useBreakpoints, useRootInstance, useSetPresetStyles, + useSetRootInstance, useSetStyles, useSubscribeScrollState, } from "~/shared/nano-states"; @@ -105,7 +105,7 @@ const useAssets = (initialAssets: Array) => { const useCopyPaste = () => { const [instance] = useSelectedInstance(); - const [allUserProps] = useAllUserProps(); + const allUserProps = useAllUserProps(); const selectedInstanceData = useMemo( () => instance && { instance, props: allUserProps[instance.id]?.props }, @@ -172,7 +172,7 @@ export const Canvas = ({ data }: CanvasProps): JSX.Element | null => { useAllUserProps(data.props); useSetPresetStyles(data.tree.presetStyles); useSetStyles(data.tree.styles); - usePopulateRootInstance(data.tree); + useSetRootInstance(data.tree.root); setParams(data.params ?? null); useCanvasStore(publish); diff --git a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx index 1b6a9cab1d..e1113a3aa8 100644 --- a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -59,7 +59,7 @@ export const WrapperComponentDev = ({ useTextEditingInstanceId(); const [selectedInstance] = useSelectedInstance(); - const [allUserProps] = useAllUserProps(); + const allUserProps = useAllUserProps(); const instanceProps = allUserProps[instance.id]; const userProps = useMemo(() => { const result: UserProps = {}; diff --git a/apps/designer/app/canvas/shared/instance.ts b/apps/designer/app/canvas/shared/instance.ts index 7adeddc8f1..2ce2f4d023 100644 --- a/apps/designer/app/canvas/shared/instance.ts +++ b/apps/designer/app/canvas/shared/instance.ts @@ -1,8 +1,7 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { type Instance, type UserProp, - type Tree, getComponentMeta, allUserPropsContainer, } from "@webstudio-is/react-sdk"; @@ -38,12 +37,6 @@ declare module "~/shared/pubsub" { } } -export const usePopulateRootInstance = (tree: Tree) => { - useState(() => { - rootInstanceContainer.value = tree.root; - }); -}; - export const findInsertLocation = ( rootInstance: Instance, selectedInstanceId: Instance["id"] | undefined @@ -119,13 +112,11 @@ export const useReparentInstance = () => { ); }); + const rootInstance = rootInstanceContainer.get(); // Make the drag item the selected instance - if ( - selectedInstance?.id !== instanceId && - rootInstanceContainer.value !== undefined - ) { + if (selectedInstance?.id !== instanceId && rootInstance !== undefined) { setSelectedInstance( - utils.tree.findInstanceById(rootInstanceContainer.value, instanceId) + utils.tree.findInstanceById(rootInstance, instanceId) ); } }); diff --git a/apps/designer/app/canvas/shared/nano-states.ts b/apps/designer/app/canvas/shared/nano-states.ts index 62ed9e1539..b080cfaa3d 100644 --- a/apps/designer/app/canvas/shared/nano-states.ts +++ b/apps/designer/app/canvas/shared/nano-states.ts @@ -1,7 +1,11 @@ -import { createValueContainer, useValue } from "react-nano-state"; +import { atom, type WritableAtom } from "nanostores"; +import { useStore } from "@nanostores/react"; import { type Instance } from "@webstudio-is/react-sdk"; -export const selectedInstanceContainer = createValueContainer< - Instance | undefined ->(); +const useValue = (atom: WritableAtom) => { + const value = useStore(atom); + return [value, atom.set] as const; +}; + +export const selectedInstanceContainer = atom(); export const useSelectedInstance = () => useValue(selectedInstanceContainer); diff --git a/apps/designer/app/designer/shared/client-settings/settings.ts b/apps/designer/app/designer/shared/client-settings/settings.ts index 4ede2d5dca..41e6a28062 100644 --- a/apps/designer/app/designer/shared/client-settings/settings.ts +++ b/apps/designer/app/designer/shared/client-settings/settings.ts @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; -import { createValueContainer, useValue } from "react-nano-state"; +import { atom } from "nanostores"; +import { useStore } from "@nanostores/react"; import { sentryMessage } from "~/shared/sentry"; import * as config from "./config"; @@ -72,24 +73,24 @@ export const setSetting = (name: Name, value: Value) => { } }; -const settingsContainer = createValueContainer(defaultSettings); +const settingsContainer = atom(defaultSettings); export const useClientSettings = (): [Settings, typeof setSetting, boolean] => { - const [settings, setSettings] = useValue(settingsContainer); + const settings = useStore(settingsContainer); // We need to know if the settings were loaded from local storage. // E.g. to decide if we should wait till the actual setting is loaded or use the default. const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { - setSettings(read()); + settingsContainer.set(read()); setIsLoaded(true); - }, [setSettings]); + }, []); const setSettingValue = (name: Name, value: Value) => { if (settings[name] === value) { return; } - setSettings({ ...settings, [name]: value }); + settingsContainer.set({ ...settings, [name]: value }); setSetting(name, value); }; return [settings, setSettingValue, isLoaded]; diff --git a/apps/designer/app/designer/shared/inspector/collapsible-section.tsx b/apps/designer/app/designer/shared/inspector/collapsible-section.tsx index eedb7cd8a6..813cce2f62 100644 --- a/apps/designer/app/designer/shared/inspector/collapsible-section.tsx +++ b/apps/designer/app/designer/shared/inspector/collapsible-section.tsx @@ -1,4 +1,5 @@ -import { createValueContainer, useValue } from "react-nano-state"; +import { atom } from "nanostores"; +import { useStore } from "@nanostores/react"; import { Box, Flex, Text, Collapsible } from "@webstudio-is/design-system"; import { ChevronLeftIcon, ChevronRightIcon } from "@webstudio-is/icons"; @@ -10,17 +11,17 @@ type CollapsibleSectionProps = { rightSlot?: JSX.Element; }; -const stateContainer = createValueContainer<{ [label: string]: boolean }>({}); +const stateContainer = atom<{ [label: string]: boolean }>({}); // Preserves the open/close state even when component gets unmounted const useOpenState = ( label: string, initialValue: boolean ): [boolean, (value: boolean) => void] => { - const [state, setState] = useValue(stateContainer); + const state = useStore(stateContainer); const isOpen = label in state ? state[label] : initialValue; const setIsOpen = (isOpen: boolean) => { - setState({ ...state, [label]: isOpen }); + stateContainer.set({ ...state, [label]: isOpen }); }; return [isOpen, setIsOpen]; }; diff --git a/apps/designer/app/designer/shared/nano-states/index.ts b/apps/designer/app/designer/shared/nano-states/index.ts index 81f0335f7e..b604f3d7a7 100644 --- a/apps/designer/app/designer/shared/nano-states/index.ts +++ b/apps/designer/app/designer/shared/nano-states/index.ts @@ -1,4 +1,5 @@ -import { createValueContainer, useValue } from "react-nano-state"; +import { atom, type WritableAtom } from "nanostores"; +import { useStore } from "@nanostores/react"; import { type Breakpoint } from "@webstudio-is/css-data"; import { type SelectedInstanceData, @@ -8,55 +9,54 @@ import { type SyncStatus } from "~/shared/sync"; import { type Pages, type Project } from "@webstudio-is/project"; import type { AssetContainer, DeletingAssetContainer } from "../assets"; -const selectedInstanceDataContainer = createValueContainer< - SelectedInstanceData | undefined ->(); +const useValue = (atom: WritableAtom) => { + const value = useStore(atom); + return [value, atom.set] as const; +}; + +const selectedInstanceDataContainer = atom(); export const useSelectedInstanceData = () => useValue(selectedInstanceDataContainer); -const hoveredInstanceDataContainer = createValueContainer< - HoveredInstanceData | undefined ->(); +const hoveredInstanceDataContainer = atom(); export const useHoveredInstanceData = () => useValue(hoveredInstanceDataContainer); -const isShareDialogOpenContainer = createValueContainer(false); +const isShareDialogOpenContainer = atom(false); export const useIsShareDialogOpen = () => useValue(isShareDialogOpenContainer); -const isPublishDialogOpenContainer = createValueContainer(false); +const isPublishDialogOpenContainer = atom(false); export const useIsPublishDialogOpen = () => useValue(isPublishDialogOpenContainer); -const selectedBreakpointContainer = createValueContainer< - Breakpoint | undefined ->(); +const selectedBreakpointContainer = atom(); export const useSelectedBreakpoint = () => useValue(selectedBreakpointContainer); -const zoomContainer = createValueContainer(100); +const zoomContainer = atom(100); export const useZoom = () => useValue(zoomContainer); -const canvasWidthContainer = createValueContainer(0); +const canvasWidthContainer = atom(0); export const useCanvasWidth = () => useValue(canvasWidthContainer); -const canvasRectContainer = createValueContainer(); +const canvasRectContainer = atom(); export const useCanvasRect = () => useValue(canvasRectContainer); -const syncStatusContainer = createValueContainer("idle"); +const syncStatusContainer = atom("idle"); export const useSyncStatus = () => useValue(syncStatusContainer); -const assetsContainer = createValueContainer< - Array ->([]); +const assetsContainer = atom>( + [] +); export const useAssetsContainer = () => useValue(assetsContainer); -const pagesContainer = createValueContainer(); +const pagesContainer = atom(); export const usePages = () => useValue(pagesContainer); -const currentPageIdContainer = createValueContainer(); +const currentPageIdContainer = atom(); export const useCurrentPageId = () => useValue(currentPageIdContainer); -const projectContainer = createValueContainer(); +const projectContainer = atom(); export const useProject = () => useValue(projectContainer); export type TextToolbarState = { @@ -68,5 +68,5 @@ export type TextToolbarState = { isLink: boolean; isSpan: boolean; }; -const textToolbarState = createValueContainer(); +const textToolbarState = atom(); export const useTextToolbarState = () => useValue(textToolbarState); diff --git a/apps/designer/app/shared/nano-states/nano-states.ts b/apps/designer/app/shared/nano-states/nano-states.ts index c656adfca1..d6011e0654 100644 --- a/apps/designer/app/shared/nano-states/nano-states.ts +++ b/apps/designer/app/shared/nano-states/nano-states.ts @@ -1,4 +1,5 @@ -import { createValueContainer, useValue } from "react-nano-state"; +import { atom, type WritableAtom } from "nanostores"; +import { useStore } from "@nanostores/react"; import type { Instance, PresetStyles, Styles } from "@webstudio-is/react-sdk"; import type { DropTargetChangePayload, @@ -8,34 +9,42 @@ import type { Breakpoint } from "@webstudio-is/css-data"; import type { DesignToken } from "@webstudio-is/design-tokens"; import { useSyncInitializeOnce } from "../hook-utils"; -export const rootInstanceContainer = createValueContainer< - Instance | undefined ->(); -export const useRootInstance = () => useValue(rootInstanceContainer); +const useValue = (atom: WritableAtom) => { + const value = useStore(atom); + return [value, atom.set] as const; +}; -export const presetStylesContainer = createValueContainer([]); +export const rootInstanceContainer = atom(); +export const useRootInstance = () => useValue(rootInstanceContainer); +export const useSetRootInstance = (root: Instance) => { + useSyncInitializeOnce(() => { + rootInstanceContainer.set(root); + }); +}; + +export const presetStylesContainer = atom([]); export const usePresetStyles = () => useValue(presetStylesContainer); export const useSetPresetStyles = (presetStyles: PresetStyles) => { useSyncInitializeOnce(() => { - presetStylesContainer.value = presetStyles; + presetStylesContainer.set(presetStyles); }); }; -export const stylesContainer = createValueContainer([]); +export const stylesContainer = atom([]); export const useStyles = () => useValue(stylesContainer); export const useSetStyles = (styles: Styles) => { useSyncInitializeOnce(() => { - stylesContainer.value = styles; + stylesContainer.set(styles); }); }; -export const breakpointsContainer = createValueContainer>([]); +export const breakpointsContainer = atom>([]); export const useBreakpoints = () => useValue(breakpointsContainer); -const isPreviewModeContainer = createValueContainer(false); +const isPreviewModeContainer = atom(false); export const useIsPreviewMode = () => useValue(isPreviewModeContainer); -const selectedInstanceOutlineContainer = createValueContainer<{ +const selectedInstanceOutlineContainer = atom<{ visible: boolean; rect?: DOMRect; }>({ @@ -45,19 +54,15 @@ const selectedInstanceOutlineContainer = createValueContainer<{ export const useSelectedInstanceOutline = () => useValue(selectedInstanceOutlineContainer); -const hoveredInstanceRectContainer = createValueContainer< - DOMRect | undefined ->(); +const hoveredInstanceRectContainer = atom(); export const useHoveredInstanceRect = () => useValue(hoveredInstanceRectContainer); -const isScrollingContainer = createValueContainer(false); +const isScrollingContainer = atom(false); export const useIsScrolling = () => useValue(isScrollingContainer); // We are editing the text of that instance in text editor. -const textEditingInstanceIdContainer = createValueContainer< - Instance["id"] | undefined ->(); +const textEditingInstanceIdContainer = atom(); export const useTextEditingInstanceId = () => useValue(textEditingInstanceIdContainer); @@ -67,12 +72,10 @@ export type DragAndDropState = { dropTarget?: DropTargetChangePayload; dragItem?: DragStartPayload["dragItem"]; }; -const dragAndDropStateContainer = createValueContainer({ +const dragAndDropStateContainer = atom({ isDragging: false, }); export const useDragAndDropState = () => useValue(dragAndDropStateContainer); -export const designTokensContainer = createValueContainer>( - [] -); +export const designTokensContainer = atom>([]); export const useDesignTokens = () => useValue(designTokensContainer); diff --git a/apps/designer/app/shared/sync/sync-stores.ts b/apps/designer/app/shared/sync/sync-stores.ts index e1069f8475..333aa45b58 100644 --- a/apps/designer/app/shared/sync/sync-stores.ts +++ b/apps/designer/app/shared/sync/sync-stores.ts @@ -76,7 +76,7 @@ export const useCanvasStore = (publish: Publish) => { for (const [namespace, container] of store.containers) { data.push({ namespace, - value: container.value, + value: container.get(), }); } publish({ @@ -96,7 +96,7 @@ export const useDesignerStore = (publish: Publish) => { for (const { namespace, value } of data) { const container = store.containers.get(namespace); if (container) { - container.dispatch(value); + container.set(value); } } }); diff --git a/apps/designer/package.json b/apps/designer/package.json index 046f9fb617..1e1542458d 100644 --- a/apps/designer/package.json +++ b/apps/designer/package.json @@ -23,6 +23,7 @@ "@lexical/react": "^0.6.0", "@lexical/selection": "^0.6.0", "@lexical/utils": "^0.6.0", + "@nanostores/react": "^0.4.1", "@radix-ui/react-select": "^1.0.0", "@radix-ui/react-tooltip": "^1.0.0", "@remix-run/node": "1.9.0", @@ -55,7 +56,7 @@ "fast-deep-equal": "3.1.3", "hyphenate-style-name": "^1.0.4", "immer": "^9.0.12", - "immerhin": "^0.3.1", + "immerhin": "^0.4.0", "lexical": "^0.6.0", "lodash": "4.17.21", "lodash.capitalize": "^4.2.1", @@ -67,13 +68,13 @@ "match-sorter": "^6.3.1", "mitt": "^3.0.0", "nanoid": "^3.2.0", + "nanostores": "^0.7.1", "pretty-bytes": "^6.0.0", "react": "^17.0.2", "react-color": "^2.19.3", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.4", "react-hotkeys-hook": "^3.4.7", - "react-nano-state": "^0.4.0", "react-router-dom": "^6.5.0", "react-use": "^17.3.2", "remix-auth": "^3.2.2", diff --git a/apps/designer/remix.config.js b/apps/designer/remix.config.js index 7ea30786e2..9967365577 100644 --- a/apps/designer/remix.config.js +++ b/apps/designer/remix.config.js @@ -12,7 +12,13 @@ module.exports = { // This makes sure remix bundles monorepo packages which are always // sym-linked and can't be hoisted. You could also manually name packages // e.g. ["@webstudio-is/ui", ...] - serverDependenciesToBundle: [/@webstudio-is\/.*/, "pretty-bytes", "djb2a"], + serverDependenciesToBundle: [ + /@webstudio-is\/.*/, + "pretty-bytes", + "djb2a", + "nanostores", + "@nanostores/react", + ], // appDirectory: "app", // assetsBuildDirectory: "public/build", serverBuildPath: "api/index.js", diff --git a/packages/project/src/db/tree.ts b/packages/project/src/db/tree.ts index b40946b846..f07b78d182 100644 --- a/packages/project/src/db/tree.ts +++ b/packages/project/src/db/tree.ts @@ -89,7 +89,7 @@ const InstanceDbOut = z.lazy(() => id: z.string(), component: z.string(), children: z.array(z.union([InstanceDbOut, Text])), - cssRules: z.array(CssRuleDbOut), + cssRules: z.optional(z.array(CssRuleDbOut)), }) ) as z.ZodType; diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 776244d2a7..57a1434467 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -53,6 +53,7 @@ "zod": "^3.19.1" }, "dependencies": { + "@nanostores/react": "^0.4.1", "@webstudio-is/asset-uploader": "workspace:^", "@webstudio-is/css-data": "workspace:^", "@webstudio-is/design-tokens": "workspace:^", @@ -60,9 +61,8 @@ "@webstudio-is/image": "workspace:^", "@webstudio-is/prisma-client": "workspace:^", "detect-font": "^0.1.5", - "immer": "^9.0.12", "mitt": "^3.0.0", - "react-nano-state": "^0.4.0", + "nanostores": "^0.7.1", "warn-once": "^0.1.1" }, "exports": { diff --git a/packages/react-sdk/src/user-props/all-user-props.ts b/packages/react-sdk/src/user-props/all-user-props.ts index 9568583062..ee33d40d80 100644 --- a/packages/react-sdk/src/user-props/all-user-props.ts +++ b/packages/react-sdk/src/user-props/all-user-props.ts @@ -1,10 +1,11 @@ import { useRef } from "react"; -import { createValueContainer, useValue } from "react-nano-state"; +import { atom } from "nanostores"; +import { useStore } from "@nanostores/react"; import type { InstanceProps, Instance } from "../db"; export type AllUserProps = { [id: Instance["id"]]: InstanceProps }; -export const allUserPropsContainer = createValueContainer({}); +export const allUserPropsContainer = atom({}); export const useAllUserProps = (initialUserProps?: Array) => { // @todo ssr workaround for https://github.com/webstudio-is/webstudio-designer/issues/213 @@ -15,8 +16,8 @@ export const useAllUserProps = (initialUserProps?: Array) => { propsMap[props.instanceId] = props; } //We don't need to trigger rerender when setting the initial value - allUserPropsContainer.value = propsMap; + allUserPropsContainer.set(propsMap); ref.current = true; } - return useValue(allUserPropsContainer); + return useStore(allUserPropsContainer); }; diff --git a/packages/react-sdk/src/user-props/use-user-props-asset.ts b/packages/react-sdk/src/user-props/use-user-props-asset.ts index cb70f55f40..205b8f430c 100644 --- a/packages/react-sdk/src/user-props/use-user-props-asset.ts +++ b/packages/react-sdk/src/user-props/use-user-props-asset.ts @@ -11,7 +11,7 @@ export const useUserPropsAsset = ( instanceId: Instance["id"], propName: UserProp["prop"] ): Asset | undefined => { - const [allUserProps] = useAllUserProps(); + const allUserProps = useAllUserProps(); const propsData = allUserProps[instanceId]; const asset = useMemo(() => { diff --git a/packages/react-sdk/src/user-props/use-user-props.ts b/packages/react-sdk/src/user-props/use-user-props.ts index 8bcbd7689d..60b5e262d3 100644 --- a/packages/react-sdk/src/user-props/use-user-props.ts +++ b/packages/react-sdk/src/user-props/use-user-props.ts @@ -10,7 +10,7 @@ type UserProps = { [prop: UserProp["prop"]]: string | number | boolean }; * up to date with props panel. */ export const useUserProps = (instanceId: Instance["id"]) => { - const [allUserProps] = useAllUserProps(); + const allUserProps = useAllUserProps(); const propsData = allUserProps[instanceId]; const props = useMemo(() => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e5babc3b5..853a109024 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,7 @@ importers: '@lexical/react': ^0.6.0 '@lexical/selection': ^0.6.0 '@lexical/utils': ^0.6.0 + '@nanostores/react': ^0.4.1 '@radix-ui/react-select': ^1.0.0 '@radix-ui/react-tooltip': ^1.0.0 '@remix-run/dev': 1.9.0 @@ -109,7 +110,7 @@ importers: fast-deep-equal: 3.1.3 hyphenate-style-name: ^1.0.4 immer: ^9.0.12 - immerhin: ^0.3.1 + immerhin: ^0.4.0 jest: ^29.3.1 lexical: ^0.6.0 lodash: 4.17.21 @@ -122,6 +123,7 @@ importers: match-sorter: ^6.3.1 mitt: ^3.0.0 nanoid: ^3.2.0 + nanostores: ^0.7.1 prettier: ^2.6.2 pretty-bytes: ^6.0.0 react: ^17.0.2 @@ -129,7 +131,6 @@ importers: react-dom: 17.0.2 react-error-boundary: ^3.1.4 react-hotkeys-hook: ^3.4.7 - react-nano-state: ^0.4.0 react-router-dom: ^6.5.0 react-test-renderer: ^17.0.2 react-use: ^17.3.2 @@ -152,6 +153,7 @@ importers: '@lexical/react': 0.6.0_p7mvqmjvwf4nexue7sv3sfptum '@lexical/selection': 0.6.0_lexical@0.6.0 '@lexical/utils': 0.6.0_lexical@0.6.0 + '@nanostores/react': 0.4.1_hn2jst675metodsznud55jugtu '@radix-ui/react-select': 1.0.0_nn45z5sr7igu7sfun6tiae5hx4 '@radix-ui/react-tooltip': 1.0.0_nn45z5sr7igu7sfun6tiae5hx4 '@remix-run/node': 1.9.0_sfoxds7t5ydpegc3knd667wn6m @@ -184,7 +186,7 @@ importers: fast-deep-equal: 3.1.3 hyphenate-style-name: 1.0.4 immer: 9.0.15 - immerhin: 0.3.1 + immerhin: 0.4.0 lexical: 0.6.0 lodash: 4.17.21 lodash.capitalize: 4.2.1 @@ -196,13 +198,13 @@ importers: match-sorter: 6.3.1 mitt: 3.0.0 nanoid: 3.3.4 + nanostores: 0.7.1 pretty-bytes: 6.0.0 react: 17.0.2 react-color: 2.19.3_react@17.0.2 react-dom: 17.0.2_react@17.0.2 react-error-boundary: 3.1.4_react@17.0.2 react-hotkeys-hook: 3.4.7_sfoxds7t5ydpegc3knd667wn6m - react-nano-state: 0.4.0_react@17.0.2 react-router-dom: 6.5.0_sfoxds7t5ydpegc3knd667wn6m react-use: 17.4.0_sfoxds7t5ydpegc3knd667wn6m remix-auth: 3.2.2_@remix-run+react@1.9.0 @@ -768,6 +770,7 @@ importers: '@babel/core': ^7.20.2 '@esbuild-kit/esm-loader': ^2.4.2 '@jest/globals': ^29.3.1 + '@nanostores/react': ^0.4.1 '@remix-run/node': 1.9.0 '@remix-run/react': 1.9.0 '@remix-run/server-runtime': 1.9.0 @@ -791,18 +794,18 @@ importers: detect-font: ^0.1.5 esbuild: 0.15.13 esbuild-node-externals: ^1.4.1 - immer: ^9.0.12 jest: ^29.3.1 mitt: ^3.0.0 + nanostores: ^0.7.1 react: ^17.0.2 react-dom: 17.0.2 - react-nano-state: ^0.4.0 remix-utils: ^4.1.0 tsx: ^3.9.0 typescript: 4.7.4 warn-once: ^0.1.1 zod: ^3.19.1 dependencies: + '@nanostores/react': 0.4.1_hn2jst675metodsznud55jugtu '@webstudio-is/asset-uploader': link:../asset-uploader '@webstudio-is/css-data': link:../css-data '@webstudio-is/design-tokens': link:../design-tokens @@ -810,9 +813,8 @@ importers: '@webstudio-is/image': link:../image '@webstudio-is/prisma-client': link:../prisma-client detect-font: 0.1.5 - immer: 9.0.15 mitt: 3.0.0 - react-nano-state: 0.4.0_react@17.0.2 + nanostores: 0.7.1 warn-once: 0.1.1 devDependencies: '@babel/core': 7.20.2 @@ -4882,6 +4884,18 @@ packages: glob-to-regexp: 0.3.0 dev: true + /@nanostores/react/0.4.1_hn2jst675metodsznud55jugtu: + resolution: {integrity: sha512-lsv0CYrMxczbXtoV/mxFVEoL/uVjEjseoP89srO/5yNAOkJka+dSFS7LYyWEbuvCPO7EgbtkvRpO5V+OztKQOw==} + engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} + peerDependencies: + nanostores: ^0.7.0 + react: '>=18.0.0' + dependencies: + nanostores: 0.7.1 + react: 17.0.2 + use-sync-external-store: 1.2.0_react@17.0.2 + dev: false + /@nodelib/fs.scandir/2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -14942,8 +14956,8 @@ packages: resolution: {integrity: sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==} dev: false - /immerhin/0.3.1: - resolution: {integrity: sha512-ZWO7lR+WPHJRUxNpNw76K6+YQKnAEnWFZcCFKqWfYHg0lyVxs+D7k72NSe92GGwyr9gH7Yj6HbMaKXfgArYpyw==} + /immerhin/0.4.0: + resolution: {integrity: sha512-o/nh3n80wd/Iku33thGAovobcnCfYHuoxDFY1dDijIX4xppd4Hb3KdfwJDdq99kF8iqCHJjUhEx/iYTiqz1pqw==} dependencies: bson-objectid: 2.0.4 immer: 9.0.15 @@ -17487,6 +17501,11 @@ packages: transitivePeerDependencies: - supports-color + /nanostores/0.7.1: + resolution: {integrity: sha512-7HenoVpZOjRudHCmAVWKloBTk3+Ozw49KuRWq/pNlEIIVS3x9dID+I5z3Rhjxl1muhHwv4dFhpT+0FebT9jvaA==} + engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} + dev: false + /napi-build-utils/1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} dev: false @@ -18899,15 +18918,6 @@ packages: resolution: {integrity: sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==} dev: false - /react-nano-state/0.4.0_react@17.0.2: - resolution: {integrity: sha512-SqSNd9Y5scLhaMpD0tqMcCl3nijFlPFJzOmOFc4ITuhjjbsyKHcdjv02tamI88fCt8bAcjWA/It0k/+QP61w9w==} - engines: {node: '>=13.2.0'} - peerDependencies: - react: '>=16.8' - dependencies: - react: 17.0.2 - dev: false - /react-refresh/0.11.0: resolution: {integrity: sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==} engines: {node: '>=0.10.0'} @@ -21563,6 +21573,14 @@ packages: tslib: 2.4.0 dev: false + /use-sync-external-store/1.2.0_react@17.0.2: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 17.0.2 + dev: false + /use/3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'}