useModal doest register only open and close
All checks were successful
forgejo/Procyon/procyon/pipeline/pr-develop This commit looks good
forgejo/Procyon/procyon/pipeline/head This commit looks good

This commit is contained in:
Roman Jaroš 2023-11-21 07:16:14 +01:00 committed by Roman Jaroš
parent 49a163482c
commit 4a283c5982
14 changed files with 45 additions and 50 deletions

View file

@ -3,7 +3,7 @@ import { omit } from 'ramda';
import { format as formatFn } from 'date-fns'; import { format as formatFn } from 'date-fns';
import { TodayIcon } from '@procyon/components/Icons/Today'; import { TodayIcon } from '@procyon/components/Icons/Today';
import { useModal } from '@procyon/components/Modal/hooks'; import { useModal } from '@procyon/components/Modal/useModal';
import { InputElementType } from '@procyon/types/form'; import { InputElementType } from '@procyon/types/form';
import { TextField } from '../../TextField'; import { TextField } from '../../TextField';
@ -21,7 +21,7 @@ export type DatePickerProps<V = Date> = Omit<
export const DatePicker: React.FC<DatePickerProps> = (props) => { export const DatePicker: React.FC<DatePickerProps> = (props) => {
const { onChange, label, name, format, ...others } = props; const { onChange, label, name, format, ...others } = props;
const [openModal, { closeModal }] = useModal(name, { const [openModal, closeModal] = useModal(name, {
title: label, title: label,
style: { width: 400 }, style: { width: 400 },
}); });
@ -34,8 +34,6 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
} }
}, [props.value]); }, [props.value]);
const handleCloseModal = () => closeModal(name);
const handleDateClick = (value: Date): void => { const handleDateClick = (value: Date): void => {
setValue(value); setValue(value);
onChange?.(value, null); onChange?.(value, null);
@ -47,7 +45,7 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
format={format ?? DefaultDateFormat} format={format ?? DefaultDateFormat}
name={name} name={name}
value={value} value={value}
closeModal={handleCloseModal} closeModal={closeModal}
onChange={handleDateClick} onChange={handleDateClick}
{...props} {...props}
/> />

View file

@ -11,6 +11,10 @@ export enum GridSizeEnum {
xl = 'xl' xl = 'xl'
} }
export const {
sm, md, lg, xl
} = GridSizeEnum;
export enum GridVerticalAlignEnum { export enum GridVerticalAlignEnum {
bottom = 'bottom', bottom = 'bottom',
center = 'center', center = 'center',

View file

@ -4,7 +4,7 @@ import { StatusEnum } from '@procyon/types/enums';
import { Button } from '../../Button'; import { Button } from '../../Button';
import { Modal } from '../context'; import { Modal } from '../context';
import { useModal } from '../hooks'; import { useModal } from '../useModal';
export type AddModalButtonProps = { export type AddModalButtonProps = {
modalTitle: string; modalTitle: string;

View file

@ -3,10 +3,10 @@ import React, { MouseEvent, useEffect } from 'react';
import { isNilOrEmpty } from '@procyon/utils'; import { isNilOrEmpty } from '@procyon/utils';
import { ModalId } from '../context'; import { ModalId } from '../context';
import { useModal } from '../hooks'; import { useModal } from '../useModal';
export const Modals = () => { export const Modals = () => {
const [, { modals, opened, closeModal }] = useModal(); const [, , { modals, opened, closeModal }] = useModal();
const handleCloseModal = (modalId: ModalId) => { const handleCloseModal = (modalId: ModalId) => {
modals[modalId]?.props?.onClose?.(); modals[modalId]?.props?.onClose?.();
@ -36,7 +36,8 @@ export const Modals = () => {
return ( return (
<> <>
{opened.map((modalId) => { {opened.map((modalId) => {
const { id, style, title, Component, props } = modals[modalId]; const { id, style, title, Component, props } = modals[modalId] ?? {};
if (!id) return null;
return ( return (
<div key={id} className="modal-wrapper bg-black bg-opacity-70" onClick={() => handleCloseModal(id)}> <div key={id} className="modal-wrapper bg-black bg-opacity-70" onClick={() => handleCloseModal(id)}>
<div <div

View file

@ -33,32 +33,32 @@ export const ModalContext = createContext<ModalContextType>({
}); });
export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => { export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
const [modals, setModals] = useState<Record<ModalId, Modal<any>>>({}); const [registered, setRegistered] = useState<Record<ModalId, Modal<any>>>({});
const [opened, setOpened] = useState<ModalId[]>([]); const [opened, setOpened] = useState<ModalId[]>([]);
const register = function <P>(modal: Modal<P>) { const register = function <P>(modal: Modal<P>) {
setModals({ setRegistered((modals) => ({
...modals, ...modals,
[modal.id]: { [modal.id]: {
...modals[modal.id], ...modals[modal.id],
...modal, ...modal,
}, },
}); }));
}; };
const unregister = (modalId: ModalId) => { const unregister = (modalId: ModalId) => {
setModals(omit([modalId], modals)); setRegistered((modals) => omit([modalId], modals));
}; };
const open = function <P>(modalId: ModalId, modal: Modal<P> | undefined) { const open = function <P>(modalId: ModalId, modal: Modal<P> | undefined) {
if (modal) { if (!registered[modalId]) {
register({ ...modal, id: modalId }); register({ ...modal, id: modalId });
} }
setOpened((opened) => [...opened, modalId]); setOpened((opened) => [...opened, modalId]);
}; };
const close = (modalId: ModalId) => { const close = (modalId: ModalId) => {
setOpened(opened.filter((id) => id !== modalId)); setOpened((opened) => opened.filter((id) => id !== modalId));
}; };
useEffect(() => { useEffect(() => {
@ -72,14 +72,14 @@ export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
const value = useMemo( const value = useMemo(
() => ({ () => ({
registered: modals, registered: registered,
opened, opened,
unregister, unregister,
register, register,
open, open,
close, close,
}), }),
[modals, opened], [registered, opened],
); );
return <ModalContext.Provider value={value}>{children}</ModalContext.Provider>; return <ModalContext.Provider value={value}>{children}</ModalContext.Provider>;

View file

@ -1,22 +1,16 @@
import { useContext, useEffect } from 'react'; import { useContext } from 'react';
import { Modal, ModalContext } from './context'; import { Modal, ModalContext } from './context';
export const useModal = <P>(id?: Modal<P>['id'], modal?: Omit<Modal<P>, 'id'>) => { export const useModal = <P>(id?: Modal<P>['id'], options?: Omit<Modal<P>, 'id'>) => {
const { registered, close, open, opened, register, unregister } = useContext(ModalContext); const { registered, close, open, opened, register, unregister } = useContext(ModalContext);
useEffect(() => {
if (id && modal) {
register({
id,
...modal,
});
return () => unregister(id);
}
}, []);
return [ return [
(modal?: Omit<Modal<P>, 'id'>) => open(id!, modal), (newOptions?: Omit<Modal<P>, 'id'>) =>
open(id!, {
...options,
...newOptions,
}),
() => close(id!),
{ {
modals: registered, modals: registered,
opened: opened, opened: opened,

View file

@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import { find, propEq, propOr } from 'ramda'; import { find, propEq, propOr } from 'ramda';
import { ButtonCursorIcon } from '@procyon/components/Icons/ButtonCursor'; import { ButtonCursorIcon } from '@procyon/components/Icons/ButtonCursor';
import { useModal } from '@procyon/components/Modal/hooks'; import { useModal } from '@procyon/components/Modal/useModal';
import { InputElementType } from '@procyon/types/form'; import { InputElementType } from '@procyon/types/form';
import { Option } from '@procyon/types/options'; import { Option } from '@procyon/types/options';
@ -20,7 +20,7 @@ export type SelectBoxProps<V = Option> = Omit<
export const Selectbox: React.FC<SelectBoxProps> = (props) => { export const Selectbox: React.FC<SelectBoxProps> = (props) => {
const { value, options, onChange, name, label, autoComplete, ...others } = props; const { value, options, onChange, name, label, autoComplete, ...others } = props;
const [openModal, { closeModal }] = useModal(name, { const [openModal, closeModal] = useModal(name, {
title: label, title: label,
style: { width: 300 }, style: { width: 300 },
}); });

View file

@ -12,7 +12,7 @@ import { ModalId } from '../../Modal/context';
export type SelectboxModalProps = { export type SelectboxModalProps = {
autoComplete?: boolean; autoComplete?: boolean;
closeModal: (id: ModalId) => void; closeModal: () => void;
name: ModalId; name: ModalId;
onChange: (options: Option) => void; onChange: (options: Option) => void;
onRemove: () => void; onRemove: () => void;
@ -27,12 +27,12 @@ export const SelectboxModal: React.FC<SelectboxModalProps> = (props) => {
const handleChange = (option: Option) => { const handleChange = (option: Option) => {
onChange(option); onChange(option);
closeModal(name); closeModal();
}; };
const removeActiveOption = (): void => { const removeActiveOption = (): void => {
onRemove(); onRemove();
closeModal(name); closeModal();
}; };
return ( return (

View file

@ -2,7 +2,7 @@ import React, { FC, useCallback, useEffect, useState } from 'react';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { ClockTimeIcon } from '@procyon/components/Icons/ClockTime'; import { ClockTimeIcon } from '@procyon/components/Icons/ClockTime';
import { useModal } from '@procyon/components/Modal/hooks'; import { useModal } from '@procyon/components/Modal/useModal';
import { InputElementType } from '@procyon/types/form'; import { InputElementType } from '@procyon/types/form';
import { Option } from '@procyon/types/options'; import { Option } from '@procyon/types/options';

View file

@ -5,7 +5,7 @@ import { Button } from '@procyon/components/Button';
import { Grid, GridSizeEnum } from '@procyon/components/Grid'; import { Grid, GridSizeEnum } from '@procyon/components/Grid';
import { GridCol } from '@procyon/components/GridCol'; import { GridCol } from '@procyon/components/GridCol';
import { TrashIcon } from '@procyon/components/Icons/Trash'; import { TrashIcon } from '@procyon/components/Icons/Trash';
import { useModal } from '@procyon/components/Modal/hooks'; import { useModal } from '@procyon/components/Modal/useModal';
import { SizeEnum, StatusEnum } from '@procyon/types/enums'; import { SizeEnum, StatusEnum } from '@procyon/types/enums';
import { Option } from '@procyon/types/options'; import { Option } from '@procyon/types/options';
@ -33,7 +33,7 @@ const { base } = GridSizeEnum;
export const TimePickerModal: React.FC<TimePickerModalProps> = (props) => { export const TimePickerModal: React.FC<TimePickerModalProps> = (props) => {
const { onChange, modalName, scrollToCode } = props; const { onChange, modalName, scrollToCode } = props;
const [, { closeModal }] = useModal(props.modalName); const [, closeModal] = useModal(modalName);
const [hour, setHour] = useState<number | null>(); const [hour, setHour] = useState<number | null>();
const [minute, setMinute] = useState<number | null>(); const [minute, setMinute] = useState<number | null>();
@ -62,14 +62,14 @@ export const TimePickerModal: React.FC<TimePickerModalProps> = (props) => {
value = setMinutes(value, minute ?? 0); value = setMinutes(value, minute ?? 0);
value = setSeconds(value, 0); value = setSeconds(value, 0);
onChange(value); onChange(value);
closeModal(modalName); closeModal();
}; };
const handleClearValue = () => { const handleClearValue = () => {
setHour(null); setHour(null);
setMinute(null); setMinute(null);
onChange(undefined); onChange(undefined);
closeModal(modalName); closeModal();
}; };
const selectedHour = hour ? hour : undefined; const selectedHour = hour ? hour : undefined;

View file

@ -11,7 +11,7 @@ type IProps<FormData extends FieldValues> = {
onSubmit: SubmitHandler<FormData>; onSubmit: SubmitHandler<FormData>;
}; };
function Form<FormData extends FieldValues>(props: IProps<FormData>) { export function Form<FormData extends FieldValues>(props: IProps<FormData>) {
const { onSubmit, onFail = always(noop), methods } = props; const { onSubmit, onFail = always(noop), methods } = props;
return ( return (
<FormProvider {...methods}> <FormProvider {...methods}>
@ -19,5 +19,3 @@ function Form<FormData extends FieldValues>(props: IProps<FormData>) {
</FormProvider> </FormProvider>
); );
} }
export default Form;

View file

@ -30,12 +30,12 @@ import { AddModalButton } from '@procyon/components/Modal/components/AddModalBut
return <AddModalButton />; return <AddModalButton />;
``` ```
<Canvas of={ModalStories.Default} withToolbar /> <Canvas of={ModalStories.Default} withToolbar className='h-80' />
## Use hook `useModal()` ## Use hook `useModal()`
```tsx ```tsx
import { useModal } from '@procyon/components/Modal/hooks'; import { useModal } from '@procyon/components/Modal/useModal';
const [openModal] = useModal({ const [openModal] = useModal({
component: <div>Modal content</div>, component: <div>Modal content</div>,
@ -48,7 +48,7 @@ const [openModal] = useModal({
openModal(); openModal();
``` ```
<Canvas of={ModalStories.Hook} withToolbar /> <Canvas of={ModalStories.Hook} withToolbar className='h-80' />
## Use function `openModal()` ## Use function `openModal()`
@ -60,4 +60,4 @@ import { openModal } from '@procyon/components/Modal/context';
openModal('modal1'); openModal('modal1');
``` ```
<Canvas of={ModalStories.OutsideReact} withToolbar /> <Canvas of={ModalStories.OutsideReact} withToolbar className='h-80' />

View file

@ -6,7 +6,7 @@ import { Button } from '@procyon/components/Button';
import { Modals } from '@procyon/components/Modal'; import { Modals } from '@procyon/components/Modal';
import { AddModalButton } from '@procyon/components/Modal/components/AddModalButton'; import { AddModalButton } from '@procyon/components/Modal/components/AddModalButton';
import { ModalWrapper, openModal as openOutsideReactModal } from '@procyon/components/Modal/context'; import { ModalWrapper, openModal as openOutsideReactModal } from '@procyon/components/Modal/context';
import { useModal } from '@procyon/components/Modal/hooks'; import { useModal } from '@procyon/components/Modal/useModal';
import { TextField } from '@procyon/components/TextField'; import { TextField } from '@procyon/components/TextField';
type Story = StoryObj<typeof Modals>; type Story = StoryObj<typeof Modals>;

View file

@ -6,7 +6,7 @@ import { Grid, GridSizeEnum } from '@procyon/components/Grid';
import { GridCol } from '@procyon/components/GridCol'; import { GridCol } from '@procyon/components/GridCol';
import { BuildingStoreIcon } from '@procyon/components/Icons/BuildingStore'; import { BuildingStoreIcon } from '@procyon/components/Icons/BuildingStore';
import { Field } from '@procyon/forms/Field'; import { Field } from '@procyon/forms/Field';
import Form from '@procyon/forms/Form'; import { Form } from '@procyon/forms/Form';
import { FormSubmit } from '@procyon/forms/types'; import { FormSubmit } from '@procyon/forms/types';
import useForm from '@procyon/forms/useForm'; import useForm from '@procyon/forms/useForm';
import { StatusEnum } from '@procyon/types/enums'; import { StatusEnum } from '@procyon/types/enums';