diff --git a/.lostpixel/baseline/components-typography--typography.png b/.lostpixel/baseline/components-typography--typography.png index 793bdb0ba1..f77534cf2a 100644 Binary files a/.lostpixel/baseline/components-typography--typography.png and b/.lostpixel/baseline/components-typography--typography.png differ diff --git a/.lostpixel/baseline/library-button--button.png b/.lostpixel/baseline/library-button--button.png index e7bb148e93..b8d55443f4 100644 Binary files a/.lostpixel/baseline/library-button--button.png and b/.lostpixel/baseline/library-button--button.png differ diff --git a/.lostpixel/baseline/library-floating-panel-dialog--dialog.png b/.lostpixel/baseline/library-floating-panel-dialog--dialog.png new file mode 100644 index 0000000000..419dc1a82a Binary files /dev/null and b/.lostpixel/baseline/library-floating-panel-dialog--dialog.png differ diff --git a/.lostpixel/baseline/library-floating-panel-popover--popover.png b/.lostpixel/baseline/library-floating-panel-popover--popover.png new file mode 100644 index 0000000000..866af011e1 Binary files /dev/null and b/.lostpixel/baseline/library-floating-panel-popover--popover.png differ diff --git a/.lostpixel/baseline/library-label--label.png b/.lostpixel/baseline/library-label--label.png index 53f45a14ef..1e6524ce42 100644 Binary files a/.lostpixel/baseline/library-label--label.png and b/.lostpixel/baseline/library-label--label.png differ diff --git a/.lostpixel/baseline/library-menu--menu.png b/.lostpixel/baseline/library-menu--menu.png deleted file mode 100644 index 3f2ccffb2f..0000000000 Binary files a/.lostpixel/baseline/library-menu--menu.png and /dev/null differ diff --git a/.lostpixel/baseline/library-menu-menu-item--menu-menu-item.png b/.lostpixel/baseline/library-menu-menu-item--menu-menu-item.png new file mode 100644 index 0000000000..35695b4e96 Binary files /dev/null and b/.lostpixel/baseline/library-menu-menu-item--menu-menu-item.png differ diff --git a/.lostpixel/baseline/library-separator--separator.png b/.lostpixel/baseline/library-separator--separator.png new file mode 100644 index 0000000000..342b1af8b7 Binary files /dev/null and b/.lostpixel/baseline/library-separator--separator.png differ diff --git a/.lostpixel/baseline/library-title--title.png b/.lostpixel/baseline/library-title--title.png new file mode 100644 index 0000000000..067e263182 Binary files /dev/null and b/.lostpixel/baseline/library-title--title.png differ diff --git a/apps/designer/app/auth/login-button.tsx b/apps/designer/app/auth/login-button.tsx index 95c1143871..3aeddef771 100644 --- a/apps/designer/app/auth/login-button.tsx +++ b/apps/designer/app/auth/login-button.tsx @@ -26,7 +26,7 @@ export const LoginButton = ({ {...props} type="submit" disabled={disabled} - variant="neutral" + color="neutral" css={{ width: "100%" }} > diff --git a/apps/designer/app/dashboard/header.tsx b/apps/designer/app/dashboard/header.tsx index 89c8d34c19..d89c0d5b29 100644 --- a/apps/designer/app/dashboard/header.tsx +++ b/apps/designer/app/dashboard/header.tsx @@ -33,7 +33,7 @@ const Menu = ({ user }: { user: User }) => { return ( - - + + } /> @@ -461,26 +456,24 @@ const PageSettingsView = ({ <> {isHomePage === false && ( - } onClick={onDelete} aria-label="Delete page" tabIndex={2} - > - - + /> )} {onClose && ( - } onClick={onClose} aria-label="Close page settings" tabIndex={2} - > - - + /> )} diff --git a/apps/designer/app/designer/features/topbar/publish.tsx b/apps/designer/app/designer/features/topbar/publish.tsx index ea25167ef9..aec7673908 100644 --- a/apps/designer/app/designer/features/topbar/publish.tsx +++ b/apps/designer/app/designer/features/topbar/publish.tsx @@ -63,7 +63,10 @@ const Content = ({ project }: PublishButtonProps) => { {fetcher.data?.errors !== undefined && ( {fetcher.data?.errors} )} - diff --git a/apps/designer/app/designer/shared/floating-panel/floating-panel.tsx b/apps/designer/app/designer/shared/floating-panel/floating-panel.tsx index 8c2e72cd36..86a024895f 100644 --- a/apps/designer/app/designer/shared/floating-panel/floating-panel.tsx +++ b/apps/designer/app/designer/shared/floating-panel/floating-panel.tsx @@ -1,9 +1,7 @@ import { - Popover, - PopoverTrigger, - PopoverContent, - PopoverHeader, - PopoverPortal, + FloatingPanelPopover as Popover, + theme, + css, } from "@webstudio-is/design-system"; import { MutableRefObject, @@ -13,7 +11,6 @@ import { useState, } from "react"; import { FloatingPanelContext } from "./floating-panel-provider"; -import { theme } from "@webstudio-is/design-system"; const useSideOffset = ({ isOpen, @@ -59,6 +56,8 @@ type FloatingPanelProps = { onOpenChange?: (isOpen: boolean) => void; }; +const contetnStyles = css({ width: theme.spacing[30] }); + export const FloatingPanel = ({ title, content, @@ -68,8 +67,8 @@ export const FloatingPanel = ({ const { isOpen, handleOpenChange, triggerRef, sideOffset } = useLogic(onOpenChange); return ( - - + { @@ -77,19 +76,16 @@ export const FloatingPanel = ({ }} > {children} - - - - {content} - - - - + + + {content} + {title} + + ); }; diff --git a/apps/designer/app/designer/shared/image-manager/image-info.tsx b/apps/designer/app/designer/shared/image-manager/image-info.tsx index 225e895929..0d34a4a36d 100644 --- a/apps/designer/app/designer/shared/image-manager/image-info.tsx +++ b/apps/designer/app/designer/shared/image-manager/image-info.tsx @@ -49,7 +49,7 @@ export const ImageInfo = ({ asset, onDelete }: ImageInfoProps) => { ) : null} @@ -123,7 +123,7 @@ const Menu = ({ {/* @todo need a menu item that looks like one from dropdown but without DropdownMenu */} + + +
+ +

This is a description

+
+
+ + + + + + +
+
+ + {/* Title is at the end intentionally, + * to make the close button last in the tab order + */} + Title +
+ +); +export { DialogDemo as Dialog }; diff --git a/packages/design-system/src/components/floating-panel/dialog.tsx b/packages/design-system/src/components/floating-panel/dialog.tsx new file mode 100644 index 0000000000..90e23c83b6 --- /dev/null +++ b/packages/design-system/src/components/floating-panel/dialog.tsx @@ -0,0 +1,92 @@ +import React, { type ReactNode, type ComponentProps, type Ref } from "react"; +import * as Primitive from "@radix-ui/react-dialog"; +import { css, theme, keyframes } from "../../stitches.config"; +import { Title } from "../title"; +import { floatingPanelStyles, CloseButton, TitleSlot } from "./shared"; + +export const Root = Primitive.Root; +export const Trigger = Primitive.Trigger; + +// Wrap a close button with this +// https://www.radix-ui.com/docs/primitives/components/dialog#close +export const Close = Primitive.Close; + +// An optional accessible description to be announced when the dialog is opened +// https://www.radix-ui.com/docs/primitives/components/dialog#description +export const Description = Primitive.Description; + +export const Content = React.forwardRef( + ( + { children, className, ...props }: ComponentProps, + forwardedRef: Ref + ) => { + return ( + + + + {children} + + + ); + } +); +Content.displayName = "Content"; + +const ContentTitle = ({ + children, + closeLabel = "Close dialog", +}: { + children: ReactNode; + closeLabel?: string; +}) => ( + + + <CloseButton aria-label={closeLabel} /> + </Primitive.Close> + } + > + <Primitive.Title className={titleStyles()}>{children}</Primitive.Title> + + +); +export { ContentTitle as Title }; + +// Styles specific to dialog +// (as opposed to be common for all floating panels) + +const overlayShow = keyframes({ + from: { opacity: 0 }, + to: { opacity: 1 }, +}); +const overlayStyles = css({ + backgroundColor: "rgba(17, 24, 28, 0.66)", + position: "fixed", + inset: 0, + animation: `${overlayShow} 150ms ${theme.easing.easeOut}`, +}); + +const contentShow = keyframes({ + from: { opacity: 0, transform: "translate(-50%, -48%) scale(0.96)" }, + to: { opacity: 1, transform: "translate(-50%, -50%) scale(1)" }, +}); +const contentStyles = css(floatingPanelStyles, { + position: "fixed", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "90vw", + maxWidth: theme.spacing[35], + maxHeight: "85vh", + animation: `${contentShow} 150ms ${theme.easing.easeOut}`, +}); + +const titleStyles = css({ + // Resetting H2 styles (Primitive.Title is H2) + all: "unset", +}); diff --git a/packages/design-system/src/components/floating-panel/index.ts b/packages/design-system/src/components/floating-panel/index.ts new file mode 100644 index 0000000000..c55484f14d --- /dev/null +++ b/packages/design-system/src/components/floating-panel/index.ts @@ -0,0 +1,7 @@ +/** + * Implementations of the "Floating Panel" component from: + * https://www.figma.com/file/sfCE7iLS0k25qCxiifQNLE/%F0%9F%93%9A-Webstudio-Library?node-id=4%3A2679&t=6Q0l4j0CBvXkuKYp-0 + */ + +export * as FloatingPanelDialog from "./dialog"; +export * as FloatingPanelPopover from "./popover"; diff --git a/packages/design-system/src/components/floating-panel/popover.stories.tsx b/packages/design-system/src/components/floating-panel/popover.stories.tsx new file mode 100644 index 0000000000..10b88e0778 --- /dev/null +++ b/packages/design-system/src/components/floating-panel/popover.stories.tsx @@ -0,0 +1,50 @@ +import { Button } from "../button"; +import { css, theme } from "../../stitches.config"; +import { typography } from "../typography"; +import * as Popover from "./popover"; + +export default { + title: "Library/Floating Panel/Popover", +}; + +const bodyStyles = css({ + padding: theme.spacing[9], +}); + +const descriptionStyles = css(typography.regular, { + marginTop: 0, + marginBottom: theme.spacing[9], +}); + +const buttonsStyle = css({ + display: "flex", + gap: theme.spacing[5], + justifyContent: "flex-end", +}); + +const PopoverDemo = () => ( + + + + + +
+

This is a description

+
+ + + + + + +
+
+ + {/* Title is at the end intentionally, + * to make the close button last in the tab order + */} + Title +
+
+); +export { PopoverDemo as Popover }; diff --git a/packages/design-system/src/components/floating-panel/popover.tsx b/packages/design-system/src/components/floating-panel/popover.tsx new file mode 100644 index 0000000000..d1a10cab5b --- /dev/null +++ b/packages/design-system/src/components/floating-panel/popover.tsx @@ -0,0 +1,56 @@ +import React, { type ComponentProps, type Ref, type ReactNode } from "react"; +import * as Primitive from "@radix-ui/react-popover"; +import { css, theme } from "../../stitches.config"; +import { Title } from "../title"; +import { floatingPanelStyles, CloseButton, TitleSlot } from "./shared"; + +export const Root = Primitive.Root; + +const contentStyles = css(floatingPanelStyles, { + minWidth: theme.spacing[28], + maxWidth: "max-content", +}); + +export const Content = React.forwardRef( + ( + { children, className, ...props }: ComponentProps, + ref: Ref + ) => ( + + + {children} + + + ) +); +Content.displayName = "Content"; + +const PopoverTitle = ({ + children, + closeLabel = "Close dialog", +}: { + children: ReactNode; + closeLabel?: string; +}) => ( + + + <CloseButton aria-label={closeLabel} /> + </Primitive.Close> + } + > + {children} + + +); +export { PopoverTitle as Title }; + +export const Trigger = Primitive.Trigger; +export const Close = Primitive.Close; diff --git a/packages/design-system/src/components/floating-panel/shared.tsx b/packages/design-system/src/components/floating-panel/shared.tsx new file mode 100644 index 0000000000..6b8b6c19aa --- /dev/null +++ b/packages/design-system/src/components/floating-panel/shared.tsx @@ -0,0 +1,38 @@ +import { forwardRef, Ref, type ReactNode, type ComponentProps } from "react"; +import { CrossIcon } from "@webstudio-is/icons"; +import { css, theme } from "../../stitches.config"; +import { Button } from "../button"; +import { Separator } from "../separator"; + +export const floatingPanelStyles = css({ + border: `1px solid ${theme.colors.borderMain}`, + boxShadow: theme.shadows.menuDropShadow, + background: theme.colors.backgroundPanel, + borderRadius: theme.borderRadius[4], + display: "flex", + flexDirection: "column", + + "&:focus": { + // override browser default + outline: "none", + }, +}); + +const titleSlotStyles = css({ + // We put title at the bottom in DOM to make the close button last in the TAB order + // But visually we want it to be first + order: -1, +}); +export const TitleSlot = ({ children }: { children: ReactNode }) => ( +
+ {children} + +
+); + +export const CloseButton = forwardRef( + (props: ComponentProps, ref: Ref) => ( +