feat: make the modal and dropdown more workable

This commit is contained in:
Aarnav Tale 2024-03-31 19:06:06 -04:00
parent f1347803a4
commit bdb00b6cd7
No known key found for this signature in database
3 changed files with 93 additions and 33 deletions

View File

@ -7,6 +7,7 @@ type Properties = {
readonly button: ReactNode;
// eslint-disable-next-line unicorn/no-keyword-prefix
readonly className?: string;
readonly width?: string;
}
function Dropdown(properties: Properties) {
@ -26,12 +27,13 @@ function Dropdown(properties: Properties) {
leaveTo='transform opacity-0 scale-95'
>
<Menu.Items className={clsx(
'absolute right-0 w-fit max-w-36 mt-2 rounded-md',
'absolute right-0 mt-2 rounded-md',
'text-gray-700 dark:text-gray-300',
'bg-white dark:bg-zinc-800 text-right',
'bg-white dark:bg-zinc-800',
'overflow-hidden z-50',
'border border-gray-200 dark:border-zinc-700',
'divide-y divide-gray-200 dark:divide-zinc-700'
'divide-y divide-gray-200 dark:divide-zinc-700',
properties.width ?? 'w-36'
)}
>
{properties.children}
@ -66,7 +68,7 @@ function Item(properties: ItemProperties) {
<div
{...properties}
className={clsx(
'px-4 py-2 w-full text-right',
'px-4 py-2 w-full',
'focus:outline-none focus:ring',
'focus:ring-gray-300 dark:focus:ring-zinc-700',
properties.className,

View File

@ -14,36 +14,60 @@ type HookParameters = {
// Optional because the button submits
onConfirm?: () => void | Promise<void>;
onOpen?: () => void | Promise<void>;
onClose?: () => void | Promise<void>;
}
type Overrides = Omit<HookParameters, 'onOpen' | 'onClose'>
type Properties = {
readonly isOpen: boolean;
readonly setIsOpen: (value: SetStateAction<boolean>) => void;
readonly parameters: HookParameters;
readonly parameters?: HookParameters;
}
export default function useModal(properties: HookParameters) {
export default function useModal(properties?: HookParameters) {
const [isOpen, setIsOpen] = useState(false)
const [liveProperties, setLiveProperties] = useState(properties)
return {
Modal: (
<Modal
isOpen={isOpen}
setIsOpen={setIsOpen}
parameters={properties}
parameters={liveProperties}
/>
),
open: () => {
open: (overrides?: Overrides) => {
if (!overrides && !properties) {
throw new Error('No properties provided')
}
setIsOpen(true)
if (properties?.onOpen) {
void properties.onOpen()
}
if (overrides) {
setLiveProperties(overrides)
}
},
close: () => {
setIsOpen(false)
if (properties?.onClose) {
void properties.onClose()
}
}
}
}
function Modal({ parameters, isOpen, setIsOpen }: Properties) {
if (!parameters) {
return
}
return (
<Transition
show={isOpen}

View File

@ -45,29 +45,9 @@ export async function action({ request }: ActionFunctionArgs) {
export default function Page() {
useLiveData({ interval: 3000 })
const data = useLoaderData<typeof loader>()
const [activeId, setActiveId] = useState<string | undefined>(undefined)
const fetcher = useFetcher()
const { Modal, open } = useModal({
title: 'Remove Machine',
description: [
'This action is irreversible and will disconnect the machine from the Headscale server.',
'All data associated with this machine including ACLs and tags will be lost.'
].join('\n'),
variant: 'danger',
buttonText: 'Remove',
onConfirm: () => {
fetcher.submit(
{
id: activeId!
},
{
method: 'DELETE',
encType: 'application/json'
}
)
}
})
const { Modal, open } = useModal()
return (
<>
@ -143,17 +123,71 @@ export default function Page() {
)}
>
<Dropdown
className='left-1/4 w-min'
className='left-1/4 cursor-pointer'
width='w-48'
button={(
<EllipsisHorizontalIcon className='w-5 h-5'/>
)}
>
<Dropdown.Item className='text-red-700'>
<Dropdown.Item>
<button
type='button' onClick={() => {
setActiveId(machine.id)
type='button'
className='text-left'
onClick={() => {
open()
}}
>
Edit machine name
</button>
</Dropdown.Item>
<Dropdown.Item>
<button
type='button'
className='text-left'
onClick={() => {
open()
}}
>
Edit route settings
</button>
</Dropdown.Item>
<Dropdown.Item>
<button
type='button'
className='text-left'
onClick={() => {
open()
}}
>
Edit ACL tags
</button>
</Dropdown.Item>
<Dropdown.Item>
<button
type='button'
className='text-left text-red-700'
onClick={() => {
open({
title: 'Remove Machine',
description: [
'This action is irreversible and will disconnect the machine from the Headscale server.',
'All data associated with this machine including ACLs and tags will be lost.'
].join('\n'),
variant: 'danger',
buttonText: 'Remove',
onConfirm: () => {
fetcher.submit(
{
id: machine.id
},
{
method: 'DELETE',
encType: 'application/json'
}
)
}
})
}}
>
Remove
</button>