diff options
Diffstat (limited to 'packages/excalidraw/components/OverwriteConfirm')
4 files changed, 330 insertions, 0 deletions
diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss new file mode 100644 index 0000000..4aad0cb --- /dev/null +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss @@ -0,0 +1,126 @@ +@import "../../css/variables.module.scss"; + +.excalidraw { + .OverwriteConfirm { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + isolation: isolate; + + h3 { + margin: 0; + + font-weight: 700; + font-size: 1.3125rem; + line-height: 130%; + align-self: flex-start; + + color: var(--text-primary-color); + } + + &__Description { + box-sizing: border-box; + + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + gap: 1rem; + + @include isMobile { + flex-direction: column; + text-align: center; + } + + padding: 2.5rem; + + background: var(--color-danger-background); + border-radius: 0.5rem; + + font-family: "Assistant"; + font-style: normal; + font-weight: 400; + font-size: 1rem; + line-height: 150%; + + color: var(--color-danger-color); + + &__spacer { + flex-grow: 1; + } + + &__icon { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + border-radius: 2.5rem; + background: var(--color-danger-icon-background); + width: 3.5rem; + height: 3.5rem; + + padding: 0.75rem; + + svg { + color: var(--color-danger-icon-color); + width: 1.5rem; + height: 1.5rem; + } + } + + &.OverwriteConfirm__Description--color-warning { + background: var(--color-warning-background); + color: var(--color-warning-color); + + .OverwriteConfirm__Description__icon { + background: var(--color-warning-icon-background); + flex: 0 0 auto; + + svg { + color: var(--color-warning-icon-color); + } + } + } + } + + &__Actions { + display: flex; + flex-direction: row; + align-items: stretch; + justify-items: stretch; + justify-content: center; + gap: 1.5rem; + + @include isMobile { + flex-direction: column; + } + + &__Action { + display: flex; + flex-direction: column; + align-items: center; + padding: 1.5rem; + gap: 0.75rem; + flex-basis: 50%; + flex-grow: 0; + + &__content { + height: 100%; + font-size: 0.875rem; + text-align: center; + } + + h4 { + font-weight: 700; + font-size: 1.125rem; + line-height: 130%; + + margin: 0; + + color: var(--text-primary-color); + } + } + } + } +} diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx new file mode 100644 index 0000000..4bf8d67 --- /dev/null +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx @@ -0,0 +1,74 @@ +import React from "react"; + +import { useTunnels } from "../../context/tunnels"; +import { useAtom } from "../../editor-jotai"; +import { Dialog } from "../Dialog"; +import { withInternalFallback } from "../hoc/withInternalFallback"; +import { overwriteConfirmStateAtom } from "./OverwriteConfirmState"; + +import { FilledButton } from "../FilledButton"; +import { alertTriangleIcon } from "../icons"; +import { Actions, Action } from "./OverwriteConfirmActions"; +import "./OverwriteConfirm.scss"; + +export type OverwriteConfirmDialogProps = { + children: React.ReactNode; +}; + +const OverwriteConfirmDialog = Object.assign( + withInternalFallback( + "OverwriteConfirmDialog", + ({ children }: OverwriteConfirmDialogProps) => { + const { OverwriteConfirmDialogTunnel } = useTunnels(); + const [overwriteConfirmState, setState] = useAtom( + overwriteConfirmStateAtom, + ); + + if (!overwriteConfirmState.active) { + return null; + } + + const handleClose = () => { + overwriteConfirmState.onClose(); + setState((state) => ({ ...state, active: false })); + }; + + const handleConfirm = () => { + overwriteConfirmState.onConfirm(); + setState((state) => ({ ...state, active: false })); + }; + + return ( + <OverwriteConfirmDialogTunnel.In> + <Dialog onCloseRequest={handleClose} title={false} size={916}> + <div className="OverwriteConfirm"> + <h3>{overwriteConfirmState.title}</h3> + <div + className={`OverwriteConfirm__Description OverwriteConfirm__Description--color-${overwriteConfirmState.color}`} + > + <div className="OverwriteConfirm__Description__icon"> + {alertTriangleIcon} + </div> + <div>{overwriteConfirmState.description}</div> + <div className="OverwriteConfirm__Description__spacer"></div> + <FilledButton + color={overwriteConfirmState.color} + size="large" + label={overwriteConfirmState.actionLabel} + onClick={handleConfirm} + /> + </div> + <Actions>{children}</Actions> + </div> + </Dialog> + </OverwriteConfirmDialogTunnel.In> + ); + }, + ), + { + Actions, + Action, + }, +); + +export { OverwriteConfirmDialog }; diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx new file mode 100644 index 0000000..5da0a08 --- /dev/null +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import { FilledButton } from "../FilledButton"; +import { useExcalidrawActionManager, useExcalidrawSetAppState } from "../App"; +import { actionSaveFileToDisk } from "../../actions"; +import { useI18n } from "../../i18n"; +import { actionChangeExportEmbedScene } from "../../actions/actionExport"; + +export type ActionProps = { + title: string; + children: React.ReactNode; + actionLabel: string; + onClick: () => void; +}; + +export const Action = ({ + title, + children, + actionLabel, + onClick, +}: ActionProps) => { + return ( + <div className="OverwriteConfirm__Actions__Action"> + <h4>{title}</h4> + <div className="OverwriteConfirm__Actions__Action__content"> + {children} + </div> + <FilledButton + variant="outlined" + color="muted" + label={actionLabel} + size="large" + fullWidth + onClick={onClick} + /> + </div> + ); +}; + +export const ExportToImage = () => { + const { t } = useI18n(); + const actionManager = useExcalidrawActionManager(); + const setAppState = useExcalidrawSetAppState(); + + return ( + <Action + title={t("overwriteConfirm.action.exportToImage.title")} + actionLabel={t("overwriteConfirm.action.exportToImage.button")} + onClick={() => { + actionManager.executeAction(actionChangeExportEmbedScene, "ui", true); + setAppState({ openDialog: { name: "imageExport" } }); + }} + > + {t("overwriteConfirm.action.exportToImage.description")} + </Action> + ); +}; + +export const SaveToDisk = () => { + const { t } = useI18n(); + const actionManager = useExcalidrawActionManager(); + + return ( + <Action + title={t("overwriteConfirm.action.saveToDisk.title")} + actionLabel={t("overwriteConfirm.action.saveToDisk.button")} + onClick={() => { + actionManager.executeAction(actionSaveFileToDisk, "ui"); + }} + > + {t("overwriteConfirm.action.saveToDisk.description")} + </Action> + ); +}; + +const Actions = Object.assign( + ({ children }: { children: React.ReactNode }) => { + return <div className="OverwriteConfirm__Actions">{children}</div>; + }, + { + ExportToImage, + SaveToDisk, + }, +); + +export { Actions }; diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts new file mode 100644 index 0000000..04616aa --- /dev/null +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts @@ -0,0 +1,45 @@ +import { atom, editorJotaiStore } from "../../editor-jotai"; +import type React from "react"; + +export type OverwriteConfirmState = + | { + active: true; + title: string; + description: React.ReactNode; + actionLabel: string; + color: "danger" | "warning"; + + onClose: () => void; + onConfirm: () => void; + onReject: () => void; + } + | { active: false }; + +export const overwriteConfirmStateAtom = atom<OverwriteConfirmState>({ + active: false, +}); + +export async function openConfirmModal({ + title, + description, + actionLabel, + color, +}: { + title: string; + description: React.ReactNode; + actionLabel: string; + color: "danger" | "warning"; +}) { + return new Promise<boolean>((resolve) => { + editorJotaiStore.set(overwriteConfirmStateAtom, { + active: true, + onConfirm: () => resolve(true), + onClose: () => resolve(false), + onReject: () => resolve(false), + title, + description, + actionLabel, + color, + }); + }); +} |
