alert dialog component
This commit is contained in:
72
components/AlertDialog/index.tsx
Normal file
72
components/AlertDialog/index.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { FC, ReactNode } from "react";
|
||||
import { proxy, useSnapshot } from "valtio";
|
||||
import Button from "../Button";
|
||||
import Flex from "../Flex";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogTitle,
|
||||
} from "./primitive";
|
||||
|
||||
export interface AlertState {
|
||||
isOpen: boolean;
|
||||
title?: string;
|
||||
body?: ReactNode;
|
||||
cancelText?: string;
|
||||
confirmNode?: ReactNode;
|
||||
onConfirm?: () => any;
|
||||
onCancel?: () => any;
|
||||
}
|
||||
|
||||
export const alertState = proxy<AlertState>({
|
||||
isOpen: false,
|
||||
});
|
||||
|
||||
const Alert: FC = () => {
|
||||
const {
|
||||
title = "Are you sure?",
|
||||
isOpen,
|
||||
body,
|
||||
cancelText,
|
||||
confirmNode = "Ok",
|
||||
onCancel,
|
||||
onConfirm,
|
||||
} = useSnapshot(alertState);
|
||||
return (
|
||||
<AlertDialog
|
||||
open={isOpen}
|
||||
onOpenChange={value => (alertState.isOpen = value)}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogTitle>{title}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{body}</AlertDialogDescription>
|
||||
<Flex css={{ justifyContent: "flex-end", gap: "$3" }}>
|
||||
{cancelText && (
|
||||
<AlertDialogCancel asChild>
|
||||
<Button css={{ minWidth: "$16" }} outline onClick={onCancel}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
</AlertDialogCancel>
|
||||
)}
|
||||
<AlertDialogAction asChild>
|
||||
<Button
|
||||
css={{ minWidth: "$16" }}
|
||||
variant="primary"
|
||||
onClick={async () => {
|
||||
await onConfirm?.();
|
||||
alertState.isOpen = false;
|
||||
}}
|
||||
>
|
||||
{confirmNode}
|
||||
</Button>
|
||||
</AlertDialogAction>
|
||||
</Flex>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default Alert;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { blackA } from "@radix-ui/colors";
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
||||
import { styled, keyframes } from "../stitches.config";
|
||||
import { styled, keyframes } from "../../stitches.config";
|
||||
|
||||
const overlayShow = keyframes({
|
||||
"0%": { opacity: 0 },
|
||||
@@ -50,15 +50,8 @@ import Stack from "./Stack";
|
||||
import { Input, Label } from "./Input";
|
||||
import Text from "./Text";
|
||||
import Tooltip from "./Tooltip";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogContent,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogCancel,
|
||||
AlertDialogAction,
|
||||
} from "./AlertDialog";
|
||||
import { styled } from "../stitches.config";
|
||||
import { showAlert } from "../state/actions/showAlert";
|
||||
|
||||
const ErrorText = styled(Text, {
|
||||
color: "$error",
|
||||
@@ -68,7 +61,6 @@ const ErrorText = styled(Text, {
|
||||
|
||||
const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
const snap = useSnapshot(state);
|
||||
const [createNewAlertOpen, setCreateNewAlertOpen] = useState(false);
|
||||
const [editorSettingsOpen, setEditorSettingsOpen] = useState(false);
|
||||
const [isNewfileDialogOpen, setIsNewfileDialogOpen] = useState(false);
|
||||
const [newfileError, setNewfileError] = useState<string | null>(null);
|
||||
@@ -87,13 +79,32 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
setNewfileError(null);
|
||||
}, [filename, setNewfileError]);
|
||||
|
||||
const showNewGistAlert = () => {
|
||||
showAlert("Are you sure?", {
|
||||
body: (
|
||||
<>
|
||||
This action will create new <strong>public</strong> Github Gist from
|
||||
your current saved files. You can delete gist anytime from your GitHub
|
||||
Gists page.
|
||||
</>
|
||||
),
|
||||
cancelText: "Cancel",
|
||||
confirmNode: (
|
||||
<>
|
||||
<FilePlus size="15px" /> Create new Gist
|
||||
</>
|
||||
),
|
||||
onConfirm: () => syncToGist(session, true),
|
||||
});
|
||||
};
|
||||
|
||||
const validateFilename = useCallback(
|
||||
(filename: string): { error: string | null } => {
|
||||
// check if filename already exists
|
||||
if (!filename) {
|
||||
return { error: "You need to add filename" };
|
||||
}
|
||||
if (snap.files.find((file) => file.name === filename)) {
|
||||
if (snap.files.find(file => file.name === filename)) {
|
||||
return { error: "Filename already exists." };
|
||||
}
|
||||
|
||||
@@ -225,8 +236,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
<Label>Filename</Label>
|
||||
<Input
|
||||
value={filename}
|
||||
onChange={(e) => setFilename(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
onChange={e => setFilename(e.target.value)}
|
||||
onKeyPress={e => {
|
||||
if (e.key === "Enter") {
|
||||
handleConfirm();
|
||||
}
|
||||
@@ -416,7 +427,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
if (snap.gistOwner === session?.user.username) {
|
||||
syncToGist(session);
|
||||
} else {
|
||||
setCreateNewAlertOpen(true);
|
||||
showNewGistAlert();
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -466,7 +477,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
<DropdownMenuItem
|
||||
disabled={status !== "authenticated"}
|
||||
onClick={() => {
|
||||
setCreateNewAlertOpen(true);
|
||||
showNewGistAlert();
|
||||
}}
|
||||
>
|
||||
<FilePlus size="16px" /> Create as a new Gist
|
||||
@@ -486,34 +497,6 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
) : null}
|
||||
</Container>
|
||||
</Flex>
|
||||
<AlertDialog
|
||||
open={createNewAlertOpen}
|
||||
onOpenChange={(value) => setCreateNewAlertOpen(value)}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action will create new <strong>public</strong> Github Gist from
|
||||
your current saved files. You can delete gist anytime from your
|
||||
GitHub Gists page.
|
||||
</AlertDialogDescription>
|
||||
<Flex css={{ justifyContent: "flex-end", gap: "$3" }}>
|
||||
<AlertDialogCancel asChild>
|
||||
<Button outline>Cancel</Button>
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction asChild>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
syncToGist(session, true);
|
||||
}}
|
||||
>
|
||||
<FilePlus size="15px" /> Create new Gist
|
||||
</Button>
|
||||
</AlertDialogAction>
|
||||
</Flex>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<Dialog open={editorSettingsOpen} onOpenChange={setEditorSettingsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
@@ -529,8 +512,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
||||
type="number"
|
||||
min="1"
|
||||
value={editorSettings.tabSize}
|
||||
onChange={(e) =>
|
||||
setEditorSettings((curr) => ({
|
||||
onChange={e =>
|
||||
setEditorSettings(curr => ({
|
||||
...curr,
|
||||
tabSize: Number(e.target.value),
|
||||
}))
|
||||
|
||||
@@ -7,7 +7,7 @@ export { default as Text } from "./Text";
|
||||
export { default as Input, Label } from "./Input";
|
||||
export { default as Select } from "./Select";
|
||||
export * from "./Tabs";
|
||||
export * from "./AlertDialog";
|
||||
export * from "./AlertDialog/primitive";
|
||||
export { default as Box } from "./Box";
|
||||
export { default as Button } from "./Button";
|
||||
export { default as Pre } from "./Pre";
|
||||
|
||||
@@ -16,6 +16,7 @@ import state from "../state";
|
||||
import TimeAgo from "javascript-time-ago";
|
||||
import en from "javascript-time-ago/locale/en.json";
|
||||
import { useSnapshot } from "valtio";
|
||||
import Alert from '../components/AlertDialog';
|
||||
|
||||
TimeAgo.setDefaultLocale(en.locale);
|
||||
TimeAgo.addLocale(en);
|
||||
@@ -140,6 +141,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
||||
})(),
|
||||
}}
|
||||
/>
|
||||
<Alert />
|
||||
</ThemeProvider>
|
||||
</SessionProvider>
|
||||
</IdProvider>
|
||||
|
||||
@@ -6,6 +6,8 @@ import Transaction from "../../components/Transaction";
|
||||
import state from "../../state";
|
||||
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
|
||||
import { transactionsState, modifyTransaction } from "../../state";
|
||||
import { useEffect } from 'react';
|
||||
import { showAlert } from '../../state/actions/showAlert';
|
||||
|
||||
const DebugStream = dynamic(() => import("../../components/DebugStream"), {
|
||||
ssr: false,
|
||||
|
||||
19
state/actions/showAlert.ts
Normal file
19
state/actions/showAlert.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ref } from 'valtio';
|
||||
import { AlertState, alertState } from "../../components/AlertDialog";
|
||||
|
||||
export const showAlert = (title: string, opts: Partial<AlertState> = {}) => {
|
||||
const { body: _body, confirmNode: _confirmNode, ...rest } = opts
|
||||
const body = (_body && typeof _body === 'object') ? ref(_body) : _body
|
||||
const confirmNode = (_confirmNode && typeof _confirmNode === 'object') ? ref(_confirmNode) : _confirmNode
|
||||
|
||||
const nwState = {
|
||||
isOpen: true,
|
||||
title,
|
||||
body,
|
||||
confirmNode,
|
||||
...rest
|
||||
}
|
||||
Object.entries(nwState).forEach(([key, value]) => {
|
||||
(alertState as any)[key] = value
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user