Skip to content

Commit

Permalink
feat: Builder mode switch menu (#4423)
Browse files Browse the repository at this point in the history
## Description

ref #3994

<img width="256" alt="image"
src="https://github.com/user-attachments/assets/ee37fe7d-41ed-40f3-909f-a29d12d07e4b">

## Steps for reproduction

1. click button
2. 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:
5de6)
- [ ] 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
istarkov authored Nov 19, 2024
1 parent 1fd98a2 commit 145f8d9
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { IconComponent } from "@webstudio-is/icons";
import {
Box,
DropdownMenu,
DropdownMenuArrow,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
Expand All @@ -14,6 +13,7 @@ import {
DropdownMenuTrigger,
Flex,
IconButton,
MenuCheckedIcon,
theme,
} from "@webstudio-is/design-system";
import { declarationDescriptions } from "@webstudio-is/css-data";
Expand Down Expand Up @@ -86,6 +86,7 @@ export const MenuControl = ({
text="sentence"
key={name}
value={name}
icon={<MenuCheckedIcon />}
onFocus={() => {
setValue(
{ type: "keyword", value: name },
Expand Down Expand Up @@ -120,7 +121,6 @@ export const MenuControl = ({
<DropdownMenuItem hint>
<Box css={{ width: theme.spacing[25] }}>{description}</Box>
</DropdownMenuItem>
<DropdownMenuArrow />
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
Expand Down
138 changes: 138 additions & 0 deletions apps/builder/app/builder/features/topbar/builder-mode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { useStore } from "@nanostores/react";
import { EditIcon, PaintBrushIcon, PlayIcon } from "@webstudio-is/icons";
import {
Box,
DropdownMenuItemRightSlot,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
Flex,
Kbd,
menuItemCss,
styled,
theme,
ToolbarToggleItem,
} from "@webstudio-is/design-system";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuPortal,
DropdownMenuTrigger,
} from "@webstudio-is/design-system";
import {
$builderMode,
$isContentModeAllowed,
$isDesignModeAllowed,
isBuilderMode,
setBuilderMode,
type BuilderMode,
} from "~/shared/nano-states";
import { useState } from "react";

const StyledMenuItem = styled(DropdownMenuRadioItem, {
"&:where([data-state='checked'])": {
backgroundColor: `oklch(from ${theme.colors.backgroundItemMenuItemHover} l c h / 0.3)`,
},
});

export const BuilderModeDropDown = () => {
const builderMode = useStore($builderMode);
const isContentModeAllowed = useStore($isContentModeAllowed);
const isDesignModeAllowed = useStore($isDesignModeAllowed);

const menuItems = {
preview: {
icon: <PlayIcon />,
description: "View the page as it will appear to users",
title: "Preview",
shortcut: ["cmd", "shift", "p"],
enabled: true,
},
design: {
icon: <PaintBrushIcon />,
description: "Edit components, styles, and properties",
title: "Design",
shortcut: ["cmd", "shift", "d"],
enabled: isDesignModeAllowed,
},
content: {
icon: <EditIcon />,
description: "Modify the page content",
title: "Build",
shortcut: ["cmd", "shift", "b"],
enabled: isContentModeAllowed,
},
} as const;

const [activeMode, setActiveMode] = useState<BuilderMode | undefined>();

const handleFocus = (mode: BuilderMode) => () => {
setActiveMode(mode);
};

const handleBlur = () => {
setActiveMode(undefined);
};

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<ToolbarToggleItem
value="preview"
aria-label="Toggle Preview"
variant="preview"
tabIndex={0}
>
{menuItems[builderMode].icon}
</ToolbarToggleItem>
</DropdownMenuTrigger>

<DropdownMenuPortal>
<DropdownMenuContent
sideOffset={4}
collisionPadding={16}
side="bottom"
loop
>
<DropdownMenuRadioGroup
value={builderMode}
onValueChange={(value) => {
if (isBuilderMode(value)) {
setBuilderMode(value);
}
}}
>
{Object.entries(menuItems)
.filter(([_, { enabled }]) => enabled)
.map(([mode, { icon, title, shortcut }]) => (
<StyledMenuItem
key={mode}
value={mode}
onFocus={handleFocus(mode as BuilderMode)}
onBlur={handleBlur}
>
<Flex
css={{ py: theme.spacing[4], px: theme.spacing[5] }}
gap={2}
>
{icon}
<Box>{title}</Box>
</Flex>
<DropdownMenuItemRightSlot>
<Kbd value={shortcut} />
</DropdownMenuItemRightSlot>
</StyledMenuItem>
))}
</DropdownMenuRadioGroup>
<DropdownMenuSeparator />

<div className={menuItemCss({ hint: true })}>
<Box css={{ width: theme.spacing[25] }}>
{menuItems[activeMode ?? builderMode].description}
</Box>
</div>
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
);
};
24 changes: 0 additions & 24 deletions apps/builder/app/builder/features/topbar/editor.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions apps/builder/app/builder/features/topbar/menu/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const Menu = () => {
<DropdownMenuItem onSelect={() => emitCommand("redo")}>
Redo
<DropdownMenuItemRightSlot>
<Kbd value={["shift", "cmd", "z"]} />
<Kbd value={["cmd", "shift", "z"]} />
</DropdownMenuItemRightSlot>
</DropdownMenuItem>
{/* https://github.com/webstudio-is/webstudio/issues/499
Expand Down Expand Up @@ -145,7 +145,7 @@ export const Menu = () => {
</DropdownMenuItemRightSlot>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => emitCommand("togglePreview")}>
<DropdownMenuItem onSelect={() => emitCommand("togglePreviewMode")}>
Preview
<DropdownMenuItemRightSlot>
<Kbd value={["cmd", "shift", "p"]} />
Expand Down
24 changes: 0 additions & 24 deletions apps/builder/app/builder/features/topbar/preview.tsx

This file was deleted.

9 changes: 4 additions & 5 deletions apps/builder/app/builder/features/topbar/topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "@webstudio-is/design-system";
import type { Project } from "@webstudio-is/project";
import { $editingPageId, $pages } from "~/shared/nano-states";
import { PreviewButton } from "./preview";

import { ShareButton } from "./share";
import { PublishButton } from "./publish";
import { SyncStatus } from "./sync-status";
Expand All @@ -28,8 +28,7 @@ import { toggleActiveSidebarPanel } from "~/builder/shared/nano-states";
import type { ReactNode } from "react";
import { CloneButton } from "./clone";
import { $selectedPage } from "~/shared/awareness";
import { EditorButton } from "./editor";
import { isFeatureEnabled } from "@webstudio-is/feature-flags";
import { BuilderModeDropDown } from "./builder-mode";

const PagesButton = () => {
const page = useStore($selectedPage);
Expand Down Expand Up @@ -125,8 +124,8 @@ export const Topbar = ({ project, hasProPlan, css, loading }: TopbarProps) => {
>
<ViewMode />
<SyncStatus />
{isFeatureEnabled("contentEditableMode") && <EditorButton />}
<PreviewButton />

<BuilderModeDropDown />
<ShareButton projectId={project.id} hasProPlan={hasProPlan} />
<PublishButton projectId={project.id} />
<CloneButton />
Expand Down
42 changes: 14 additions & 28 deletions apps/builder/app/builder/shared/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import {
$instances,
$selectedInstanceSelector,
$textEditingInstanceSelector,
$builderMode,
$isDesignMode,
$isDesignModeAllowed,
setBuilderMode,
toggleBuilderMode,
} from "~/shared/nano-states";
import {
$breakpointsMenuView,
Expand Down Expand Up @@ -128,39 +126,27 @@ export const { emitCommand, subscribeCommands } = createCommandsEmitter({
// ui

{
name: "togglePreview",
name: "togglePreviewMode",
defaultHotkeys: ["meta+shift+p", "ctrl+shift+p"],
handler: () => {
setActiveSidebarPanel("auto");

// @todo: This is temporary until we have a combo to switch modes
if ($builderMode.get() === "preview") {
if ($isDesignModeAllowed.get()) {
setBuilderMode("design");
} else {
setBuilderMode("content");
}
} else {
setBuilderMode("preview");
}
toggleBuilderMode("preview");
},
},
{
name: "toggleEditor",
defaultHotkeys: ["meta+shift+e", "ctrl+shift+e"],
name: "toggleDesignMode",
defaultHotkeys: ["meta+shift+d", "ctrl+shift+d"],
handler: () => {
setActiveSidebarPanel("auto");

// @todo: This is temporary until we have a combo to switch modes
if ($builderMode.get() === "content") {
if ($isDesignModeAllowed.get()) {
setBuilderMode("design");
} else {
setBuilderMode("preview");
}
} else {
setBuilderMode("content");
}
toggleBuilderMode("design");
},
},
{
name: "toggleBuildMode",
defaultHotkeys: ["meta+shift+b", "ctrl+shift+b"],
handler: () => {
setActiveSidebarPanel("auto");
toggleBuilderMode("content");
},
},
{
Expand Down
11 changes: 8 additions & 3 deletions apps/builder/app/routes/rest.patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,15 @@ export const action = async ({
};
}

const [canEditContent, canEdit] = await Promise.all([
// @todo: Commented until better Content Edit mode checks are implemented
const [canEditContent /* canEdit */] = await Promise.all([
authorizeProject.hasProjectPermit({ projectId, permit: "edit" }, context),
/*
authorizeProject.hasProjectPermit(
{ projectId, permit: "build" },
context
),
*/
]);

if (canEditContent === false) {
Expand All @@ -134,7 +137,7 @@ export const action = async ({
};
}

const isContentEditMode = canEditContent && !canEdit;
// const isContentEditMode = canEditContent && !canEdit;

const build = await loadRawBuildById(context, buildId);

Expand Down Expand Up @@ -231,12 +234,14 @@ export const action = async ({
}

// This is super simple and naive implementation of permissions checks for Content Edit mode
// @todo: Implement proper permissions checks, one of idea is to allow any changes to the new instances
/*
if (isContentEditMode) {
return {
status: "error",
errors: `You don't have permission to patch namespace ${namespace}`,
};
}
}*/

if (namespace === "styleSourceSelections") {
const styleSourceSelections =
Expand Down
Loading

0 comments on commit 145f8d9

Please sign in to comment.