diff --git a/apps/designer/app/designer/features/topbar/share.tsx b/apps/designer/app/designer/features/topbar/share.tsx
index 58fa4f8827..b1c967c5b1 100644
--- a/apps/designer/app/designer/features/topbar/share.tsx
+++ b/apps/designer/app/designer/features/topbar/share.tsx
@@ -1,22 +1,25 @@
import {
Button,
- Flex,
DeprecatedIconButton,
Popover,
PopoverContent,
PopoverPortal,
PopoverTrigger,
- TextField,
} from "@webstudio-is/design-system";
import { Share1Icon } from "@webstudio-is/icons";
import { useIsShareDialogOpen } from "../../shared/nano-states";
import { theme } from "@webstudio-is/design-system";
+import {
+ ShareProjectDeprecated,
+ ShareProject,
+ LinkOptions,
+} from "~/shared/share-project";
-type ShareButtonProps = {
+type ShareButtonDeprecatedProps = {
url: string;
};
-const Content = ({ url }: ShareButtonProps) => {
+const Content = ({ url }: ShareButtonDeprecatedProps) => {
if (typeof location === "undefined") {
return null;
}
@@ -29,31 +32,12 @@ const Content = ({ url }: ShareButtonProps) => {
event.preventDefault();
}}
>
-
+
);
};
-export const ShareButton = (props: ShareButtonProps) => {
+export const ShareButtonDeprecated = (props: ShareButtonDeprecatedProps) => {
const [isOpen, setIsOpen] = useIsShareDialogOpen();
return (
@@ -68,3 +52,24 @@ export const ShareButton = (props: ShareButtonProps) => {
);
};
+
+export const ShareButton = () => {
+ const handleChange = (link: LinkOptions) => {
+ // @todo implement
+ };
+ const handleDelete = (link: LinkOptions) => {
+ // @todo implement
+ };
+ const handleCreate = () => {
+ // @todo implement
+ };
+ return (
+
+
+
+ );
+};
diff --git a/apps/designer/app/designer/features/topbar/topbar.tsx b/apps/designer/app/designer/features/topbar/topbar.tsx
index 7b8cfac98a..e2c4283811 100644
--- a/apps/designer/app/designer/features/topbar/topbar.tsx
+++ b/apps/designer/app/designer/features/topbar/topbar.tsx
@@ -1,13 +1,14 @@
import type { Publish } from "~/shared/pubsub";
import { darkTheme, Flex, type CSS } from "@webstudio-is/design-system";
import { PreviewButton } from "./preview";
-import { ShareButton } from "./share";
+import { ShareButton, ShareButtonDeprecated } from "./share";
import { PublishButton } from "./publish";
import { SyncStatus } from "./sync-status";
import { Menu } from "./menu";
import { Breakpoints } from "../breakpoints";
import type { Project } from "@webstudio-is/project";
import { theme } from "@webstudio-is/design-system";
+import { isFeatureEnabled } from "@webstudio-is/feature-flags";
type TopbarProps = {
css: CSS;
@@ -55,7 +56,8 @@ export const Topbar = ({ css, project, publish, previewUrl }: TopbarProps) => {
>
-
+
+ {isFeatureEnabled("share2") && }
diff --git a/apps/designer/app/shared/share-project/index.ts b/apps/designer/app/shared/share-project/index.ts
new file mode 100644
index 0000000000..bc7d2e6a7a
--- /dev/null
+++ b/apps/designer/app/shared/share-project/index.ts
@@ -0,0 +1,2 @@
+export { ShareProject as ShareProjectDeprecated } from "./share-project";
+export * from "./share-project2";
diff --git a/apps/designer/app/shared/share-project/share-project.stories.tsx b/apps/designer/app/shared/share-project/share-project.stories.tsx
new file mode 100644
index 0000000000..5b4ac678e4
--- /dev/null
+++ b/apps/designer/app/shared/share-project/share-project.stories.tsx
@@ -0,0 +1,69 @@
+import { v4 as uuid } from "uuid";
+import type { ComponentStory } from "@storybook/react";
+import { Button } from "@webstudio-is/design-system";
+import { useState } from "react";
+import { type LinkOptions, ShareProject } from "./share-project2";
+
+export default {
+ component: ShareProject,
+};
+
+const initialLinks: Array = [
+ {
+ url: `https://google.com/${uuid()}`,
+ name: "View Only",
+ permission: "view",
+ },
+ {
+ url: `https://google.com/${uuid()}`,
+ name: "View and Edit",
+ permission: "edit",
+ },
+ {
+ url: `https://google.com/${uuid()}`,
+ name: "Build",
+ permission: "build",
+ },
+];
+
+const useShareProject = (initialLinks: Array = []) => {
+ const [links, setLinks] = useState(initialLinks);
+
+ const onChange = (updatedLink: LinkOptions) => {
+ setLinks(
+ links.map((link) => (link.url === updatedLink.url ? updatedLink : link))
+ );
+ };
+ const onDelete = (deletedLink: LinkOptions) => {
+ setLinks(links.filter((link) => link.url !== deletedLink.url));
+ };
+ const onCreate = () => {
+ setLinks([
+ ...links,
+ {
+ url: `https://google.com/${uuid()}`,
+ name: "Custom Link",
+ permission: "view",
+ },
+ ]);
+ };
+ return { links, onChange, onDelete, onCreate };
+};
+
+export const Empty: ComponentStory = () => {
+ const props = useShareProject();
+ return (
+
+
+
+ );
+};
+
+export const WithLinks: ComponentStory = () => {
+ const props = useShareProject(initialLinks);
+ return (
+
+
+
+ );
+};
diff --git a/apps/designer/app/shared/share-project/share-project.tsx b/apps/designer/app/shared/share-project/share-project.tsx
new file mode 100644
index 0000000000..b217baefb8
--- /dev/null
+++ b/apps/designer/app/shared/share-project/share-project.tsx
@@ -0,0 +1,28 @@
+import { Button, Flex, TextField } from "@webstudio-is/design-system";
+
+type ShareProjectProps = { url: string };
+
+export const ShareProject = ({ url }: ShareProjectProps) => {
+ return (
+
+ );
+};
diff --git a/apps/designer/app/shared/share-project/share-project2.tsx b/apps/designer/app/shared/share-project/share-project2.tsx
new file mode 100644
index 0000000000..62d6ed586c
--- /dev/null
+++ b/apps/designer/app/shared/share-project/share-project2.tsx
@@ -0,0 +1,263 @@
+import {
+ Box,
+ Button,
+ css,
+ Flex,
+ Label,
+ Popover,
+ PopoverContent,
+ PopoverHeader,
+ PopoverPortal,
+ PopoverTrigger,
+ rawTheme,
+ Separator,
+ Switch,
+ TextField,
+ theme,
+ Tooltip,
+ useId,
+} from "@webstudio-is/design-system";
+import { CopyIcon, InfoIcon, MenuIcon, PlusIcon } from "@webstudio-is/icons";
+import { Fragment, type ComponentProps } from "react";
+
+const Item = (props: ComponentProps) => (
+
+);
+
+type PermissionProps = {
+ title: string;
+ info: string;
+ checked: boolean;
+ onCheckedChange: (checked: boolean) => void;
+};
+const Permission = ({
+ title,
+ info,
+ checked,
+ onCheckedChange,
+}: PermissionProps) => {
+ const id = useId();
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+type MenuProps = {
+ permission: Permission;
+ name: string;
+ onChangePermission: (permission: Permission) => void;
+ onChangeName: (name: string) => void;
+ onDelete: () => void;
+};
+
+const Menu = ({
+ permission,
+ name,
+ onChangePermission,
+ onChangeName,
+ onDelete,
+}: MenuProps) => {
+ const handleCheckedChange =
+ (permission: Permission) => (checked: boolean) => {
+ if (checked) {
+ onChangePermission(permission);
+ }
+ };
+
+ return (
+
+
+ }
+ variant="ghost"
+ aria-label="Menu Button for options"
+ >
+
+
+ -
+
+ {
+ onChangeName(event.target.value);
+ }}
+ />
+
+
+ -
+
+
+
+
+
+
+ -
+ {/* @todo need a menu item that looks like one from dropdown but without DropdownMenu */}
+
+
+
+
+ );
+};
+
+const itemStyle = css({
+ display: "flex",
+ alignItems: "center",
+ gap: theme.spacing[3],
+ py: theme.spacing[5],
+ px: theme.spacing[9],
+});
+
+type Permission = "view" | "edit" | "build";
+
+export type LinkOptions = {
+ url: string;
+ name: string;
+ permission: Permission;
+};
+
+type SharedLinkItemType = LinkOptions & {
+ onChangePermission: (permission: Permission) => void;
+ onChangeName: (name: string) => void;
+ onDelete: () => void;
+};
+
+const SharedLinkItem = ({
+ url,
+ name,
+ permission,
+ onChangePermission,
+ onChangeName,
+ onDelete,
+}: SharedLinkItemType) => {
+ return (
+
+
+ }
+ onClick={() => {
+ navigator.clipboard.writeText(url);
+ }}
+ >
+ Copy link
+
+
+
+ );
+};
+
+type ShareProjectProps = {
+ links?: Array;
+ onChange: (link: LinkOptions) => void;
+ onDelete: (link: LinkOptions) => void;
+ onCreate: () => void;
+ children: JSX.Element;
+ isOpen?: boolean;
+ onOpenChange?: (isOpen: boolean) => void;
+};
+
+export const ShareProject = ({
+ links = [],
+ onChange,
+ onDelete,
+ onCreate,
+ children,
+ isOpen,
+ onOpenChange,
+}: ShareProjectProps) => {
+ const items = links.map((link) => (
+
+ {
+ onChange({ ...link, permission });
+ }}
+ onChangeName={(name) => {
+ onChange({ ...link, name });
+ }}
+ onDelete={() => {
+ onDelete(link);
+ }}
+ />
+
+
+ ));
+
+ const create = (
+
+ }
+ onClick={() => {
+ onCreate();
+ }}
+ >
+ {links.length === 0 ? "Share a custom link" : "Add another link"}
+
+
+ );
+
+ // @todo this should use FloatingPanel instead of Popover, which we need to refactor first
+ return (
+
+ {children}
+
+
+
+ {items}
+ {create}
+
+
+
+
+
+ );
+};
diff --git a/packages/design-system/src/components/label.tsx b/packages/design-system/src/components/label.tsx
index b694ae7f2e..bfd3b6fe00 100644
--- a/packages/design-system/src/components/label.tsx
+++ b/packages/design-system/src/components/label.tsx
@@ -3,7 +3,7 @@
* https://www.figma.com/file/sfCE7iLS0k25qCxiifQNLE/%F0%9F%93%9A-Webstudio-Library?node-id=4%3A3274
*/
-import type { LabelHTMLAttributes, ReactNode, Ref } from "react";
+import type { ComponentProps, ReactNode, Ref } from "react";
import { forwardRef } from "react";
import { typography } from "../__generated__/figma-design-tokens";
import { styled, theme } from "../stitches.config";
@@ -62,7 +62,7 @@ const StyledLabel = styled("label", typography.labelSentenceCase, {
},
});
-type Props = LabelHTMLAttributes & {
+type Props = ComponentProps & {
color?: "default" | "preset" | "local" | "remote";
disabled?: boolean;
children: ReactNode;
diff --git a/packages/design-system/src/components/popover.tsx b/packages/design-system/src/components/popover.tsx
index c47e3324a8..99bff4c44d 100644
--- a/packages/design-system/src/components/popover.tsx
+++ b/packages/design-system/src/components/popover.tsx
@@ -22,7 +22,7 @@ const StyledContent = styled(PopoverPrimitive.Content, panelStyles, {
backgroundColor: "white",
minWidth: 200,
minHeight: theme.spacing[13],
- maxWidth: 265,
+ maxWidth: "max-content",
"&:focus": {
outline: "none",
},
diff --git a/packages/design-system/src/components/separator.tsx b/packages/design-system/src/components/separator.tsx
index 1fc42eddab..7297bdd7a7 100644
--- a/packages/design-system/src/components/separator.tsx
+++ b/packages/design-system/src/components/separator.tsx
@@ -11,26 +11,15 @@ export const Separator = styled(SeparatorPrimitive.Root, {
variants: {
size: {
- "1": {
+ 1: {
'&[data-orientation="horizontal"]': {
height: theme.spacing[1],
- width: theme.spacing[9],
+ my: theme.spacing[5],
},
'&[data-orientation="vertical"]': {
width: theme.spacing[1],
- height: theme.spacing[9],
- },
- },
- "2": {
- '&[data-orientation="horizontal"]': {
- height: theme.spacing[2],
- width: theme.spacing[17],
- },
-
- '&[data-orientation="vertical"]': {
- width: theme.spacing[2],
- height: theme.spacing[17],
+ mx: theme.spacing[5],
},
},
auto: {
diff --git a/packages/design-system/src/components/toast.tsx b/packages/design-system/src/components/toast.tsx
index 4fb447ce1a..bcab3884cd 100644
--- a/packages/design-system/src/components/toast.tsx
+++ b/packages/design-system/src/components/toast.tsx
@@ -4,7 +4,7 @@ import { keyframes, styled } from "../stitches.config";
import {
CheckCircledIcon,
CrossCircledIcon,
- InfoCircledIcon,
+ InfoIcon,
} from "@webstudio-is/icons";
import { Box } from "./box";
import { theme } from "../stitches.config";
@@ -120,7 +120,7 @@ export const Toaster = () => {
>
{toast.type === "success" && }
{toast.type === "error" && }
- {toast.type === "blank" && }
+ {toast.type === "blank" && }
{toast.message}
diff --git a/packages/design-system/src/components/tooltip.tsx b/packages/design-system/src/components/tooltip.tsx
index 643dc2f1ac..0f28579b06 100644
--- a/packages/design-system/src/components/tooltip.tsx
+++ b/packages/design-system/src/components/tooltip.tsx
@@ -7,10 +7,9 @@ import type { CSS } from "../stitches.config";
import { theme } from "../stitches.config";
export type TooltipProps = ComponentProps &
- ComponentProps & {
+ ComponentProps & {
children: React.ReactElement;
content: React.ReactNode;
- multiline?: boolean;
delayDuration?: number;
disableHoverableContent?: boolean;
css?: CSS;
@@ -20,16 +19,18 @@ const Content = styled(TooltipPrimitive.Content, {
backgroundColor: theme.colors.hiContrast,
color: theme.colors.loContrast,
borderRadius: theme.borderRadius[4],
- padding: `${theme.spacing[3]} ${theme.spacing[5]}`,
+ padding: theme.spacing[5],
zIndex: theme.zIndices[1],
position: "relative",
variants: {
- multiline: {
- true: {
- // @todo make this part of the design system
- maxWidth: 110,
- pb: 7,
+ variant: {
+ wrapped: {
+ maxWidth: theme.spacing["29"],
+ },
+ large: {
+ maxWidth: theme.spacing["32"],
+ padding: theme.spacing[9],
},
},
},
@@ -46,7 +47,6 @@ export const Tooltip = React.forwardRef(
children,
content,
defaultOpen,
- multiline,
delayDuration,
disableHoverableContent,
open,
@@ -80,7 +80,6 @@ export const Tooltip = React.forwardRef(
align="center"
sideOffset={5}
{...props}
- multiline={multiline}
>
{content}
diff --git a/packages/design-system/src/stitches.config.ts b/packages/design-system/src/stitches.config.ts
index cf95fb6d49..dd984d5595 100644
--- a/packages/design-system/src/stitches.config.ts
+++ b/packages/design-system/src/stitches.config.ts
@@ -159,7 +159,7 @@ const spacing = {
33: "320px",
34: "384px",
35: "448px",
-};
+} as const;
const {
styled,
diff --git a/packages/icons/icons/info.svg b/packages/icons/icons/info.svg
new file mode 100644
index 0000000000..b42008c6fd
--- /dev/null
+++ b/packages/icons/icons/info.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/packages/icons/src/__generated__/index.ts b/packages/icons/src/__generated__/index.ts
index 245e5d8779..d4c9932442 100644
--- a/packages/icons/src/__generated__/index.ts
+++ b/packages/icons/src/__generated__/index.ts
@@ -1,12 +1,4 @@
export * from "./auto-scroll";
-export * from "./eyecon-closed";
-export * from "./eyecon-open";
-export * from "./linked";
-export * from "./scroll";
-export * from "./subscript";
-export * from "./superscript";
-export * from "./unlinked";
-export * from "./webstudio";
export * from "./chevron-big-down";
export * from "./chevron-big-left";
export * from "./chevron-big-right";
@@ -16,3 +8,12 @@ export * from "./chevron-filled-left";
export * from "./chevron-filled-right";
export * from "./chevron-filled-up";
export * from "./dot";
+export * from "./eyecon-closed";
+export * from "./eyecon-open";
+export * from "./info";
+export * from "./linked";
+export * from "./scroll";
+export * from "./subscript";
+export * from "./superscript";
+export * from "./unlinked";
+export * from "./webstudio";
diff --git a/packages/icons/src/__generated__/info.tsx b/packages/icons/src/__generated__/info.tsx
new file mode 100644
index 0000000000..ce7983d188
--- /dev/null
+++ b/packages/icons/src/__generated__/info.tsx
@@ -0,0 +1,15 @@
+// Generated from icons/info.svg
+
+import { forwardRef } from "react";
+import { IconProps } from "../types";
+
+// prettier-ignore
+export const InfoIcon = forwardRef(
+ ({ color = "currentColor", ...props }, forwardedRef) => {
+ return (
+
+ );
+ }
+);
+
+InfoIcon.displayName = "InfoIcon";
diff --git a/packages/icons/src/legacy.ts b/packages/icons/src/legacy.ts
index 8d221b609d..dfc160e741 100644
--- a/packages/icons/src/legacy.ts
+++ b/packages/icons/src/legacy.ts
@@ -24,7 +24,6 @@ export {
HeadingIcon,
TextIcon,
SquareIcon,
- InfoCircledIcon,
CrossCircledIcon,
CheckCircledIcon,
TokensIcon,