Skip to content

Commit

Permalink
feat: Floating panel v2 (#4613)
Browse files Browse the repository at this point in the history
Closes #4572

## Description

- All floating panels are now using a dialog and are draggable (bindings
and expressions not migrated yet, separate PR)
- Logic for positioning the panels is now in our control and easier to
manage, because we have lots of special cases
- Deleted FloatingPanelPopover, now everything uses either FloatingPanel
or Popover, difference is in positioning logic, FloatingPanel is using
Dialog
- Big center positioned dialogs like code for html embed allow having
another panel open at the same time, wile all other floating panels
close the other panel when clicked
- Most extended settings in style panel are now positioned above style
panel, not on the left

## Steps for reproduction

1. click button
3. expect xyz

## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")

## Before merging

- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
  • Loading branch information
kof authored Dec 20, 2024
1 parent b1b607e commit 6cb74d4
Show file tree
Hide file tree
Showing 43 changed files with 763 additions and 720 deletions.
2 changes: 1 addition & 1 deletion apps/builder/app/builder/features/inspector/inspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import {
Separator,
Tooltip,
Kbd,
FloatingPanelProvider,
} from "@webstudio-is/design-system";
import { StylePanel } from "~/builder/features/style-panel";
import { SettingsPanelContainer } from "~/builder/features/settings-panel";
import { FloatingPanelProvider } from "~/builder/shared/floating-panel";
import {
$registeredComponentMetas,
$dragAndDropState,
Expand Down
4 changes: 2 additions & 2 deletions apps/builder/app/builder/features/pages/folder-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ const DeleteConfirmationDialog = ({
<Flex gap="3" direction="column" css={{ padding: theme.panel.padding }}>
<Text>{`Delete folder "${folder.name}" including all of its pages?`}</Text>
<Flex direction="rowReverse" gap="2">
<DialogClose asChild>
<DialogClose>
<Button
color="destructive"
onClick={() => {
Expand All @@ -539,7 +539,7 @@ const DeleteConfirmationDialog = ({
Delete
</Button>
</DialogClose>
<DialogClose asChild>
<DialogClose>
<Button color="ghost">Cancel</Button>
</DialogClose>
</Flex>
Expand Down
16 changes: 7 additions & 9 deletions apps/builder/app/builder/features/pages/form.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import type { JSX } from "react";
import { forwardRef, type ReactNode } from "react";

export const Form = ({
onSubmit,
children,
}: {
onSubmit: () => void;
children: JSX.Element;
}) => {
export const Form = forwardRef<
HTMLFormElement,
{ onSubmit: () => void; children: ReactNode }
>(({ onSubmit, children }, ref) => {
return (
<form
ref={ref}
onSubmit={(event) => {
event.preventDefault();
onSubmit();
Expand All @@ -19,4 +17,4 @@ export const Form = ({
<input type="submit" hidden />
</form>
);
};
});
15 changes: 10 additions & 5 deletions apps/builder/app/builder/features/pages/page-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useId,
useEffect,
type JSX,
useRef,
} from "react";
import { useStore } from "@nanostores/react";
import { useDebouncedCallback } from "use-debounce";
Expand Down Expand Up @@ -56,6 +57,7 @@ import {
Switch,
PanelTitle,
TitleSuffixSpacer,
FloatingPanelProvider,
} from "@webstudio-is/design-system";
import {
ChevronDoubleLeftIcon,
Expand Down Expand Up @@ -1664,6 +1666,7 @@ const PageSettingsView = ({
children: JSX.Element;
}) => {
const isDesignMode = useStore($isDesignMode);
const containerRef = useRef<HTMLFormElement>(null);
return (
<>
<PanelTitle
Expand Down Expand Up @@ -1706,11 +1709,13 @@ const PageSettingsView = ({
Page Settings
</PanelTitle>
<Separator />
<Form onSubmit={onClose}>
<fieldset style={{ display: "contents" }} disabled={!isDesignMode}>
{children}
</fieldset>
</Form>
<FloatingPanelProvider container={containerRef}>
<Form onSubmit={onClose} ref={containerRef}>
<fieldset style={{ display: "contents" }} disabled={!isDesignMode}>
{children}
</fieldset>
</Form>
</FloatingPanelProvider>
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FloatingPanel } from "~/builder/shared/floating-panel";
import { ImageManager } from "~/builder/shared/image-manager";
import type { ReactElement } from "react";
import { FloatingPanel } from "@webstudio-is/design-system";
import { ImageManager } from "~/builder/shared/image-manager";

// @todo should be moved to shared as its being reused in another feature
export const ImageControl = (props: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const ProjectSettingsView = ({
return (
<Dialog open={sections.has(currentSection!)} onOpenChange={onOpenChange}>
<DialogContent
draggable
css={{
width: `calc(${leftPanelWidth} + ${rightPanelWidth})`,
maxWidth: "none",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export const SectionGeneral = () => {
tag to every page across the published project.
</Text>
<CodeEditor
title="Custom code"
lang="html"
value={meta.code ?? ""}
onChange={handleSave("code")}
Expand Down
21 changes: 17 additions & 4 deletions apps/builder/app/builder/features/settings-panel/controls/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import {
import { useState } from "react";
import {
Button,
DialogClose,
DialogMaximize,
DialogTitle,
DialogTitleActions,
Flex,
SmallIconButton,
Text,
Expand Down Expand Up @@ -161,10 +165,19 @@ export const CodeControl = ({
<CodeEditor
lang={lang}
title={
<Flex gap="1" align="center">
<Text variant="labelsTitleCase">Code Editor</Text>
{errorInfo}
</Flex>
<DialogTitle
suffix={
<DialogTitleActions>
<DialogMaximize />
<DialogClose />
</DialogTitleActions>
}
>
<Flex gap="1" align="center">
<Text variant="labelsTitleCase">Code Editor</Text>
{errorInfo}
</Flex>
</DialogTitle>
}
readOnly={overwritable === false}
invalid={error !== undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import {
Flex,
SmallIconButton,
Text,
FloatingPanel,
} from "@webstudio-is/design-system";
import { TrashIcon } from "@webstudio-is/icons";
import type { Prop } from "@webstudio-is/sdk";
import { $assets } from "~/shared/nano-states";
import { FloatingPanel } from "~/builder/shared/floating-panel";
import { ImageManager } from "~/builder/shared/image-manager";
import { type ControlProps } from "../shared";
import { acceptToMimeCategories } from "@webstudio-is/asset-uploader";
Expand Down
155 changes: 75 additions & 80 deletions apps/builder/app/builder/features/settings-panel/variable-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,17 @@ import {
useImperativeHandle,
useRef,
createContext,
useContext,
useEffect,
} from "react";
import { mergeRefs } from "@react-aria/utils";
import { CopyIcon, RefreshIcon, UpgradeIcon } from "@webstudio-is/icons";
import {
Box,
Button,
DialogClose,
DialogTitle,
DialogTitleActions,
Flex,
FloatingPanelPopover,
FloatingPanelPopoverContent,
FloatingPanelPopoverTitle,
FloatingPanelPopoverTrigger,
FloatingPanel,
Grid,
InputErrorsTooltip,
InputField,
Expand Down Expand Up @@ -59,7 +57,6 @@ import {
import { serverSyncStore } from "~/shared/sync";

import { BindingPopoverProvider } from "~/builder/shared/binding-popover";
import { useSideOffset } from "~/builder/shared/floating-panel";
import {
EditorDialog,
EditorDialogButton,
Expand Down Expand Up @@ -618,14 +615,15 @@ const areAllFormErrorsVisible = (form: null | HTMLFormElement) => {
return true;
};

export const VariablePopoverTrigger = forwardRef<
HTMLButtonElement,
{ variable?: DataSource; children: ReactNode }
>(({ variable, children }, ref) => {
export const VariablePopoverTrigger = ({
variable,
children,
}: {
variable?: DataSource;
children: ReactNode;
}) => {
const areResourcesLoading = useStore($areResourcesLoading);
const [isOpen, setOpen] = useState(false);
const { containerRef } = useContext(VariablePopoverContext);
const [triggerRef, sideOffsset] = useSideOffset({ isOpen, containerRef });
const bindingPopoverContainerRef = useRef<HTMLDivElement>(null);
const panelRef = useRef<undefined | PanelApi>(undefined);
const formRef = useRef<HTMLFormElement>(null);
Expand All @@ -635,8 +633,7 @@ export const VariablePopoverTrigger = forwardRef<
const requiresUpgrade = allowDynamicData === false && isResource;

return (
<FloatingPanelPopover
modal
<FloatingPanel
open={isOpen}
onOpenChange={(newOpen) => {
if (newOpen) {
Expand All @@ -652,16 +649,64 @@ export const VariablePopoverTrigger = forwardRef<
// prevent closing when not all errors are shown to user
}
}}
>
<FloatingPanelPopoverTrigger ref={mergeRefs(ref, triggerRef)} asChild>
{children}
</FloatingPanelPopoverTrigger>
<FloatingPanelPopoverContent
ref={bindingPopoverContainerRef}
sideOffset={sideOffsset}
side="left"
align="start"
>
title={
variable === undefined ? (
<DialogTitle>New Variable</DialogTitle>
) : (
<DialogTitle
suffix={
<DialogTitleActions>
{variable?.type === "resource" && (
<>
{/* allow to copy curl only for default and graphql resource controls */}
{resources.get(variable.resourceId)?.control !==
"system" && (
<Tooltip
content="Copy resource as cURL command"
side="bottom"
>
<Button
aria-label="Copy resource as cURL command"
prefix={<CopyIcon />}
color="ghost"
onClick={() => {
const resourceRequest = getComputedResource(
variable.resourceId
);
if (resourceRequest) {
navigator.clipboard.writeText(
generateCurl(resourceRequest)
);
}
}}
/>
</Tooltip>
)}
<Tooltip content="Refresh resource data" side="bottom">
<Button
aria-label="Refresh resource data"
prefix={<RefreshIcon />}
color="ghost"
disabled={areResourcesLoading}
onClick={() => {
formRef.current?.requestSubmit();
if (formRef.current?.checkValidity()) {
invalidateResource(variable.resourceId);
}
}}
/>
</Tooltip>
</>
)}
<DialogClose />
</DialogTitleActions>
}
>
Edit Variable
</DialogTitle>
)
}
content={
<ScrollArea
css={{
// flex fixes content overflowing artificial scroll area
Expand Down Expand Up @@ -731,61 +776,11 @@ export const VariablePopoverTrigger = forwardRef<
</form>
</Flex>
</ScrollArea>
{/* put after content to avoid auto focusing "Close" button */}
{variable === undefined ? (
<FloatingPanelPopoverTitle>New Variable</FloatingPanelPopoverTitle>
) : (
<FloatingPanelPopoverTitle
actions={
variable?.type === "resource" && (
<>
{/* allow to copy curl only for default and graphql resource controls */}
{resources.get(variable.resourceId)?.control !== "system" && (
<Tooltip
content="Copy resource as cURL command"
side="bottom"
>
<Button
aria-label="Copy resource as cURL command"
prefix={<CopyIcon />}
color="ghost"
onClick={() => {
const resourceRequest = getComputedResource(
variable.resourceId
);
if (resourceRequest) {
navigator.clipboard.writeText(
generateCurl(resourceRequest)
);
}
}}
/>
</Tooltip>
)}
<Tooltip content="Refresh resource data" side="bottom">
<Button
aria-label="Refresh resource data"
prefix={<RefreshIcon />}
color="ghost"
disabled={areResourcesLoading}
onClick={() => {
formRef.current?.requestSubmit();
if (formRef.current?.checkValidity()) {
invalidateResource(variable.resourceId);
}
}}
/>
</Tooltip>
</>
)
}
>
Edit Variable
</FloatingPanelPopoverTitle>
)}
</FloatingPanelPopoverContent>
</FloatingPanelPopover>
}
>
{children}
</FloatingPanel>
);
});
};

VariablePopoverTrigger.displayName = "VariablePopoverTrigger";
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {
EnhancedTooltip,
Flex,
NestedInputButton,
FloatingPanel,
} from "@webstudio-is/design-system";
import { FontsManager } from "~/builder/shared/fonts-manager";
import { FloatingPanel } from "~/builder/shared/floating-panel";
import { forwardRef, useMemo, useState, type ComponentProps } from "react";
import { toValue } from "@webstudio-is/css-engine";
import { matchSorter } from "match-sorter";
Expand Down Expand Up @@ -68,8 +68,7 @@ export const FontFamilyControl = () => {
suffix={
<FloatingPanel
title="Fonts"
isOpen={isFontManagerOpen}
onIsOpenChange={setIsFontMangerOpen}
onOpenChange={setIsFontMangerOpen}
content={
<FontsManager
value={value.type === "fontFamily" ? value : undefined}
Expand Down
Loading

0 comments on commit 6cb74d4

Please sign in to comment.