Skip to content

Instantly share code, notes, and snippets.

@28development
Created June 11, 2022 22:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 28development/4e651e53cd0af432ef2ebd4850629612 to your computer and use it in GitHub Desktop.
Save 28development/4e651e53cd0af432ef2ebd4850629612 to your computer and use it in GitHub Desktop.
import { gql, useMutation } from '@apollo/client';
import dynamic from 'next/dynamic';
import { array, object, string } from 'zod';
import { Form, useZodForm } from '../ui/Form';
import Modal from '../ui/Modal';
import TagSelection from './TagSelection';
import TechSelection from './TechSelection';
interface Props {
open: boolean;
setOpen: (open: boolean) => void;
}
const newSnippetSchema = object({
title: string().min(1),
description: string().min(1),
tags: array(string()),
technologies: array(string()),
content: string().min(1),
});
const Editor = dynamic(() => import('~/components/Editor'), { ssr: false });
export default function CreateSnippetModal(props: Props) {
const { open, setOpen } = props;
const [createSnippet] = useMutation(
gql`
mutation CreateSnippetMutation($input: CreateSnippetInput!) {
createSnippet(input: $input) {
id
title
description
content
tags {
id
name
}
technologies {
id
name
}
}
}
`,
);
const form = useZodForm({
schema: newSnippetSchema,
});
return (
<Form
form={form}
onSubmit={({ title, description, tags, techs, content }) =>
createSnippet({
variables: {
input: {
title,
description,
tags,
techs,
content,
},
},
})
}
>
<Modal
title="Create Snippet"
description="Create a new snippet"
open={open}
setOpen={setOpen}
size="xl"
actions={
<>
<button
type="submit"
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-cyan-600 text-base font-medium text-white hover:bg-cyan-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500 sm:ml-3 sm:w-auto sm:text-sm"
>
Create
</button>
<button
type="button"
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500 sm:mt-0 sm:w-auto sm:text-sm"
onClick={() => setOpen(false)}
>
Cancel
</button>
</>
}
>
<>
<div className="flex">
<div className="flex-1 w-full sm:w-1/2">
<TechSelection {...form.register('technologies')} />
</div>
<div className="flex-1 w-full sm:w-1/2">
<TagSelection {...form.register('tags')} />
</div>
</div>
<div className="py-2">
<Editor {...form.register('content')} />
</div>
</>
</Modal>
</Form>
);
}
import { Fragment, useRef } from 'react';
import { Dialog, Transition } from '@headlessui/react';
import clsx from 'clsx';
interface Props {
open: boolean;
setOpen: (open: boolean) => void;
title: string;
description: string;
icon?: JSX.Element;
children?: JSX.Element;
actions?: JSX.Element;
size: 'sm' | 'md' | 'lg' | 'xl';
}
export default function Modal(props: Props) {
const { open, setOpen, title, description, icon, children, actions, size } =
props;
const cancelButtonRef = useRef(null);
return (
<Transition.Root show={open} as={Fragment}>
<Dialog
as="div"
className="fixed z-10 inset-0 overflow-y-auto"
initialFocus={cancelButtonRef}
onClose={setOpen}
>
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>
{/* This element is to trick the browser into centering the modal contents. */}
<span
className="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true"
>
&#8203;
</span>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<div
className={clsx(
size === 'sm' && 'md:w-64',
size === 'md' && 'md:w-1/2',
size === 'lg' && 'md:w-6/12',
size === 'xl' && 'md:w-10/12',
'w-11/12 shadow-none backdrop-blur-md bg-white/80 relative inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden transform transition-all sm:my-8 sm:align-middle sm:p-6',
)}
>
<div className="sm:flex sm:items-start">
{icon && (
<div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-cyan-100 sm:mx-0 sm:h-10 sm:w-10">
{icon}
</div>
)}
<div className="mt-3 text-center sm:mt-0 sm:text-left w-full">
<Dialog.Title
as="h3"
className="text-lg leading-6 font-medium text-gray-900"
>
{title}
</Dialog.Title>
<div className="mt-2 flex flex-col">
<p className="text-sm text-gray-500">{description}</p>
{children}
</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
{actions}
</div>
</div>
</Transition.Child>
</div>
</Dialog>
</Transition.Root>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment