Skip to content

Commit c20e22d

Browse files
authored
UI improve table (#1519)
* feat(table): Improve table layout * fix(styling): Change rows shown, add tests, fix styling
1 parent 4d773c1 commit c20e22d

34 files changed

+1122
-361
lines changed

tavern/internal/www/build/asset-manifest.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tavern/internal/www/build/index.html

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tavern/internal/www/build/static/css/main.eca4e068.css

Lines changed: 0 additions & 4 deletions
This file was deleted.

tavern/internal/www/build/static/css/main.eca4e068.css.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

tavern/internal/www/build/static/css/main.f96af0b6.css

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tavern/internal/www/build/static/css/main.f96af0b6.css.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tavern/internal/www/build/static/js/main.750dcaba.js renamed to tavern/internal/www/build/static/js/main.93fa5492.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tavern/internal/www/build/static/js/main.750dcaba.js.LICENSE.txt renamed to tavern/internal/www/build/static/js/main.93fa5492.js.LICENSE.txt

File renamed without changes.

tavern/internal/www/build/static/js/main.750dcaba.js.map renamed to tavern/internal/www/build/static/js/main.93fa5492.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 119 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import React, { ReactElement, useEffect, useRef, useState } from 'react';
2-
import { Dialog } from '@headlessui/react';
1+
import React, { ReactElement, useEffect, useRef, useState, Fragment } from 'react';
2+
import { Dialog, Transition } from '@headlessui/react';
3+
import { XMarkIcon } from '@heroicons/react/24/outline';
34
import Button from './tavern-base-ui/button/Button';
45

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

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

23+
useEffect(() => {
24+
const checkMobile = () => {
25+
setIsMobile(window.innerWidth < 768) // md breakpoint
26+
}
27+
28+
checkMobile()
29+
window.addEventListener('resize', checkMobile)
30+
return () => window.removeEventListener('resize', checkMobile)
31+
}, [])
32+
2133
const openDialog = (): void => {
22-
const dialogWidth = 384;
23-
const spacing = 8;
24-
25-
const button = buttonRef.current
26-
if (button) {
27-
const rect = button.getBoundingClientRect()
28-
const viewportWidth = window.innerWidth
29-
30-
const calculatedLeft = Math.min(
31-
rect.left + window.scrollX,
32-
viewportWidth - dialogWidth - 16 // 16px right margin
33-
)
34-
35-
setPosition({
36-
top: rect.bottom + window.scrollY + spacing,
37-
left: calculatedLeft,
38-
})
34+
if (!isMobile) {
35+
const dialogWidth = 384;
36+
const spacing = 8;
37+
38+
const button = buttonRef.current
39+
if (button) {
40+
const rect = button.getBoundingClientRect()
41+
const viewportWidth = window.innerWidth
42+
43+
const calculatedLeft = Math.min(
44+
rect.left,
45+
viewportWidth - dialogWidth - 16
46+
)
47+
48+
setPosition({
49+
top: rect.bottom + spacing,
50+
left: calculatedLeft,
51+
})
52+
}
3953
}
4054
setIsOpen(true)
4155
}
4256

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

4559
useEffect(() => {
46-
if (!isOpen) return
60+
if (!isOpen || isMobile) return
4761

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

6074
document.addEventListener('mousedown', handleClickOutside)
6175
return () => document.removeEventListener('mousedown', handleClickOutside)
62-
}, [isOpen])
76+
}, [isOpen, isMobile])
6377

6478
return (
6579
<div className='flex justify-end'>
66-
<Button ref={buttonRef} leftIcon={leftIcon} buttonVariant='ghost' buttonStyle={{ color: "gray", size: "md" }} onClick={openDialog}>{label}</Button>
67-
<Dialog open={isOpen} onClose={closeDialog}>
68-
<div>
69-
<div className="fixed inset-0 bg-black/30 z-40" aria-hidden="true" />
70-
<div
71-
ref={panelRef}
72-
className="absolute z-50 bg-white border rounded-lg shadow-lg w-96 p-4 flex flex-col gap-4"
73-
style={{
74-
top: position.top,
75-
left: position.left,
76-
}}
80+
<Button
81+
ref={buttonRef}
82+
leftIcon={leftIcon}
83+
buttonVariant='ghost'
84+
buttonStyle={{ color: "gray", size: "md" }}
85+
onClick={openDialog}
86+
>
87+
{label}
88+
</Button>
89+
90+
<Transition.Root show={isOpen} as={Fragment}>
91+
<Dialog as="div" className="relative z-50" onClose={closeDialog}>
92+
{/* Backdrop */}
93+
<Transition.Child
94+
as={Fragment}
95+
enter="ease-out duration-300"
96+
enterFrom="opacity-0"
97+
enterTo="opacity-100"
98+
leave="ease-in duration-200"
99+
leaveFrom="opacity-100"
100+
leaveTo="opacity-0"
77101
>
78-
{children}
79-
</div>
80-
</div>
81-
</Dialog>
102+
<div className="fixed inset-0 bg-black/30 z-40" aria-hidden="true" />
103+
</Transition.Child>
104+
105+
{isMobile ? (
106+
/* Mobile: Full-screen modal */
107+
<div className="fixed inset-0 z-50 overflow-y-auto">
108+
<Transition.Child
109+
as={Fragment}
110+
enter="ease-out duration-300"
111+
enterFrom="opacity-0 scale-95"
112+
enterTo="opacity-100 scale-100"
113+
leave="ease-in duration-200"
114+
leaveFrom="opacity-100 scale-100"
115+
leaveTo="opacity-0 scale-95"
116+
>
117+
<Dialog.Panel className="min-h-full bg-white">
118+
{/* Header with close button */}
119+
<div className="sticky top-0 z-10 bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between">
120+
<Dialog.Title className="text-lg font-medium text-gray-900">
121+
{label}
122+
</Dialog.Title>
123+
<button
124+
onClick={closeDialog}
125+
className="text-gray-400 hover:text-gray-500"
126+
>
127+
<XMarkIcon className="h-6 w-6" />
128+
</button>
129+
</div>
130+
131+
{/* Content */}
132+
<div className="px-4 py-4">
133+
{children}
134+
</div>
135+
</Dialog.Panel>
136+
</Transition.Child>
137+
</div>
138+
) : (
139+
/* Desktop: Positioned popover */
140+
<div className="fixed inset-0 z-50 overflow-y-auto">
141+
<Transition.Child
142+
as={Fragment}
143+
enter="ease-out duration-200"
144+
enterFrom="opacity-0 scale-95"
145+
enterTo="opacity-100 scale-100"
146+
leave="ease-in duration-150"
147+
leaveFrom="opacity-100 scale-100"
148+
leaveTo="opacity-0 scale-95"
149+
>
150+
<div
151+
ref={panelRef}
152+
className="fixed bg-white border rounded-lg shadow-lg w-96 p-4 flex flex-col gap-4"
153+
style={{
154+
top: position.top,
155+
left: position.left,
156+
}}
157+
>
158+
{children}
159+
</div>
160+
</Transition.Child>
161+
</div>
162+
)}
163+
</Dialog>
164+
</Transition.Root>
82165
</div>
83166
);
84167
}

0 commit comments

Comments
 (0)