refactor: migrate params editor drawer to custom component

This commit is contained in:
Carlos Valente
2025-06-20 22:23:21 +02:00
committed by Carlos Valente
parent 4827b295fa
commit fdd81354cc
13 changed files with 281 additions and 97 deletions
@@ -57,5 +57,9 @@
}
.large {
height: 3.5rem;
height: 2.5rem;
}
.xlarge {
height: 3rem;
}
@@ -6,7 +6,7 @@ import style from './Button.module.scss';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'subtle' | 'primary';
size?: 'medium' | 'large';
size?: 'medium' | 'large' | 'xlarge';
}
export default function Button(props: ButtonProps) {
@@ -2,8 +2,6 @@
.baseIconButton {
aspect-ratio: 1;
height: 2rem;
width: 2rem;
display: grid;
place-content: center;
@@ -65,3 +63,18 @@
border-color: $red-900;
}
}
.medium {
height: 2rem;
width: 2rem;
}
.large {
height: 2.5rem;
width: 2.5rem;
}
.xlarge {
height: 3rem;
height: 3rem;
}
@@ -6,13 +6,18 @@ import style from './IconButton.module.scss';
interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'subtle' | 'subtle-white' | 'destructive';
size?: 'medium' | 'large' | 'xlarge';
}
export default function IconButton(props: IconButtonProps) {
const { className, children, variant = 'subtle', ...buttonProps } = props;
const { className, children, variant = 'subtle', size = 'medium', ...buttonProps } = props;
return (
<button className={cx([style.baseIconButton, style[variant], className])} type='button' {...buttonProps}>
<button
className={cx([style.baseIconButton, style[variant], style[size], className])}
type='button'
{...buttonProps}
>
{children}
</button>
);
@@ -11,15 +11,31 @@ interface ViewNavigationMenuProps {
isLockable?: boolean;
}
function ViewNavigationMenu(props: ViewNavigationMenuProps) {
const { isLockable } = props;
export default memo(ViewNavigationMenu);
function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) {
const { isOpen: isMenuOpen, onOpen: onMenuOpen, onClose: onMenuClose } = useDisclosure();
const { showEditFormDrawer, isViewLocked } = useViewEditor({ isLockable });
const toggleMenu = () => (isMenuOpen ? onMenuClose() : onMenuOpen());
useHotkeys([['mod + ,', () => toggleMenu()]]);
useHotkeys([
[
'Space',
() => {
if (isViewLocked) return;
toggleMenu();
},
{ preventDefault: true },
],
[
'mod + ,',
() => {
if (isViewLocked) return;
showEditFormDrawer();
},
{ preventDefault: true },
],
]);
if (isViewLocked) {
return <ViewLockedIcon />;
@@ -32,5 +48,3 @@ function ViewNavigationMenu(props: ViewNavigationMenuProps) {
</>
);
}
export default memo(ViewNavigationMenu);
@@ -8,12 +8,81 @@
}
}
.backdrop {
position: fixed;
inset: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.7);
transition: opacity 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
}
}
.drawer {
box-sizing: border-box;
position: fixed;
top: 0;
right: 0;
bottom: 0;
z-index: 11;
width: 40rem;
height: 100vh;
display: flex;
flex-direction: column;
padding: 1rem 1.5rem;
background-color: $gray-1250;
color: $ui-white;
&[data-open] {
transform: translateX(0%);
transition: transform 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
}
&[data-starting-style],
&[data-ending-style] {
transform: translateX(100%);
transition: transform 500ms cubic-bezier(0.45, 1.005, 0, 1.005);
}
// take the whole screen in mobile devices
@media (max-width: $min-tablet) {
width: 100vw;
}
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
height: 3.5rem;
font-weight: 600;
font-size: 1.25rem;
}
.body {
flex: 1;
padding-bottom: 10vh;
overflow-y: scroll;
}
.footer {
display: flex;
gap: 1rem;
align-items: center;
justify-content: flex-end;
}
.sectionList {
display: flex;
flex-direction: column;
min-height: 100%;
gap: 2rem;
overflow-y: scroll;
padding-right: 0.5rem;
}
@@ -1,18 +1,12 @@
import { FormEvent, memo, useEffect } from 'react';
import { IoClose } from 'react-icons/io5';
import { useSearchParams } from 'react-router-dom';
import {
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
useDisclosure,
} from '@chakra-ui/react';
import { Dialog } from '@base-ui-components/react/dialog';
import { useDisclosure } from '@mantine/hooks';
import useViewSettings from '../../hooks-query/useViewSettings';
import Button from '../buttons/Button';
import IconButton from '../buttons/IconButton';
import Info from '../info/Info';
import { ViewOption } from './viewParams.types';
@@ -31,27 +25,27 @@ function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
const [searchParams, setSearchParams] = useSearchParams();
const { data: viewSettings } = useViewSettings();
const { isOpen, onClose, onOpen } = useDisclosure();
const [isOpen, handlers] = useDisclosure(false);
// handle opening the drawer
useEffect(() => {
const isEditing = searchParams.get('edit');
if (isEditing === 'true') {
return onOpen();
return handlers.open();
}
}, [searchParams, onOpen]);
}, [searchParams, handlers]);
const handleClose = () => {
searchParams.delete('edit');
setSearchParams(searchParams);
onClose();
handlers.close();
};
const resetParams = () => {
setSearchParams();
onClose();
handlers.close();
};
const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => {
@@ -63,39 +57,48 @@ function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
};
return (
<Drawer isOpen={isOpen} placement='right' onClose={handleClose} variant='ontime' size='lg'>
<DrawerOverlay />
<DrawerContent>
<DrawerHeader>
<DrawerCloseButton size='lg' />
Customise
</DrawerHeader>
<DrawerBody>
{viewSettings.overrideStyles && (
<Info className={style.info}>This view style is being modified by a custom CSS file.</Info>
)}
<form id='edit-params-form' onSubmit={onParamsFormSubmit} className={style.sectionList}>
{viewOptions.map((section) => (
<ViewParamsSection
key={section.title}
title={section.title}
collapsible={section.collapsible}
options={section.options}
/>
))}
</form>
</DrawerBody>
<DrawerFooter className={style.drawerFooter}>
<Button variant='ontime-ghosted' onClick={resetParams} type='reset'>
Reset to default
</Button>
<Button variant='ontime-filled' form='edit-params-form' type='submit'>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<Dialog.Root
open={isOpen}
onOpenChange={(open) => {
if (!open) {
handleClose();
}
}}
>
<Dialog.Portal>
<Dialog.Backdrop className={style.backdrop} />
<Dialog.Popup className={style.drawer}>
<div className={style.header}>
<Dialog.Title>Customise</Dialog.Title>
<IconButton variant='subtle-white' size='large' onClick={handleClose}>
<IoClose />
</IconButton>
</div>
<div className={style.body}>
{viewSettings.overrideStyles && (
<Info className={style.info}>This view style is being modified by a custom CSS file.</Info>
)}
<form id='edit-params-form' onSubmit={onParamsFormSubmit} className={style.sectionList}>
{viewOptions.map((section) => (
<ViewParamsSection
key={section.title}
title={section.title}
collapsible={section.collapsible}
options={section.options}
/>
))}
</form>
</div>
<div className={style.footer}>
<Button variant='subtle' size='large' onClick={resetParams} type='reset'>
Reset to default
</Button>
<Button variant='primary' size='large' form='edit-params-form' type='submit'>
Save
</Button>
</div>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
);
}