feat: Allow page clone in Content Edit mode (#4531)

## Description

ref #3994
+ disable edit project settings

## 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:
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
This commit is contained in:
Ivan Starkov authored and GitHub committed 2024-12-07 12:52:25 +03:00
1 parent 1f09b0e5d7
commit b82fcd494b
3 files changed
+86 -69

No files matched your search

@@ -1679,17 +1679,16 @@ const PageSettingsView = ({
/>
</Tooltip>
)}
{isDesignMode && (
<Tooltip content="Duplicate page" side="bottom">
<Button
color="ghost"
prefix={<CopyIcon />}
onClick={onDuplicate}
aria-label="Duplicate page"
tabIndex={2}
/>
</Tooltip>
)}
<Tooltip content="Duplicate page" side="bottom">
<Button
color="ghost"
prefix={<CopyIcon />}
onClick={onDuplicate}
aria-label="Duplicate page"
tabIndex={2}
/>
</Tooltip>
<Tooltip content="Close page settings" side="bottom">
<Button
@@ -18,6 +18,7 @@ import { SectionPublish } from "./section-publish";
import { SectionMarketplace } from "./section-marketplace";
import { leftPanelWidth, rightPanelWidth } from "./utils";
import type { FunctionComponent } from "react";
import { $isDesignMode } from "~/shared/nano-states";
type SectionName = "general" | "redirects" | "publish" | "marketplace";
@@ -37,6 +38,8 @@ export const ProjectSettingsView = ({
onSectionChange?: (section: SectionName) => void;
onOpenChange?: (isOpen: boolean) => void;
}) => {
const isDesignMode = useStore($isDesignMode);
return (
<Dialog open={sections.has(currentSection!)} onOpenChange={onOpenChange}>
<DialogContent
@@ -46,66 +49,68 @@ export const ProjectSettingsView = ({
height: theme.spacing[35],
}}
>
<Flex grow>
<List asChild>
<Flex
direction="column"
shrink={false}
css={{
width: leftPanelWidth,
borderRight: `1px solid ${theme.colors.borderMain}`,
}}
>
{Array.from(sections.keys()).map((name, index) => {
return (
<ListItem
current={currentSection === name}
asChild
index={index}
key={name}
onSelect={() => {
onSectionChange?.(name);
}}
>
<Flex
css={{
position: "relative",
height: theme.spacing[13],
paddingInline: theme.panel.paddingInline,
outline: "none",
"&:focus-visible, &:hover": {
background: theme.colors.backgroundHover,
},
"&[aria-current=true]": {
background: theme.colors.backgroundItemCurrent,
color: theme.colors.foregroundMain,
},
<fieldset style={{ display: "contents" }} disabled={!isDesignMode}>
<Flex grow>
<List asChild>
<Flex
direction="column"
shrink={false}
css={{
width: leftPanelWidth,
borderRight: `1px solid ${theme.colors.borderMain}`,
}}
>
{Array.from(sections.keys()).map((name, index) => {
return (
<ListItem
current={currentSection === name}
asChild
index={index}
key={name}
onSelect={() => {
onSectionChange?.(name);
}}
align="center"
>
<Text variant="labelsTitleCase" truncate>
{name}
</Text>
</Flex>
</ListItem>
);
})}
</Flex>
</List>
<ScrollArea>
<Grid gap={2} css={{ py: theme.spacing[5] }}>
{currentSection === "general" && <SectionGeneral />}
{currentSection === "redirects" && <SectionRedirects />}
{currentSection === "publish" && <SectionPublish />}
{currentSection === "marketplace" && <SectionMarketplace />}
<div />
</Grid>
</ScrollArea>
</Flex>
{/* Title is at the end intentionally,
* to make the close button last in the tab order
*/}
<DialogTitle>Project Settings</DialogTitle>
<Flex
css={{
position: "relative",
height: theme.spacing[13],
paddingInline: theme.panel.paddingInline,
outline: "none",
"&:focus-visible, &:hover": {
background: theme.colors.backgroundHover,
},
"&[aria-current=true]": {
background: theme.colors.backgroundItemCurrent,
color: theme.colors.foregroundMain,
},
}}
align="center"
>
<Text variant="labelsTitleCase" truncate>
{name}
</Text>
</Flex>
</ListItem>
);
})}
</Flex>
</List>
<ScrollArea>
<Grid gap={2} css={{ py: theme.spacing[5] }}>
{currentSection === "general" && <SectionGeneral />}
{currentSection === "redirects" && <SectionRedirects />}
{currentSection === "publish" && <SectionPublish />}
{currentSection === "marketplace" && <SectionMarketplace />}
<div />
</Grid>
</ScrollArea>
</Flex>
{/* Title is at the end intentionally,
* to make the close button last in the tab order
*/}
<DialogTitle>Project Settings</DialogTitle>
</fieldset>
</DialogContent>
</Dialog>
);
@@ -41,6 +41,7 @@ import {
DialogClose,
Flex,
rawTheme,
globalCss,
} from "@webstudio-is/design-system";
import { CrossIcon, MaximizeIcon, MinimizeIcon } from "@webstudio-is/icons";
import { solarizedLight } from "./code-highlight";
@@ -67,6 +68,12 @@ export const getMinMaxHeightVars = ({
[maxHeightVar]: maxHeight,
});
const globalStyles = globalCss({
"fieldset[disabled] .cm-editor": {
opacity: 0.3,
},
});
const editorContentStyle = css({
...textVariants.mono,
// fit editor into parent if stretched
@@ -188,6 +195,8 @@ export const EditorContent = ({
onChange,
onBlur,
}: EditorContentProps) => {
globalStyles();
const editorRef = useRef<null | HTMLDivElement>(null);
const viewRef = useRef<undefined | EditorView>();
@@ -222,9 +231,13 @@ export const EditorContent = ({
if (view === undefined) {
return;
}
const hasDisabledFieldset =
editorRef.current?.closest("fieldset[disabled]");
view.dispatch({
effects: StateEffect.reconfigure.of([
...extensions,
...(hasDisabledFieldset ? [EditorView.editable.of(false)] : []),
autocompletionTooltipTheme,
history(),
drawSelection(),