Update useModal hook
All checks were successful
forgejo/Procyon/procyon/pipeline/head This commit looks good
All checks were successful
forgejo/Procyon/procyon/pipeline/head This commit looks good
This commit is contained in:
parent
4e84d18090
commit
bc7aeb3f01
16 changed files with 88 additions and 115 deletions
|
@ -11,14 +11,14 @@
|
|||
"vscode": {
|
||||
"extensions": [
|
||||
"EditorConfig.EditorConfig",
|
||||
"rvest.vs-code-prettier-eslint",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"unifiedjs.vscode-mdx",
|
||||
"naumovs.color-highlight",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"hediet.tasks-statusbar",
|
||||
"BeardedBear.beardedicons",
|
||||
"mia-hall.vscode-git-branch-sidebar"
|
||||
"mia-hall.vscode-git-branch-sidebar",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import { omit } from 'ramda';
|
|||
import { format as formatFn } from 'date-fns';
|
||||
|
||||
import { TodayIcon } from '@procyon/components/Icons/Today';
|
||||
import { ModalId } from '@procyon/components/Modal/context';
|
||||
import { useModal } from '@procyon/components/Modal/hooks';
|
||||
import { InputElementType } from '@procyon/types/form';
|
||||
|
||||
|
@ -22,7 +21,10 @@ export type DatePickerProps<V = Date> = Omit<
|
|||
export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
||||
const { onChange, label, name, format, ...others } = props;
|
||||
|
||||
const { closeModal, openModal, registerModal } = useModal();
|
||||
const [openModal, { closeModal }] = useModal(name, {
|
||||
title: label,
|
||||
style: { width: 400 },
|
||||
});
|
||||
|
||||
const [value, setValue] = useState<Date>();
|
||||
|
||||
|
@ -32,7 +34,7 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
|||
}
|
||||
}, [props.value]);
|
||||
|
||||
const handleCloseModal = (id: ModalId) => closeModal(id);
|
||||
const handleCloseModal = () => closeModal(name);
|
||||
|
||||
const handleDateClick = (value: Date): void => {
|
||||
setValue(value);
|
||||
|
@ -50,17 +52,13 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
|||
{...props}
|
||||
/>
|
||||
),
|
||||
[format, name, value]
|
||||
[format, name, value],
|
||||
);
|
||||
|
||||
const handleOpenModal = (): void => {
|
||||
registerModal({
|
||||
id: name,
|
||||
title: label,
|
||||
style: { width: 400 },
|
||||
Component: modalComponent
|
||||
openModal({
|
||||
Component: modalComponent,
|
||||
});
|
||||
openModal(name);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Calendar } from '../../Calendar';
|
|||
import { ModalId } from '../../Modal/context';
|
||||
|
||||
export type DatePickerModalProps = {
|
||||
closeModal: (id: ModalId) => void;
|
||||
closeModal: () => void;
|
||||
format: ModalId;
|
||||
name: ModalId;
|
||||
onChange: (value: Date) => void;
|
||||
|
@ -12,14 +12,14 @@ export type DatePickerModalProps = {
|
|||
};
|
||||
|
||||
export const DatePickerModal: FC<DatePickerModalProps> = (props) => {
|
||||
const { onChange, closeModal, name, value } = props;
|
||||
const { onChange, closeModal, value } = props;
|
||||
|
||||
const setActiveOption = useCallback(
|
||||
(value: Date) => {
|
||||
onChange?.(value);
|
||||
closeModal(name);
|
||||
closeModal();
|
||||
},
|
||||
[onChange, closeModal, name]
|
||||
[onChange, closeModal],
|
||||
);
|
||||
|
||||
return <Calendar selected={value ?? undefined} value={value} onClick={setActiveOption} enableHover />;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useEffect } from 'react';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { StatusEnum } from '@procyon/types/enums';
|
||||
|
||||
|
@ -7,29 +7,15 @@ import { Modal } from '../context';
|
|||
import { useModal } from '../hooks';
|
||||
|
||||
export type AddModalButtonProps = {
|
||||
Component: FC;
|
||||
modalTitle: string;
|
||||
props?: { [key: string]: any };
|
||||
status?: StatusEnum;
|
||||
title: string;
|
||||
} & Modal<any>;
|
||||
|
||||
export const AddModalButton: FC<AddModalButtonProps> = ({ id, title, Component, style, status, props }) => {
|
||||
const { openModal, registerModal } = useModal();
|
||||
|
||||
useEffect(() => {
|
||||
registerModal({
|
||||
Component,
|
||||
id,
|
||||
props,
|
||||
style,
|
||||
title,
|
||||
export const AddModalButton: FC<AddModalButtonProps> = ({ status, title, ...props }) => {
|
||||
const [openModal] = useModal(props.id, {
|
||||
...props,
|
||||
title: props.modalTitle,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClick = () => {
|
||||
openModal(id);
|
||||
};
|
||||
|
||||
return <Button label={title} onClick={handleClick} status={status} />;
|
||||
return <Button label={title} onClick={openModal} status={status} />;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ import { ModalId } from '../context';
|
|||
import { useModal } from '../hooks';
|
||||
|
||||
export const Modals = () => {
|
||||
const { modals, opened, closeModal } = useModal();
|
||||
const [, { modals, opened, closeModal }] = useModal();
|
||||
|
||||
const handleCloseModal = (modalId: ModalId) => {
|
||||
modals[modalId]?.props?.onClose?.();
|
||||
|
@ -43,11 +43,11 @@ export const Modals = () => {
|
|||
className="modal-container"
|
||||
style={{
|
||||
width: style?.width,
|
||||
...(style?.overrideMaxWidth ? { maxWidth: 'initial' } : {})
|
||||
...(style?.overrideMaxWidth ? { maxWidth: 'initial' } : {}),
|
||||
}}>
|
||||
<div className="modal-title">{title}</div>
|
||||
<div className="modal-body" onClick={(e: MouseEvent) => e.stopPropagation()}>
|
||||
<Component {...props} />
|
||||
{Component && <Component {...props} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { createContext, FC, ReactNode, useEffect, useMemo, useState } fro
|
|||
import { omit } from 'ramda';
|
||||
|
||||
export type Modal<P> = {
|
||||
Component: FC<P>;
|
||||
Component?: FC<P>;
|
||||
id: string;
|
||||
props?: Partial<P>;
|
||||
style?: {
|
||||
|
@ -29,7 +29,7 @@ export const ModalContext = createContext<ModalContextType>({
|
|||
open: () => {},
|
||||
close: () => {},
|
||||
register: () => {},
|
||||
unregister: () => {}
|
||||
unregister: () => {},
|
||||
});
|
||||
|
||||
export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
|
@ -41,8 +41,8 @@ export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
|||
...modals,
|
||||
[modal.id]: {
|
||||
...modals[modal.id],
|
||||
...modal
|
||||
}
|
||||
...modal,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -77,9 +77,9 @@ export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
|||
unregister,
|
||||
register,
|
||||
open,
|
||||
close
|
||||
close,
|
||||
}),
|
||||
[modals, opened]
|
||||
[modals, opened],
|
||||
);
|
||||
|
||||
return <ModalContext.Provider value={value}>{children}</ModalContext.Provider>;
|
||||
|
|
|
@ -2,22 +2,28 @@ import { useContext, useEffect } from 'react';
|
|||
|
||||
import { Modal, ModalContext } from './context';
|
||||
|
||||
export const useModal = <P>(modal?: Modal<P>) => {
|
||||
export const useModal = <P>(id?: Modal<P>['id'], modal?: Omit<Modal<P>, 'id'>) => {
|
||||
const { registered, close, open, opened, register, unregister } = useContext(ModalContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (modal) {
|
||||
register(modal);
|
||||
return () => unregister(modal.id);
|
||||
if (id && modal) {
|
||||
register({
|
||||
id,
|
||||
...modal,
|
||||
});
|
||||
return () => unregister(id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
return [
|
||||
(modal?: Omit<Modal<P>, 'id'>) => open(id!, modal),
|
||||
{
|
||||
modals: registered,
|
||||
opened: opened,
|
||||
openModal: open,
|
||||
closeModal: close,
|
||||
registerModal: register,
|
||||
unregisterModal: unregister,
|
||||
};
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
|
|
@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useState } from 'react';
|
|||
import { find, propEq, propOr } from 'ramda';
|
||||
|
||||
import { ButtonCursorIcon } from '@procyon/components/Icons/ButtonCursor';
|
||||
import { ModalId } from '@procyon/components/Modal/context';
|
||||
import { useModal } from '@procyon/components/Modal/hooks';
|
||||
import { InputElementType } from '@procyon/types/form';
|
||||
import { Option } from '@procyon/types/options';
|
||||
|
@ -21,7 +20,10 @@ export type SelectBoxProps<V = Option> = Omit<
|
|||
export const Selectbox: React.FC<SelectBoxProps> = (props) => {
|
||||
const { value, options, onChange, name, label, autoComplete, ...others } = props;
|
||||
|
||||
const { openModal, closeModal, registerModal } = useModal();
|
||||
const [openModal, { closeModal }] = useModal(name, {
|
||||
title: label,
|
||||
style: { width: 300 },
|
||||
});
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
||||
|
||||
|
@ -42,10 +44,6 @@ export const Selectbox: React.FC<SelectBoxProps> = (props) => {
|
|||
onChange?.(undefined, null);
|
||||
};
|
||||
|
||||
const handleCloseModal = (id: ModalId) => {
|
||||
closeModal(id);
|
||||
};
|
||||
|
||||
const modalComponent = useCallback(
|
||||
(props) => (
|
||||
<SelectboxModal
|
||||
|
@ -53,23 +51,19 @@ export const Selectbox: React.FC<SelectBoxProps> = (props) => {
|
|||
name={name}
|
||||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
closeModal={handleCloseModal}
|
||||
closeModal={closeModal}
|
||||
onChange={handleOptionClick}
|
||||
onRemove={handleOptionRemove}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
[autoComplete, name, options, selectedOption]
|
||||
[autoComplete, name, options, selectedOption],
|
||||
);
|
||||
|
||||
const handleOpenModal = (): void => {
|
||||
registerModal({
|
||||
id: name,
|
||||
title: label,
|
||||
style: { width: 300 },
|
||||
Component: modalComponent
|
||||
openModal({
|
||||
Component: modalComponent,
|
||||
});
|
||||
openModal(name);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -42,7 +42,7 @@ const TimeList: React.FC<TimeListProps> = ({ title, onChange, min, max, step, se
|
|||
selectedCode={selectedCode}
|
||||
scrollToCode={scrollToCode}
|
||||
enableAutoScroll
|
||||
className="h-60"
|
||||
className="h-60 text-center"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -29,8 +29,6 @@ export type TimePickerProps<V = Date> = Omit<
|
|||
export const TimePicker: FC<TimePickerProps> = (props) => {
|
||||
const { onChange, label, name, hour, minute, scrollToCode, ...others } = props;
|
||||
|
||||
const { openModal, registerModal } = useModal();
|
||||
|
||||
const [value, setValue] = useState<Date>();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -42,15 +40,10 @@ export const TimePicker: FC<TimePickerProps> = (props) => {
|
|||
onChange?.(value, null);
|
||||
};
|
||||
|
||||
const handleOpenModal = (): void => {
|
||||
registerModal({
|
||||
id: name,
|
||||
const [openModal] = useModal(name, {
|
||||
title: label,
|
||||
style: { width: 200 },
|
||||
Component: modalComponent
|
||||
style: { width: 220 },
|
||||
});
|
||||
openModal(name);
|
||||
};
|
||||
|
||||
const modalComponent = useCallback(
|
||||
(props) => (
|
||||
|
@ -64,9 +57,13 @@ export const TimePicker: FC<TimePickerProps> = (props) => {
|
|||
{...props}
|
||||
/>
|
||||
),
|
||||
[value, scrollToCode, name, minute, hour]
|
||||
[value, scrollToCode, name, minute, hour],
|
||||
);
|
||||
|
||||
const handleOpenModal = () => {
|
||||
openModal({ Component: modalComponent });
|
||||
};
|
||||
|
||||
return (
|
||||
<TextField
|
||||
{...others}
|
||||
|
|
|
@ -33,7 +33,7 @@ const { base } = GridSizeEnum;
|
|||
export const TimePickerModal: React.FC<TimePickerModalProps> = (props) => {
|
||||
const { onChange, modalName, scrollToCode } = props;
|
||||
|
||||
const { closeModal } = useModal();
|
||||
const [, { closeModal }] = useModal(props.modalName);
|
||||
|
||||
const [hour, setHour] = useState<number | null>();
|
||||
const [minute, setMinute] = useState<number | null>();
|
||||
|
|
|
@ -15,7 +15,7 @@ const creator: PluginCreator = ({ addComponents, theme }) =>
|
|||
borderRadius: theme('borderRadius.md'),
|
||||
border: `1px solid ${theme('colors.list.item.border')}`,
|
||||
'&-compact': {
|
||||
padding: '1px 1px 1px 8px',
|
||||
padding: '1px',
|
||||
},
|
||||
'&--focus': {
|
||||
backgroundColor: theme('colors.focus.background'),
|
||||
|
|
|
@ -29,6 +29,9 @@ const creator: PluginCreator = ({ addComponents, theme }) =>
|
|||
lineHeight: '24px',
|
||||
padding: '0.875rem',
|
||||
height: '54px',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
'&-cross': {
|
||||
position: 'absolute',
|
||||
|
|
|
@ -37,24 +37,15 @@ return <AddModalButton />;
|
|||
```tsx
|
||||
import { useModal } from '@procyon/components/Modal/hooks';
|
||||
|
||||
const { registerModal } = useModal();
|
||||
|
||||
registerModal({
|
||||
const [openModal] = useModal({
|
||||
component: <div>Modal content</div>,
|
||||
id: 'modal1',
|
||||
props: { onClick: () => {} },
|
||||
style: { width: '300px' },
|
||||
title: 'Modal Title',
|
||||
});
|
||||
```
|
||||
|
||||
Then, call `openModal` hook.
|
||||
|
||||
```ts
|
||||
import { openModal } from '@procyon/components/Modal/hooks';
|
||||
|
||||
const { openModal } = useModal();
|
||||
openModal('modal1');
|
||||
openModal();
|
||||
```
|
||||
|
||||
<Canvas of={ModalStories.Hook} withToolbar />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
|
@ -43,15 +43,11 @@ export const Hook: Story = {
|
|||
name: 'Hook',
|
||||
render: () => {
|
||||
const Comp = () => {
|
||||
const { registerModal, openModal } = useModal();
|
||||
useEffect(() => {
|
||||
registerModal({
|
||||
const [openModal] = useModal('modal2', {
|
||||
Component: () => <div>Hi there</div>,
|
||||
id: 'modal2',
|
||||
title: 'Modal Title',
|
||||
});
|
||||
}, []);
|
||||
return <Button label="Open Modal" onClick={() => openModal('modal2')} />;
|
||||
return <Button label="Open Modal" onClick={openModal} />;
|
||||
};
|
||||
return <Comp />;
|
||||
},
|
||||
|
|
|
@ -25,10 +25,12 @@ export default {
|
|||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ height: '600px' }}>
|
||||
<ModalWrapper>
|
||||
<Story />
|
||||
<Modals />
|
||||
</ModalWrapper>
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} as Meta<typeof TimePicker>;
|
||||
|
@ -37,7 +39,7 @@ const date = new Date();
|
|||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
name: 'datePicker',
|
||||
name: 'timePicker1',
|
||||
label: 'Choose time',
|
||||
value: setHours(date, 20),
|
||||
},
|
||||
|
@ -45,14 +47,14 @@ export const Default: Story = {
|
|||
|
||||
export const WithoutLabel: Story = {
|
||||
args: {
|
||||
name: 'datePicker',
|
||||
name: 'timePicker2',
|
||||
value: setHours(date, 20),
|
||||
},
|
||||
};
|
||||
|
||||
export const RestrictedTimes: Story = {
|
||||
args: {
|
||||
name: 'datePicker',
|
||||
name: 'timePicker3',
|
||||
label: 'Choose time',
|
||||
value: setHours(date, 20),
|
||||
minute: {
|
||||
|
|
Loading…
Add table
Reference in a new issue