feat: create settings modal

This commit is contained in:
Sonny
2024-05-16 23:54:40 +02:00
committed by Sonny
parent 18b2eb2c5a
commit 53aa7bc22b
16 changed files with 205 additions and 19 deletions

View File

@@ -0,0 +1,12 @@
import styled from '@emotion/styled';
const ModalBody = styled.div({
width: '100%',
display: 'flex',
flex: 1,
alignItems: 'center',
flexDirection: 'column',
overflow: 'auto',
});
export default ModalBody;

View File

@@ -0,0 +1,23 @@
import styled from '@emotion/styled';
const ModalContainer = styled.div(({ theme }) => ({
minWidth: '500px',
background: theme.colors.secondary,
padding: '1em',
borderRadius: theme.border.radius,
marginTop: '6em',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
boxShadow: theme.colors.boxShadow,
[`@media (max-width: ${theme.media.mobile})`]: {
maxHeight: 'calc(100% - 2em)',
width: 'calc(100% - 2em)',
minWidth: 'unset',
marginTop: '1em',
},
}));
export default ModalContainer;

View File

@@ -0,0 +1,20 @@
import styled from '@emotion/styled';
const ModalHeader = styled.h3({
width: '100%',
marginBottom: '0.75em',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
});
const ModalCloseBtn = styled.button(({ theme }) => ({
cursor: 'pointer',
color: theme.colors.primary,
backgroundColor: 'transparent',
border: 0,
padding: 0,
margin: 0,
}));
export { ModalHeader, ModalCloseBtn };

View File

@@ -0,0 +1,18 @@
import styled from '@emotion/styled';
import { rgba } from '~/lib/color';
const ModalWrapper = styled.div(({ theme }) => ({
zIndex: 9999,
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
background: rgba(theme.colors.black, 0.35),
backdropFilter: 'blur(0.25em)',
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
}));
export default ModalWrapper;

View File

@@ -0,0 +1,59 @@
import { Fragment, ReactNode, useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';
import { IoClose } from 'react-icons/io5';
import ModalBody from '~/components/common/modal/_modal_body';
import ModalContainer from '~/components/common/modal/_modal_container';
import {
ModalCloseBtn,
ModalHeader,
} from '~/components/common/modal/_modal_header';
import ModalWrapper from '~/components/common/modal/_modal_wrapper';
import TextEllipsis from '~/components/common/text_ellipsis';
import useClickOutside from '~/hooks/use_click_outside';
import useGlobalHotkeys from '~/hooks/use_global_hotkeys';
import useShortcut from '~/hooks/use_shortcut';
interface ModalProps {
title?: string;
children: ReactNode;
opened: boolean;
close: () => void;
}
export default function Modal({
title,
children,
opened = true,
close,
}: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
const { setGlobalHotkeysEnabled } = useGlobalHotkeys();
useClickOutside(modalRef, close);
useShortcut('ESCAPE_KEY', close, { ignoreGlobalHotkeysStatus: true });
useEffect(() => setGlobalHotkeysEnabled(!opened), [opened]);
if (typeof window === 'undefined') {
return <Fragment />;
}
return (
opened &&
createPortal(
<ModalWrapper>
<ModalContainer ref={modalRef}>
<ModalHeader>
{title && <TextEllipsis>{title}</TextEllipsis>}
<ModalCloseBtn onClick={close}>
<IoClose size={20} />
</ModalCloseBtn>
</ModalHeader>
<ModalBody>{children}</ModalBody>
</ModalContainer>
</ModalWrapper>,
document.body
)
);
}

View File

@@ -1,11 +1,15 @@
import styled from '@emotion/styled';
import { rgba } from '~/lib/color';
const RoundedImage = styled.img({
'borderRadius': '50%',
const RoundedImage = styled.img(({ theme }) => {
const transparentBlack = rgba(theme.colors.black, 0.1);
return {
borderRadius: '50%',
'&:hover': {
boxShadow: '0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1)',
},
'&:hover': {
boxShadow: `0 1px 3px 0 ${transparentBlack}, 0 1px 2px -1px ${transparentBlack}`,
},
};
});
export default RoundedImage;

View File

@@ -21,7 +21,7 @@ const LinksWrapper = styled.div({
const CollectionHeaderWrapper = styled.h2(({ theme }) => ({
fontWeight: 400,
color: theme.colors.font,
paddingRight: '1.1em',
paddingInline: '0.8em 1.1em',
display: 'flex',
gap: '0.4em',
alignItems: 'center',

View File

@@ -2,6 +2,7 @@ import styled from '@emotion/styled';
import { Link } from '@inertiajs/react';
export const Item = styled.div(({ theme }) => ({
cursor: 'pointer',
userSelect: 'none',
height: '40px',
width: '250px',

View File

@@ -0,0 +1,25 @@
import styled from '@emotion/styled';
import { BsGear } from 'react-icons/bs';
import Modal from '~/components/common/modal/modal';
import { Item } from '~/components/dashboard/side_nav/nav_item';
import useToggle from '~/hooks/use_modal';
const SettingsButton = styled(Item)(({ theme }) => ({
color: theme.colors.grey,
}));
export default function OpenSettingsButton() {
const { isShowing, open, close } = useToggle();
return (
<>
<SettingsButton onClick={open}>
<BsGear />
Settings
</SettingsButton>
<Modal title="Settings" opened={isShowing} close={close}>
Modal settings
</Modal>
</>
);
}

View File

@@ -1,11 +1,11 @@
import styled from '@emotion/styled';
import { route } from '@izzyjs/route/client';
import { AiOutlineFolderAdd } from 'react-icons/ai';
import { BsGear } from 'react-icons/bs';
import { IoAdd } from 'react-icons/io5';
import { MdOutlineAdminPanelSettings } from 'react-icons/md';
import FavoriteList from '~/components/dashboard/side_nav/favorite/favorite_list';
import { Item, ItemLink } from '~/components/dashboard/side_nav/nav_item';
import OpenSettingsButton from '~/components/dashboard/side_nav/open_settings';
import UserCard from '~/components/dashboard/side_nav/user_card';
import useActiveCollection from '~/hooks/use_active_collection';
import { appendCollectionId } from '~/lib/navigation';
@@ -21,10 +21,6 @@ const AdminButton = styled(Item)(({ theme }) => ({
color: theme.colors.lightRed,
}));
const SettingsButton = styled(Item)(({ theme }) => ({
color: theme.colors.grey,
}));
const AddButton = styled(ItemLink)(({ theme }) => ({
color: theme.colors.primary,
}));
@@ -38,10 +34,7 @@ export default function SideNavigation() {
<AdminButton>
<MdOutlineAdminPanelSettings /> Administrator
</AdminButton>
<SettingsButton>
<BsGear />
Settings
</SettingsButton>
<OpenSettingsButton />
<AddButton
href={appendCollectionId(
route('link.create-form').url,

View File

@@ -2,10 +2,18 @@ import KEYS from '#constants/keys';
import { useHotkeys } from 'react-hotkeys-hook';
import useGlobalHotkeys from '~/hooks/use_global_hotkeys';
export default function useShortcut(key: keyof typeof KEYS, cb: () => void) {
type ShortcutOptions = { ignoreGlobalHotkeysStatus?: boolean };
export default function useShortcut(
key: keyof typeof KEYS,
cb: () => void,
options: ShortcutOptions = {
ignoreGlobalHotkeysStatus: false,
}
) {
const { globalHotkeysEnabled } = useGlobalHotkeys();
return useHotkeys(KEYS[key], cb, {
enabled: globalHotkeysEnabled,
enabled: !options.ignoreGlobalHotkeysStatus ? globalHotkeysEnabled : true,
enableOnFormTags: ['INPUT'],
});
}

6
inertia/lib/color.ts Normal file
View File

@@ -0,0 +1,6 @@
import hexRgb from 'hex-rgb';
export const rgba = (hex: string, alpha: number) => {
const rgb = hexRgb(hex, { format: 'array' }).slice(0, -1).join(',');
return `rgba(${rgb},${alpha})`;
};

View File

@@ -1,4 +1,5 @@
import { Theme } from '@emotion/react';
import { rgba } from '~/lib/color';
export const primaryColor = '#3f88c5';
export const primaryDarkColor = '#005aa5';
@@ -32,6 +33,7 @@ export const lightTheme: Theme = {
primary: primaryColor,
secondary: '#fff',
black: '#333',
white: '#ffffff',
lightGrey: '#dadce0',
@@ -45,7 +47,7 @@ export const lightTheme: Theme = {
yellow: '#FF8A08',
boxShadow: '0 0 1em 0 rgba(102, 102, 102, 0.25)',
boxShadow: `0 0 1em 0 ${rgba('#aaa', 0.4)}`,
},
border,
@@ -60,6 +62,7 @@ export const darkTheme: Theme = {
primary: '#4fadfc',
secondary: '#323a47',
black: '#333',
white: '#ffffff',
lightGrey: '#323a47',
@@ -73,7 +76,7 @@ export const darkTheme: Theme = {
yellow: '#ffc107',
boxShadow: '0 0 1em 0 rgb(40 40 40)',
boxShadow: `0 0 1em 0 ${rgba('#111', 0.4)}`,
},
border,

View File

@@ -8,6 +8,7 @@ declare module '@emotion/react' {
primary: string;
secondary: string;
black: string;
white: string;
lightGrey: string;