Skip to content

Commit e61baad

Browse files
author
김동규
committed
Merge branch 'main' of github.com:actbase/react-ui
2 parents 989914a + 9fcb148 commit e61baad

File tree

12 files changed

+417
-9
lines changed

12 files changed

+417
-9
lines changed

components/modal/context.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react';
2+
import { ModalContext } from './context.types';
3+
4+
const context = React.createContext<ModalContext | undefined>(undefined);
5+
6+
export default context;

components/modal/context.types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { HandleModalType } from './provider.types';
2+
3+
export interface ModalContext {
4+
modal: HandleModalType;
5+
}

components/modal/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { HandleModalType } from './provider.types';
2+
3+
export * from './modal.types';
4+
export * from './provider.types';
5+
export * from './context.types';
6+
7+
import InternalModal from './modal';
8+
import ModalProvider, { ref } from './provider';
9+
import context from './context';
10+
11+
const Modal = InternalModal as typeof InternalModal & {
12+
Provider: typeof ModalProvider;
13+
context: typeof context;
14+
modal: HandleModalType;
15+
};
16+
17+
Modal.Provider = ModalProvider;
18+
Modal.context = context;
19+
Modal.modal = function modal(options) {
20+
ref.current?.modal(options);
21+
};
22+
23+
export default Modal;

components/modal/modal.stories.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Meta, StoryObj } from '@storybook/react';
2+
3+
import React from 'react';
4+
import UI from '../ui';
5+
6+
export default {
7+
title: 'Feedback/Modal',
8+
component: UI.Modal,
9+
} satisfies Meta<typeof UI.Modal>;
10+
11+
type Story = StoryObj<typeof UI.Modal>;
12+
export const Modal: Story = {
13+
render: () => {
14+
return (
15+
<UI.Button
16+
type="primary"
17+
onClick={() => {
18+
UI.Modal.modal({
19+
render: () => <div>Modal Test</div>,
20+
});
21+
}}
22+
>
23+
Show Modal
24+
</UI.Button>
25+
);
26+
},
27+
};

components/modal/modal.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react';
2+
import { ModalProps } from './modal.types';
3+
import Theme from '../theme';
4+
import { ClassNames } from '@emotion/react';
5+
import getNamespace from '../_util/getNamespace';
6+
import getClassName from '../_util/getClassName';
7+
8+
const Modal = React.forwardRef<HTMLDivElement, ModalProps>(function Modal(
9+
{
10+
type,
11+
css: _css,
12+
children,
13+
className,
14+
onDestroy,
15+
index,
16+
template,
17+
render,
18+
...props
19+
},
20+
ref,
21+
) {
22+
const theme = Theme.useContext();
23+
const themeComponent = theme?.components?.modal;
24+
const themeComponentType = type
25+
? theme?.components?.modal?.type?.[type]
26+
: undefined;
27+
28+
return (
29+
<ClassNames>
30+
{({ css, cx }) => (
31+
<div
32+
ref={ref}
33+
className={cx(
34+
getNamespace(theme?.namespace),
35+
css`
36+
${typeof themeComponent?.css === 'function'
37+
? themeComponent.css({
38+
color: theme?.color,
39+
index,
40+
})
41+
: themeComponent?.css};
42+
${type &&
43+
(typeof themeComponentType?.css === 'function'
44+
? themeComponentType.css({
45+
color: theme?.color,
46+
index,
47+
})
48+
: themeComponentType?.css)};
49+
${typeof _css === 'function'
50+
? _css({ color: theme?.color, index })
51+
: _css};
52+
`,
53+
getClassName(theme?.namespace, 'modal'),
54+
themeComponent?.className,
55+
type && getClassName(theme?.namespace, `modal__type__${type}`),
56+
themeComponentType?.className,
57+
className,
58+
)}
59+
{...props}
60+
>
61+
{template
62+
? template({
63+
children: render?.({ onDestroy, ...props }),
64+
onDestroy,
65+
...props,
66+
})
67+
: themeComponentType?.template
68+
? themeComponentType.template({
69+
children: render?.({ onDestroy, ...props }),
70+
onDestroy,
71+
...props,
72+
})
73+
: themeComponent?.template
74+
? themeComponent.template({
75+
children: render?.({ onDestroy, ...props }),
76+
onDestroy,
77+
...props,
78+
})
79+
: render?.({ onDestroy, ...props })}
80+
</div>
81+
)}
82+
</ClassNames>
83+
);
84+
});
85+
86+
export default Modal;

components/modal/modal.types.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type React from 'react';
2+
import type {
3+
ComponentProps,
4+
ComponentTheme,
5+
ComponentTypeProps,
6+
} from '../component';
7+
8+
export type HandleModalDestroyType = () => void;
9+
10+
export interface ModalTemplateProps {
11+
onDestroy: HandleModalDestroyType;
12+
[key: string]: any;
13+
}
14+
15+
export interface ModalRenderProps {
16+
onDestroy: HandleModalDestroyType;
17+
[key: string]: any;
18+
}
19+
export type ModalTemplateType = (props: ModalTemplateProps) => React.ReactNode;
20+
export type ModalRenderType = (props: ModalRenderProps) => React.ReactNode;
21+
22+
export type ModalCssPropsType = {
23+
index?: number;
24+
};
25+
export type ModalType = Record<
26+
string,
27+
ComponentTypeProps<ModalCssPropsType> & {
28+
template?: ModalTemplateType;
29+
}
30+
>;
31+
32+
export interface ModalProps
33+
extends React.HTMLAttributes<HTMLDivElement>,
34+
ComponentProps<ModalType, ModalCssPropsType> {
35+
index?: number;
36+
template?: ModalTemplateType;
37+
render?: ModalRenderType;
38+
onDestroy: HandleModalDestroyType;
39+
}
40+
41+
export type ModalThemeType = ComponentTheme<ModalType, ModalCssPropsType> & {
42+
template?: ModalTemplateType;
43+
};

components/modal/provider.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import type {
2+
HandleModalType,
3+
ModalProviderProps,
4+
ModalProviderState,
5+
} from './provider.types';
6+
import type { ModalContext } from './context.types';
7+
8+
import React from 'react';
9+
import { v4 as uuidv4 } from 'uuid';
10+
import context from './context';
11+
import Modal from './modal';
12+
13+
export const ref = {
14+
current: undefined,
15+
} as {
16+
current?: {
17+
modal: HandleModalType;
18+
};
19+
};
20+
21+
const ModalProvider = React.memo<ModalProviderProps>(function ModalProvider({
22+
children,
23+
}) {
24+
const [modals, setModals] = React.useState<ModalProviderState[]>([]);
25+
const value = React.useMemo<ModalContext>(
26+
() => ({
27+
modal(options) {
28+
setModals((prevState) => [
29+
...prevState,
30+
{
31+
key: uuidv4(),
32+
...options,
33+
},
34+
]);
35+
},
36+
}),
37+
[],
38+
);
39+
React.useEffect(() => {
40+
ref.current = {
41+
modal: value.modal,
42+
};
43+
}, [value.modal]);
44+
return (
45+
<context.Provider value={value}>
46+
{children}
47+
{modals.map(({ key, message, ...props }) => {
48+
return (
49+
<Modal
50+
key={key}
51+
onDestroy={() => {
52+
setModals((prevState) =>
53+
prevState.filter((modal) => modal.key !== key),
54+
);
55+
}}
56+
{...props}
57+
>
58+
{message}
59+
</Modal>
60+
);
61+
})}
62+
</context.Provider>
63+
);
64+
});
65+
66+
export default ModalProvider;

components/modal/provider.types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type React from 'react';
2+
import type { ModalType, ModalTemplateType } from './modal.types';
3+
4+
export interface ModalOptions {
5+
type?: keyof ModalType;
6+
render?: ModalTemplateType;
7+
[key: string]: any;
8+
}
9+
export type HandleModalType = (options?: ModalOptions) => void;
10+
11+
export interface ModalProviderState {
12+
key?: React.Key;
13+
message?: React.ReactNode;
14+
type?: keyof ModalType;
15+
render?: ModalTemplateType;
16+
}
17+
18+
export interface ModalProviderProps {
19+
children?: React.ReactNode;
20+
}

components/provider/provider.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ProviderProps } from './provider.types';
22

33
import React from 'react';
44
import Theme from '../theme';
5+
import Modal from '../modal';
56
import Alert from '../alert';
67
import Toast from '../toast';
78

@@ -11,9 +12,11 @@ const Provider = React.memo<ProviderProps>(function Provider({
1112
}) {
1213
return (
1314
<Theme.Provider theme={theme}>
14-
<Alert.Provider>
15-
<Toast.Provider>{children}</Toast.Provider>
16-
</Alert.Provider>
15+
<Modal.Provider>
16+
<Alert.Provider>
17+
<Toast.Provider>{children}</Toast.Provider>
18+
</Alert.Provider>
19+
</Modal.Provider>
1720
</Theme.Provider>
1821
);
1922
});

components/theme/theme.types.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
import type { GlobalProps } from '@emotion/react';
12
import type { ButtonThemeType } from '../button';
2-
import type { CardThemeType } from '../card/card.types';
3+
import type { CardThemeType } from '../card';
34
import type { DividerThemeType } from '../divider';
45
import type { FormThemeType } from '../form';
56
import type { InputThemeType } from '../input';
67
import type { SpaceThemeType } from '../space';
78
import type { TypographyThemeType } from '../typography';
8-
import type { GlobalProps } from '@emotion/react';
9-
import type { ListThemeType } from '../list/list.types';
9+
import type { ListThemeType } from '../list';
1010
import type { ToastThemeType } from '../toast/toast.type';
11-
import { AlertThemeType } from '../alert';
12-
import { BlockThemeType } from '../block';
13-
import { SuspenseThemeType } from '../suspense/suspense.types';
11+
import type { AlertThemeType } from '../alert';
12+
import type { BlockThemeType } from '../block';
13+
import type { SuspenseThemeType } from '../suspense';
14+
import type { ModalThemeType } from '../modal';
1415

1516
export type ThemeColorType = Record<string, string>;
1617

@@ -28,6 +29,7 @@ export type ThemeType = {
2829
form?: FormThemeType;
2930
input?: InputThemeType;
3031
list?: ListThemeType;
32+
modal?: ModalThemeType;
3133
space?: SpaceThemeType;
3234
suspense?: SuspenseThemeType;
3335
table?: {};

0 commit comments

Comments
 (0)