mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 21:15:18 +09:00
Add export dialog to publish (#2269)
## Description <img width="374" alt="image" src="https://github.com/webstudio-is/webstudio/assets/5077042/a76bdeeb-a1c4-4449-a9d4-673896e3309c"> <img width="368" alt="image" src="https://github.com/webstudio-is/webstudio/assets/5077042/ccd71fd1-8d1c-4fc1-b2a2-7534f3f1096e"> ## 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.example` and the `builder/env-check.js` if mandatory
This commit is contained in:
1 parent
e8c2da7da4
commit
47a3f3aaab
7 files changed
+200
-19
No files matched your search
@@ -6,6 +6,7 @@ import {
|
||||
Separator,
|
||||
theme,
|
||||
Text,
|
||||
Grid,
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { DomainRouter } from "@webstudio-is/domain/index.server";
|
||||
import { validateDomain } from "@webstudio-is/domain";
|
||||
@@ -13,12 +14,14 @@ import type { Project } from "@webstudio-is/project";
|
||||
import { useId, useState } from "react";
|
||||
import { createTrpcFetchProxy } from "~/shared/remix/trpc-remix-proxy";
|
||||
import { builderDomainsPath } from "~/shared/router-utils";
|
||||
import { CustomCodeIcon } from "@webstudio-is/icons";
|
||||
|
||||
const trpc = createTrpcFetchProxy<DomainRouter>(builderDomainsPath);
|
||||
|
||||
type DomainsAddProps = {
|
||||
projectId: Project["id"];
|
||||
onCreate: (domain: string) => void;
|
||||
onExportClick: () => void;
|
||||
refreshDomainResult: (
|
||||
input: { projectId: Project["id"] },
|
||||
onSuccess: () => void
|
||||
@@ -33,6 +36,7 @@ export const AddDomain = ({
|
||||
refreshDomainResult,
|
||||
domainState,
|
||||
isPublishing,
|
||||
onExportClick,
|
||||
}: DomainsAddProps) => {
|
||||
const id = useId();
|
||||
const {
|
||||
@@ -131,23 +135,32 @@ export const AddDomain = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={
|
||||
isPublishing || сreateState !== "idle" || domainState !== "idle"
|
||||
}
|
||||
color={isOpen ? "primary" : "neutral"}
|
||||
css={{ width: "100%", flexShrink: 0 }}
|
||||
onClick={() => {
|
||||
if (isOpen === false) {
|
||||
setIsOpen(true);
|
||||
return;
|
||||
<Grid gap={2} columns={2}>
|
||||
<Button
|
||||
disabled={
|
||||
isPublishing || сreateState !== "idle" || domainState !== "idle"
|
||||
}
|
||||
color={isOpen ? "primary" : "neutral"}
|
||||
onClick={() => {
|
||||
if (isOpen === false) {
|
||||
setIsOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
handleCreate();
|
||||
}}
|
||||
>
|
||||
{isOpen ? "Add domain" : "Add a new domain"}
|
||||
</Button>
|
||||
handleCreate();
|
||||
}}
|
||||
>
|
||||
{isOpen ? "Add domain" : "Add a new domain"}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color={"dark"}
|
||||
prefix={<CustomCodeIcon />}
|
||||
onClick={onExportClick}
|
||||
>
|
||||
Export
|
||||
</Button>
|
||||
</Grid>
|
||||
</Flex>
|
||||
{isOpen && <Separator css={{ mb: theme.spacing[5] }} />}
|
||||
</>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
ScrollArea,
|
||||
Box,
|
||||
rawTheme,
|
||||
styled,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { useIsPublishDialogOpen } from "../../shared/nano-states";
|
||||
import { validateProjectDomain, type Project } from "@webstudio-is/project";
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
CheckCircleIcon,
|
||||
ExternalLinkIcon,
|
||||
AlertIcon,
|
||||
CopyIcon,
|
||||
} from "@webstudio-is/icons";
|
||||
import { createTrpcFetchProxy } from "~/shared/remix/trpc-remix-proxy";
|
||||
import { builderDomainsPath } from "~/shared/router-utils";
|
||||
@@ -338,7 +340,10 @@ const ErrorText = ({ children }: { children: string }) => (
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const Content = (props: { projectId: Project["id"] }) => {
|
||||
const Content = (props: {
|
||||
projectId: Project["id"];
|
||||
onExportClick: () => void;
|
||||
}) => {
|
||||
const [newDomains, setNewDomains] = useState(new Set<string>());
|
||||
const {
|
||||
data: domainsResult,
|
||||
@@ -446,6 +451,7 @@ const Content = (props: { projectId: Project["id"] }) => {
|
||||
});
|
||||
}}
|
||||
isPublishing={isPublishing}
|
||||
onExportClick={props.onExportClick}
|
||||
/>
|
||||
|
||||
{projectData?.success === true ? (
|
||||
@@ -466,12 +472,129 @@ const Content = (props: { projectId: Project["id"] }) => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @todo change colors on theme colors when tokens will be ready
|
||||
* https://discord.com/channels/955905230107738152/1149380442315825212/1149408306671128666
|
||||
**/
|
||||
const StyledLink = styled("a", {
|
||||
color: "#006ADC", // @todo theme.colors.foregroundLink,
|
||||
"&:visited": {
|
||||
color: "#793AAF", // @todo theme.colors.foregroundLinkVisited,
|
||||
},
|
||||
});
|
||||
|
||||
const ExportContent = () => {
|
||||
const npxCommand = "npx @webstudio-is/cli";
|
||||
const npxVercelCommand = "npx vercel";
|
||||
return (
|
||||
<Grid
|
||||
columns={1}
|
||||
gap={3}
|
||||
css={{
|
||||
margin: theme.spacing[9],
|
||||
marginTop: theme.spacing[5],
|
||||
}}
|
||||
>
|
||||
<Grid columns={1} gap={1}>
|
||||
<Text color="main" variant="labelsTitleCase">
|
||||
Step 1
|
||||
</Text>
|
||||
<Text color="subtle">
|
||||
Download and install node v18+ from{" "}
|
||||
<StyledLink
|
||||
href="https://nodejs.org/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
nodejs.org
|
||||
</StyledLink>{" "}
|
||||
or with{" "}
|
||||
<StyledLink
|
||||
href="https://nodejs.org/en/download/package-manager"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
a package manager
|
||||
</StyledLink>
|
||||
.
|
||||
</Text>
|
||||
</Grid>
|
||||
|
||||
<Grid columns={1} gap={2}>
|
||||
<Grid columns={1} gap={1}>
|
||||
<Text color="main" variant="labelsTitleCase">
|
||||
Step 2
|
||||
</Text>
|
||||
<Text color="subtle">
|
||||
Run this command in your Terminal to install Webstudio CLI and sync
|
||||
your project.
|
||||
</Text>
|
||||
</Grid>
|
||||
|
||||
<Flex gap={2}>
|
||||
<InputField css={{ flex: 1 }} readOnly value={npxCommand} />
|
||||
|
||||
<Tooltip content={"Copy to clipboard"}>
|
||||
<Button
|
||||
color="neutral"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(npxCommand);
|
||||
}}
|
||||
prefix={<CopyIcon />}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<Grid columns={1} gap={2}>
|
||||
<Grid columns={1} gap={1}>
|
||||
<Text color="main" variant="labelsTitleCase">
|
||||
Step 3
|
||||
</Text>
|
||||
<Text color="subtle">
|
||||
Use the{" "}
|
||||
<StyledLink
|
||||
href="https://vercel.com/docs/cli"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Vercel CLI
|
||||
</StyledLink>{" "}
|
||||
to publish your project on Vercel.
|
||||
</Text>
|
||||
</Grid>
|
||||
|
||||
<Flex gap={2}>
|
||||
<InputField css={{ flex: 1 }} readOnly value={npxVercelCommand} />
|
||||
|
||||
<Tooltip content={"Copy to clipboard"}>
|
||||
<Button
|
||||
color="neutral"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(npxVercelCommand);
|
||||
}}
|
||||
prefix={<CopyIcon />}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
type PublishProps = {
|
||||
projectId: Project["id"];
|
||||
};
|
||||
|
||||
export const PublishButton = ({ projectId }: PublishProps) => {
|
||||
const [isOpen, setIsOpen] = useIsPublishDialogOpen();
|
||||
const [authPermit] = useAuthPermit();
|
||||
const [dialogContentType, setDialogContentType] = useState<
|
||||
"publish" | "export"
|
||||
>("publish");
|
||||
|
||||
const isPublishEnabled = authPermit === "own" || authPermit === "admin";
|
||||
|
||||
@@ -479,8 +602,19 @@ export const PublishButton = ({ projectId }: PublishProps) => {
|
||||
? undefined
|
||||
: "Only owner or admin can publish projects";
|
||||
|
||||
const handleExportClick = () => {
|
||||
setDialogContentType("export");
|
||||
};
|
||||
|
||||
const handleOpenChange = (isOpen: boolean) => {
|
||||
if (isOpen === false) {
|
||||
setDialogContentType("publish");
|
||||
}
|
||||
setIsOpen(isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<FloatingPanelPopover modal open={isOpen} onOpenChange={setIsOpen}>
|
||||
<FloatingPanelPopover modal open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<FloatingPanelAnchor>
|
||||
<Tooltip side="bottom" content={tooltipContent}>
|
||||
<FloatingPanelPopoverTrigger asChild>
|
||||
@@ -500,8 +634,19 @@ export const PublishButton = ({ projectId }: PublishProps) => {
|
||||
marginRight: theme.spacing[3],
|
||||
}}
|
||||
>
|
||||
<FloatingPanelPopoverTitle>Publish</FloatingPanelPopoverTitle>
|
||||
<Content projectId={projectId} />
|
||||
{dialogContentType === "export" && (
|
||||
<>
|
||||
<FloatingPanelPopoverTitle>Export</FloatingPanelPopoverTitle>
|
||||
<ExportContent />
|
||||
</>
|
||||
)}
|
||||
|
||||
{dialogContentType === "publish" && (
|
||||
<>
|
||||
<FloatingPanelPopoverTitle>Publish</FloatingPanelPopoverTitle>
|
||||
<Content projectId={projectId} onExportClick={handleExportClick} />
|
||||
</>
|
||||
)}
|
||||
</FloatingPanelPopoverContent>
|
||||
</FloatingPanelPopover>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.67307 1.35013H11.3269C11.8641 1.35012 12.3064 1.35011 12.6665 1.37954C13.0402 1.41007 13.3823 1.4755 13.7031 1.63896C14.2017 1.89303 14.6071 2.29842 14.8612 2.79705C15.0246 3.11787 15.0901 3.4599 15.1206 3.8336C15.15 4.19371 15.15 4.63604 15.15 5.17319V10.8271C15.15 11.3642 15.15 11.8065 15.1206 12.1667C15.0901 12.5404 15.0246 12.8824 14.8612 13.2032C14.6071 13.7018 14.2017 14.1072 13.7031 14.3613C13.3823 14.5248 13.0402 14.5902 12.6665 14.6207C12.3064 14.6501 11.8641 14.6501 11.3269 14.6501H5.67307C5.13591 14.6501 4.69359 14.6501 4.33348 14.6207C3.95978 14.5902 3.61775 14.5248 3.29693 14.3613C2.7983 14.1072 2.3929 13.7018 2.13884 13.2032C1.97538 12.8824 1.90995 12.5404 1.87941 12.1667C1.84999 11.8065 1.85 11.3642 1.85001 10.8271V5.17319C1.85 4.63603 1.84999 4.19371 1.87941 3.8336C1.90995 3.4599 1.97538 3.11787 2.13884 2.79705C2.3929 2.29842 2.7983 1.89303 3.29693 1.63896C3.61775 1.4755 3.95978 1.41007 4.33348 1.37954C4.69359 1.35011 5.13591 1.35012 5.67307 1.35013ZM4.43934 2.67522C4.14603 2.69918 3.99413 2.74275 3.88712 2.79727C3.6331 2.9267 3.42658 3.13322 3.29715 3.38724C3.24262 3.49425 3.19906 3.64616 3.1751 3.93946C3.15051 4.24036 3.15001 4.62935 3.15001 5.20013V10.8001C3.15001 11.3709 3.15051 11.7599 3.1751 12.0608C3.19906 12.3541 3.24262 12.506 3.29715 12.613C3.42658 12.867 3.6331 13.0736 3.88712 13.203C3.99413 13.2575 4.14603 13.3011 4.43934 13.325C4.74024 13.3496 5.12923 13.3501 5.70001 13.3501H11.3C11.8708 13.3501 12.2598 13.3496 12.5607 13.325C12.854 13.3011 13.0059 13.2575 13.1129 13.203C13.3669 13.0736 13.5734 12.867 13.7029 12.613C13.7574 12.506 13.801 12.3541 13.8249 12.0608C13.8495 11.7599 13.85 11.3709 13.85 10.8001V5.20013C13.85 4.62935 13.8495 4.24036 13.8249 3.93946C13.801 3.64616 13.7574 3.49425 13.7029 3.38724C13.5734 3.13322 13.3669 2.9267 13.1129 2.79727C13.0059 2.74275 12.854 2.69918 12.5607 2.67522C12.2598 2.65063 11.8708 2.65013 11.3 2.65013H5.70001C5.12923 2.65013 4.74024 2.65063 4.43934 2.67522Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.51676 10.3301C8.51676 9.97114 8.80777 9.68013 9.16676 9.68013H11.8334C12.1924 9.68013 12.4834 9.97114 12.4834 10.3301C12.4834 10.6891 12.1924 10.9801 11.8334 10.9801H9.16676C8.80777 10.9801 8.51676 10.6891 8.51676 10.3301Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.70714 5.21051C4.96098 4.95667 5.37253 4.95667 5.62637 5.21051L7.62637 7.21051C7.74827 7.33241 7.81676 7.49774 7.81675 7.67013C7.81675 7.84252 7.74827 8.00785 7.62637 8.12975L5.62637 10.1297C5.37253 10.3836 4.96098 10.3836 4.70713 10.1297C4.45329 9.87591 4.45329 9.46435 4.70714 9.21051L6.24752 7.67013L4.70714 6.12975C4.45329 5.87591 4.45329 5.46435 4.70714 5.21051Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
+15
@@ -0,0 +1,15 @@
|
||||
// Generated from icons/custom-code.svg
|
||||
|
||||
import { forwardRef } from "react";
|
||||
import type { IconProps } from "../types";
|
||||
|
||||
// prettier-ignore
|
||||
export const CustomCodeIcon = forwardRef<SVGSVGElement, IconProps>(
|
||||
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path fillRule="evenodd" d="M5.673 1.35h5.654c.537 0 .98 0 1.34.03.373.03.715.096 1.036.259.499.254.904.66 1.158 1.158.164.32.23.663.26 1.037.029.36.029.802.029 1.34v5.653c0 .537 0 .98-.03 1.34-.03.373-.095.715-.259 1.036a2.65 2.65 0 0 1-1.158 1.158c-.32.164-.663.23-1.037.26-.36.03-.802.03-1.34.03H5.674c-.537 0-.98 0-1.34-.03-.373-.03-.715-.096-1.036-.26a2.65 2.65 0 0 1-1.158-1.158c-.164-.32-.229-.663-.26-1.036-.029-.36-.029-.803-.029-1.34V5.173c0-.537 0-.98.03-1.34.03-.373.095-.715.259-1.036A2.65 2.65 0 0 1 3.297 1.64c.32-.163.663-.229 1.036-.26.36-.029.803-.029 1.34-.029ZM4.44 2.675c-.293.024-.445.068-.552.122a1.35 1.35 0 0 0-.59.59c-.054.107-.098.26-.122.552-.024.301-.025.69-.025 1.261v5.6c0 .57 0 .96.025 1.26.024.294.068.446.122.553.13.254.336.46.59.59.107.055.259.098.552.122.301.025.69.025 1.261.025h5.6c.57 0 .96 0 1.26-.025.294-.024.446-.067.553-.122.254-.13.46-.336.59-.59.054-.107.098-.259.122-.552.025-.301.025-.69.025-1.26V5.2c0-.572 0-.96-.025-1.262-.024-.293-.068-.445-.122-.552a1.35 1.35 0 0 0-.59-.59c-.107-.054-.259-.098-.552-.122-.301-.024-.69-.025-1.261-.025H5.7c-.57 0-.96 0-1.26.025Z" clipRule="evenodd" /><path fillRule="evenodd" d="M8.517 10.33a.65.65 0 0 1 .65-.65h2.666a.65.65 0 1 1 0 1.3H9.167a.65.65 0 0 1-.65-.65ZM4.707 5.21a.65.65 0 0 1 .92 0l2 2a.65.65 0 0 1 0 .92l-2 2a.65.65 0 1 1-.92-.92l1.54-1.54-1.54-1.54a.65.65 0 0 1 0-.92Z" clipRule="evenodd" /></svg>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
CustomCodeIcon.displayName = "CustomCodeIcon";
|
||||
+1
@@ -64,6 +64,7 @@ export * from "./collapsible";
|
||||
export * from "./commit";
|
||||
export * from "./content";
|
||||
export * from "./copy";
|
||||
export * from "./custom-code";
|
||||
export * from "./dash-border";
|
||||
export * from "./dash";
|
||||
export * from "./dashed-border";
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const CustomCodeIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16" fill="currentColor" width="100%" height="100%" style="display: block;"><path fill-rule="evenodd" d="M5.673 1.35h5.654c.537 0 .98 0 1.34.03.373.03.715.096 1.036.259.499.254.904.66 1.158 1.158.164.32.23.663.26 1.037.029.36.029.802.029 1.34v5.653c0 .537 0 .98-.03 1.34-.03.373-.095.715-.259 1.036a2.65 2.65 0 0 1-1.158 1.158c-.32.164-.663.23-1.037.26-.36.03-.802.03-1.34.03H5.674c-.537 0-.98 0-1.34-.03-.373-.03-.715-.096-1.036-.26a2.65 2.65 0 0 1-1.158-1.158c-.164-.32-.229-.663-.26-1.036-.029-.36-.029-.803-.029-1.34V5.173c0-.537 0-.98.03-1.34.03-.373.095-.715.259-1.036A2.65 2.65 0 0 1 3.297 1.64c.32-.163.663-.229 1.036-.26.36-.029.803-.029 1.34-.029ZM4.44 2.675c-.293.024-.445.068-.552.122a1.35 1.35 0 0 0-.59.59c-.054.107-.098.26-.122.552-.024.301-.025.69-.025 1.261v5.6c0 .57 0 .96.025 1.26.024.294.068.446.122.553.13.254.336.46.59.59.107.055.259.098.552.122.301.025.69.025 1.261.025h5.6c.57 0 .96 0 1.26-.025.294-.024.446-.067.553-.122.254-.13.46-.336.59-.59.054-.107.098-.259.122-.552.025-.301.025-.69.025-1.26V5.2c0-.572 0-.96-.025-1.262-.024-.293-.068-.445-.122-.552a1.35 1.35 0 0 0-.59-.59c-.107-.054-.259-.098-.552-.122-.301-.024-.69-.025-1.261-.025H5.7c-.57 0-.96 0-1.26.025Z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M8.517 10.33a.65.65 0 0 1 .65-.65h2.666a.65.65 0 1 1 0 1.3H9.167a.65.65 0 0 1-.65-.65ZM4.707 5.21a.65.65 0 0 1 .92 0l2 2a.65.65 0 0 1 0 .92l-2 2a.65.65 0 1 1-.92-.92l1.54-1.54-1.54-1.54a.65.65 0 0 1 0-.92Z" clip-rule="evenodd"/></svg>`;
|
||||
+1
@@ -64,6 +64,7 @@ export { CollapsibleIcon } from "./collapsible";
|
||||
export { CommitIcon } from "./commit";
|
||||
export { ContentIcon } from "./content";
|
||||
export { CopyIcon } from "./copy";
|
||||
export { CustomCodeIcon } from "./custom-code";
|
||||
export { DashBorderIcon } from "./dash-border";
|
||||
export { DashIcon } from "./dash";
|
||||
export { DashedBorderIcon } from "./dashed-border";
|
||||
|
||||
Reference in new issue
Block a user