Instantly share code, notes, and snippets.
Last active
July 5, 2024 15:07
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save gmotzespina/08eb4532fc1d324c5e5b3ba5cd45beb0 to your computer and use it in GitHub Desktop.
Share dialog with chips
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import InputWithChips, { UserChip } from "@components/InputWithChips"; | |
| import SimpleDivider from "@components/SimpleDivider"; | |
| import { Transition, Dialog } from "@headlessui/react"; | |
| import { PaperAirplaneIcon } from "@heroicons/react/20/solid"; | |
| import { Button, Typography } from "@material-tailwind/react"; | |
| import React, { Fragment, useEffect, useState } from "react"; | |
| import { Formik } from "formik"; | |
| import { AccessPermissions, AccessType } from "@prisma/client"; | |
| import AccessPermissionsPicker from "@components/AccessPermissionsPicker"; | |
| interface ShareTwineProps { | |
| open: boolean; | |
| accessPermissions?: AccessPermissions; | |
| onBackClicked: () => void; | |
| onSend: (users: string[], accessType?: AccessType) => void; | |
| } | |
| const initializeUsers = (users?: string[]) => { | |
| if (!users) return []; | |
| return users.map((user) => { | |
| return { username: user, img: "" }; | |
| }); | |
| }; | |
| function useUsers(initialUsers?: string[]): [UserChip[], (user: string) => void, (index: number) => void] { | |
| const [users, setUsers] = useState<UserChip[]>(initializeUsers(initialUsers)); | |
| const addUser = (username: string) => { | |
| if (users.some(user => user.username === username)) return; | |
| setUsers([...users, { username, img: "" }]); | |
| }; | |
| const removeUser = (index: number) => { | |
| setUsers(users => users.filter((_, i) => i !== index)); | |
| }; | |
| return [users, addUser, removeUser]; | |
| } | |
| const ShareTwine: React.FC<ShareTwineProps> = (props) => { | |
| const { open, onBackClicked, onSend, accessPermissions } = props; | |
| const [isOpen, setIsOpen] = useState(false); | |
| const [users, addUser, removeUser] = useUsers(accessPermissions?.sharedWith); | |
| const [selectedAccessType, setSelectedAccessType] = useState<AccessType>( | |
| AccessType.PUBLIC | |
| ); | |
| useEffect(() => { | |
| setIsOpen(open); | |
| }, [open]); | |
| return ( | |
| <Transition.Root show={isOpen} as={Fragment}> | |
| <Dialog as="div" onClose={() => null}> | |
| <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" | |
| > | |
| <div className="fixed lg:z-40 inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> | |
| </Transition.Child> | |
| <div className="fixed inset-0 z-50 overflow-y-auto"> | |
| <div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0"> | |
| <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" | |
| > | |
| <Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white dark:bg-blue-gray-900 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6"> | |
| <Dialog.Title | |
| as="h2" | |
| className="text-base font-semibold leading-6 text-gray-900 dark:text-blue-gray-300 mb-2" | |
| > | |
| <div className="flex justify-between gap-2"> | |
| <Typography variant="h5">Share Your Twine</Typography> | |
| </div> | |
| </Dialog.Title> | |
| <Typography | |
| variant="small" | |
| className="dark:text-blue-gray-400 text-sm font-light" | |
| > | |
| Press enter/spacebar/comma to add a user | |
| </Typography> | |
| <Formik | |
| initialValues={{ users, accessPermissions }} | |
| onSubmit={async (values, { setSubmitting }) => { | |
| setSubmitting(true); | |
| }} | |
| > | |
| {() => ( | |
| <> | |
| <div className="mt-4 mb-2 flex flex-col gap-3"> | |
| <div className="flex flex-col"> | |
| <InputWithChips | |
| users={users} | |
| label="Add People" | |
| variant="outlined" | |
| name="users" | |
| onUserRemoved={removeUser} | |
| onUserAdded={addUser} | |
| /> | |
| </div> | |
| <AccessPermissionsPicker | |
| name="accessPermissions" | |
| accessType={accessPermissions?.access} | |
| onPermissionsChange={(accessType: AccessType) => | |
| setSelectedAccessType(accessType) | |
| } | |
| /> | |
| </div> | |
| <SimpleDivider /> | |
| <div className="mt-7 sm:mt- flex justify-between"> | |
| <Button | |
| color="red" | |
| className="dark:bg-red-400" | |
| onClick={onBackClicked} | |
| > | |
| Back | |
| </Button> | |
| <Button | |
| className="flex gap-2 items-center dark:bg-indigo-400" | |
| color="indigo" | |
| disabled={users.length === 0} | |
| onClick={() => { | |
| onSend(users.map(u => u.username.toLowerCase()), selectedAccessType); | |
| setIsOpen(false); | |
| }} | |
| > | |
| Send | |
| <PaperAirplaneIcon className="h-5 w-5 self-center flex-none text-white" /> | |
| </Button> | |
| </div> | |
| </> | |
| )} | |
| </Formik> | |
| </Dialog.Panel> | |
| </Transition.Child> | |
| </div> | |
| </div> | |
| </Dialog> | |
| </Transition.Root> | |
| ); | |
| }; | |
| export default ShareTwine; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment