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"