mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 20:03:52 +00:00
refactor: migrate params editor drawer to custom component
This commit is contained in:
committed by
Carlos Valente
parent
4827b295fa
commit
fdd81354cc
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user