mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 22:14:29 +09:00
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:
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(),
|
||||
|
||||
Reference in new issue
Block a user