Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions tavern/internal/www/build/asset-manifest.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tavern/internal/www/build/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions tavern/internal/www/build/static/css/main.eca4e068.css

This file was deleted.

1 change: 0 additions & 1 deletion tavern/internal/www/build/static/css/main.eca4e068.css.map

This file was deleted.

4 changes: 4 additions & 0 deletions tavern/internal/www/build/static/css/main.f96af0b6.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tavern/internal/www/build/static/css/main.f96af0b6.css.map

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

155 changes: 119 additions & 36 deletions tavern/internal/www/src/components/ButtonDialogPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { ReactElement, useEffect, useRef, useState } from 'react';
import { Dialog } from '@headlessui/react';
import React, { ReactElement, useEffect, useRef, useState, Fragment } from 'react';
import { Dialog, Transition } from '@headlessui/react';
import { XMarkIcon } from '@heroicons/react/24/outline';
import Button from './tavern-base-ui/button/Button';

type Position = {
Expand All @@ -14,36 +15,49 @@ export const ButtonDialogPopover = ({ children, label, leftIcon }: {
}) => {
const [isOpen, setIsOpen] = useState(false)
const [position, setPosition] = useState<Position>({ top: 0, left: 0 })
const [isMobile, setIsMobile] = useState(false)

const buttonRef = useRef<HTMLButtonElement | null>(null);
const panelRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768) // md breakpoint
}

checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])

const openDialog = (): void => {
const dialogWidth = 384;
const spacing = 8;

const button = buttonRef.current
if (button) {
const rect = button.getBoundingClientRect()
const viewportWidth = window.innerWidth

const calculatedLeft = Math.min(
rect.left + window.scrollX,
viewportWidth - dialogWidth - 16 // 16px right margin
)

setPosition({
top: rect.bottom + window.scrollY + spacing,
left: calculatedLeft,
})
if (!isMobile) {
const dialogWidth = 384;
const spacing = 8;

const button = buttonRef.current
if (button) {
const rect = button.getBoundingClientRect()
const viewportWidth = window.innerWidth

const calculatedLeft = Math.min(
rect.left,
viewportWidth - dialogWidth - 16
)

setPosition({
top: rect.bottom + spacing,
left: calculatedLeft,
})
}
}
setIsOpen(true)
}

const closeDialog = (): void => setIsOpen(false)

useEffect(() => {
if (!isOpen) return
if (!isOpen || isMobile) return

const handleClickOutside = (event: MouseEvent): void => {
const target = event.target as Node
Expand All @@ -59,26 +73,95 @@ export const ButtonDialogPopover = ({ children, label, leftIcon }: {

document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [isOpen])
}, [isOpen, isMobile])

return (
<div className='flex justify-end'>
<Button ref={buttonRef} leftIcon={leftIcon} buttonVariant='ghost' buttonStyle={{ color: "gray", size: "md" }} onClick={openDialog}>{label}</Button>
<Dialog open={isOpen} onClose={closeDialog}>
<div>
<div className="fixed inset-0 bg-black/30 z-40" aria-hidden="true" />
<div
ref={panelRef}
className="absolute z-50 bg-white border rounded-lg shadow-lg w-96 p-4 flex flex-col gap-4"
style={{
top: position.top,
left: position.left,
}}
<Button
ref={buttonRef}
leftIcon={leftIcon}
buttonVariant='ghost'
buttonStyle={{ color: "gray", size: "md" }}
onClick={openDialog}
>
{label}
</Button>

<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-50" onClose={closeDialog}>
{/* Backdrop */}
<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"
>
{children}
</div>
</div>
</Dialog>
<div className="fixed inset-0 bg-black/30 z-40" aria-hidden="true" />
</Transition.Child>

{isMobile ? (
/* Mobile: Full-screen modal */
<div className="fixed inset-0 z-50 overflow-y-auto">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="min-h-full bg-white">
{/* Header with close button */}
<div className="sticky top-0 z-10 bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between">
<Dialog.Title className="text-lg font-medium text-gray-900">
{label}
</Dialog.Title>
<button
onClick={closeDialog}
className="text-gray-400 hover:text-gray-500"
>
<XMarkIcon className="h-6 w-6" />
</button>
</div>

{/* Content */}
<div className="px-4 py-4">
{children}
</div>
</Dialog.Panel>
</Transition.Child>
</div>
) : (
/* Desktop: Positioned popover */
<div className="fixed inset-0 z-50 overflow-y-auto">
<Transition.Child
as={Fragment}
enter="ease-out duration-200"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-150"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div
ref={panelRef}
className="fixed bg-white border rounded-lg shadow-lg w-96 p-4 flex flex-col gap-4"
style={{
top: position.top,
left: position.left,
}}
>
{children}
</div>
</Transition.Child>
</div>
)}
</Dialog>
</Transition.Root>
</div>
);
}
128 changes: 0 additions & 128 deletions tavern/internal/www/src/components/tavern-base-ui/Table.tsx

This file was deleted.

Loading
Loading