diff --git a/apps/designer/app/designer/features/workspace/workspace.tsx b/apps/designer/app/designer/features/workspace/workspace.tsx index 41b0d6a7e3..53c2581306 100644 --- a/apps/designer/app/designer/features/workspace/workspace.tsx +++ b/apps/designer/app/designer/features/workspace/workspace.tsx @@ -1,4 +1,4 @@ -import { Box, Flex } from "@webstudio-is/design-system"; +import { Box, Flex, Toaster } from "@webstudio-is/design-system"; import { useCanvasWidth, useZoom } from "~/designer/shared/nano-states"; import { CanvasTools } from "./canvas-tools"; import { type Publish } from "@webstudio-is/react-sdk"; @@ -9,6 +9,7 @@ const workspaceStyle = { // scroll behaviour should be derived from the iframe overflow: "hidden", scrollbarGutter: "stable", + position: "relative", }; const zoomStyle = { @@ -55,6 +56,7 @@ export const Workspace = ({ + ); }; diff --git a/apps/designer/package.json b/apps/designer/package.json index 9aca9df4b8..5c0baba219 100644 --- a/apps/designer/package.json +++ b/apps/designer/package.json @@ -26,10 +26,10 @@ "@stitches/react": "^1.2.7", "@vercel/node": "^2.4.1", "@webstudio-is/asset-uploader": "*", - "@webstudio-is/prisma-client": "*", - "@webstudio-is/react-sdk": "*", "@webstudio-is/design-system": "*", "@webstudio-is/icons": "*", + "@webstudio-is/prisma-client": "*", + "@webstudio-is/react-sdk": "*", "bson-objectid": "^2.0.2", "camelcase": "^6.3.0", "debug": "^4.3.4", @@ -93,6 +93,8 @@ "@types/react-dom": "^17.0.9", "@types/sharp": "^0.30.4", "@types/w3c-css-typed-object-model-level-1": "^20180410.0.4", + "@webstudio-is/eslint-config-custom": "*", + "@webstudio-is/tsconfig": "*", "babel-loader": "^8.2.5", "css-tree": "^2.0.4", "dotenv": "^16.0.0", @@ -103,8 +105,6 @@ "prettier": "^2.6.2", "react-test-renderer": "^17.0.2", "relative-deps": "^1.0.7", - "@webstudio-is/eslint-config-custom": "*", - "@webstudio-is/tsconfig": "*", "typescript": "4.6.4" }, "sideEffects": false, diff --git a/packages/design-system/package.json b/packages/design-system/package.json index e69178f2c6..78fe722bc5 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -15,8 +15,6 @@ "storybook:build": "build-storybook" }, "devDependencies": { - "tsup": "^6.1.3", - "typescript": "^4.6.2", "@storybook/addon-actions": "^6.5.6", "@storybook/addon-essentials": "^6.5.6", "@storybook/addon-interactions": "^6.5.6", @@ -25,7 +23,9 @@ "@storybook/manager-webpack4": "^6.5.6", "@storybook/react": "^6.5.6", "@storybook/testing-library": "^0.0.11", - "@webstudio-is/jest-config": "*" + "@webstudio-is/jest-config": "*", + "tsup": "^6.1.3", + "typescript": "^4.6.2" }, "peerDependencies": { "react": "^17.0.2", @@ -55,12 +55,14 @@ "@radix-ui/react-slider": "^0.1.4", "@radix-ui/react-switch": "^0.1.5", "@radix-ui/react-tabs": "^0.1.5", + "@radix-ui/react-toast": "^1.0.0", "@radix-ui/react-toggle": "^0.1.4", "@radix-ui/react-toggle-group": "^0.1.5", "@radix-ui/react-tooltip": "^1.0.0", "@radix-ui/react-use-layout-effect": "^0.1.0", "@webstudio-is/icons": "*", - "@webstudio-is/react-sdk": "*" + "@webstudio-is/react-sdk": "*", + "react-hot-toast": "^2.3.0" }, "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/design-system/src/components/toast.tsx b/packages/design-system/src/components/toast.tsx new file mode 100644 index 0000000000..f4b28ccaf0 --- /dev/null +++ b/packages/design-system/src/components/toast.tsx @@ -0,0 +1,115 @@ +import * as ToastPrimitive from "@radix-ui/react-toast"; +import toast, { useToaster } from "react-hot-toast/headless"; +import { keyframes, styled } from "../stitches.config"; +import { CheckIcon, Cross1Icon } from "@webstudio-is/icons"; +import { Box } from "./box"; + +const VIEWPORT_PADDING = "$2"; + +const hide = keyframes({ + "0%": { opacity: 1 }, + "100%": { opacity: 0 }, +}); + +const slideIn = keyframes({ + from: { transform: `translateY(calc(0px - ${VIEWPORT_PADDING}px))` }, + to: { transform: "translateY(0)" }, +}); + +const swipeOut = keyframes({ + from: { transform: "translateX(var(--radix-toast-swipe-end-x))" }, + to: { transform: `translateX(calc(100% + ${VIEWPORT_PADDING}px))` }, +}); + +const StyledViewport = styled(ToastPrimitive.Viewport, { + position: "absolute", + top: 0, + right: 0, + display: "flex", + flexDirection: "column", + padding: VIEWPORT_PADDING, + gap: VIEWPORT_PADDING, + minWidth: 200, + width: "auto", + maxWidth: "100vw", + margin: 0, + listStyle: "none", + zIndex: "$max", + outline: "none", +}); + +const StyledToast = styled(ToastPrimitive.Root, { + borderRadius: 6, + boxShadow: + "hsl(206 22% 7% / 35%) 0 $space$2 $space$6 -$space$2, hsl(206 22% 7% / 20%) 0 $space$2 $space$4 -$space$3", + padding: "$3", + display: "flex", + gap: "$1", + alignItems: "center", + color: "$highContrast", + fontWeight: 500, + fontSize: "$3", + background: "$loContrast", + + "@media (prefers-reduced-motion: no-preference)": { + '&[data-state="open"]': { + animation: `${slideIn} 250ms cubic-bezier(0.16, 1, 0.3, 1)`, + }, + '&[data-state="closed"]': { + animation: `${hide} 105ms ease-in`, + }, + '&[data-swipe="move"]': { + transform: "translateX(var(--radix-toast-swipe-move-x))", + }, + '&[data-swipe="cancel"]': { + transform: "translateX(0)", + transition: "transform 200ms ease-out", + }, + '&[data-swipe="end"]': { + animation: `${swipeOut} 150ms ease-out`, + }, + }, + variants: { + variant: { + error: { + color: "$red11", + }, + success: { + color: "$green9", + }, + blank: {}, + custom: {}, + loading: {}, + }, + }, +}); + +const StyledTitle = styled(ToastPrimitive.Title, { + marginBottom: "$1", +}); + +export const Toaster = () => { + const { toasts, handlers } = useToaster(); + const { startPause, endPause } = handlers; + return ( + + {toasts.map((toast) => ( + + + {toast.type === "success" && } + {toast.type === "error" && } + + {toast.message} + + ))} + + + ); +}; + +export { toast }; diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts index 8f8b6ad032..26c115a71f 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -11,6 +11,7 @@ export { theme, type CSS, } from "./stitches.config"; +export * from "./components/toast"; export * as Collapsible from "@radix-ui/react-collapsible"; export { AccessibleIcon } from "@radix-ui/react-accessible-icon"; diff --git a/yarn.lock b/yarn.lock index 38b02e8583..22cf874352 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2864,6 +2864,17 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-slot" "0.1.2" +"@radix-ui/react-collection@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.0.tgz#0ec4c72fabd35a03b5787075ac799e3b17ca5710" + integrity sha512-8i1pf5dKjnq90Z8udnnXKzdCEV3/FYrfw0n/b6NvB6piXEn3fO1bOh7HBcpG8XrnIXzxlYu2oCcR38QpyLS/mg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-primitive" "1.0.0" + "@radix-ui/react-slot" "1.0.0" + "@radix-ui/react-compose-refs@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz#cff6e780a0f73778b976acff2c2a5b6551caab95" @@ -3276,6 +3287,25 @@ "@radix-ui/react-roving-focus" "0.1.5" "@radix-ui/react-use-controllable-state" "0.1.0" +"@radix-ui/react-toast@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.0.0.tgz#0d808e7f13376ddc74c0dbb7a4ff1c0602778ae2" + integrity sha512-mdoF6rahgushdev0OX+9a7JKoH0xZAZBo2Ktf/s779S7EnkZeL3/MFiRIV5LpRP5CtASmfdSD3FLnEvG1RHRtQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-collection" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-dismissable-layer" "1.0.0" + "@radix-ui/react-portal" "1.0.0" + "@radix-ui/react-presence" "1.0.0" + "@radix-ui/react-primitive" "1.0.0" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-controllable-state" "1.0.0" + "@radix-ui/react-use-layout-effect" "1.0.0" + "@radix-ui/react-visually-hidden" "1.0.0" + "@radix-ui/react-toggle-group@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-0.1.5.tgz#9e4d65e22c4fc0ba3a42fbc8d5496c430e5e9852" @@ -10095,6 +10125,11 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +goober@^2.1.10: + version "2.1.10" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.10.tgz#058def43ba1e3b06f973dbb372a4978aa42f1049" + integrity sha512-7PpuQMH10jaTWm33sQgBQvz45pHR8N4l3Cu3WMGEWmHShAcTuuP7I+5/DwKo39fwti5A80WAjvqgz6SSlgWmGA== + got@^11.0.0: version "11.8.5" resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" @@ -14643,6 +14678,13 @@ react-error-boundary@^3.1.0, react-error-boundary@^3.1.4: dependencies: "@babel/runtime" "^7.12.5" +react-hot-toast@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.3.0.tgz#70b3d183ac2a4afb6b17cda4a7f4cfe02e730415" + integrity sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA== + dependencies: + goober "^2.1.10" + react-hotkeys-hook@^3.4.4: version "3.4.6" resolved "https://registry.yarnpkg.com/react-hotkeys-hook/-/react-hotkeys-hook-3.4.6.tgz#21eda8e97121583a14056479e3eea9e51d2e2a69"