polpo 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/theme.ts +2 -2
- package/.turbo/turbo-lint.log +1 -1
- package/README.md +2 -5
- package/dist/chunk-CFYQBHH5.js +3 -0
- package/dist/chunk-CFYQBHH5.js.map +1 -0
- package/dist/chunk-MAWW6AA7.js +3 -0
- package/dist/chunk-MAWW6AA7.js.map +1 -0
- package/dist/get-modal-position-drle0OjP.d.cts +49 -0
- package/dist/get-modal-position-drle0OjP.d.ts +49 -0
- package/dist/helpers.cjs +1 -1
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +9 -2
- package/dist/helpers.d.ts +9 -2
- package/dist/helpers.js +1 -1
- package/dist/hooks.cjs +1 -1
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +59 -21
- package/dist/hooks.d.ts +59 -21
- package/dist/hooks.js +1 -1
- package/dist/ui.cjs +601 -389
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.d.cts +97 -77
- package/dist/ui.d.ts +97 -77
- package/dist/ui.js +585 -373
- package/dist/ui.js.map +1 -1
- package/dist/use-modal-in-container-DiNW1PE_.d.cts +34 -0
- package/dist/use-modal-in-container-neGo-kMk.d.ts +34 -0
- package/package.json +2 -2
- package/src/components/buttons/button/button.stories.tsx +4 -4
- package/src/components/buttons/button/button.style.ts +10 -5
- package/src/components/buttons/button/button.tsx +7 -19
- package/src/components/cards/flip-card/flip-card.tsx +1 -1
- package/src/components/cursor/cursor.stories.tsx +35 -0
- package/src/components/cursor/cursor.style.ts +73 -0
- package/src/components/cursor/cursor.tsx +49 -0
- package/src/components/cursor/index.ts +1 -0
- package/src/components/form/checkbox/checkbox.stories.tsx +51 -0
- package/src/components/form/checkbox/checkbox.style.ts +73 -37
- package/src/components/form/checkbox/checkbox.tsx +38 -4
- package/src/components/form/field/field.stories.tsx +5 -1
- package/src/components/form/field/field.style.ts +12 -0
- package/src/components/form/field/field.tsx +3 -1
- package/src/components/form/field/field.types.ts +6 -0
- package/src/components/form/input-color/input-color.style.ts +5 -4
- package/src/components/form/input-color/input-color.tsx +41 -44
- package/src/components/form/radio/radio.stories.tsx +29 -5
- package/src/components/form/radio/radio.style.ts +45 -24
- package/src/components/form/radio/radio.tsx +22 -3
- package/src/components/form/select/options.tsx +119 -67
- package/src/components/form/select/select.stories.tsx +103 -42
- package/src/components/form/select/select.style.ts +10 -92
- package/src/components/form/select/select.tsx +19 -42
- package/src/components/form/select/select.types.ts +4 -21
- package/src/components/form/slider/slider.style.ts +2 -0
- package/src/components/icon/icons/social.tsx +17 -1
- package/src/components/index.ts +1 -0
- package/src/components/infinity-scroll/infinity-scroll.tsx +1 -1
- package/src/components/line/line.stories.tsx +3 -4
- package/src/components/modals/action-modal/action-modal.stories.tsx +58 -39
- package/src/components/modals/action-modal/action-modal.style.ts +13 -25
- package/src/components/modals/action-modal/action-modal.tsx +68 -70
- package/src/components/modals/aside-modal/aside-modal.stories.tsx +11 -15
- package/src/components/modals/aside-modal/aside-modal.style.ts +17 -37
- package/src/components/modals/aside-modal/aside-modal.tsx +41 -43
- package/src/components/modals/confirmation-modal/confirmation-modal.stories.tsx +21 -9
- package/src/components/modals/index.ts +2 -0
- package/src/components/modals/menu/index.ts +1 -0
- package/src/components/modals/menu/menu.stories.tsx +69 -0
- package/src/components/modals/menu/menu.style.ts +62 -0
- package/src/components/modals/menu/menu.tsx +142 -0
- package/src/components/modals/modal/backdrop.tsx +70 -0
- package/src/components/modals/modal/index.ts +1 -0
- package/src/components/modals/modal/modal.stories.tsx +325 -0
- package/src/components/modals/modal/modal.style.ts +62 -2
- package/src/components/modals/modal/modal.tsx +82 -123
- package/src/components/modals/portal/index.ts +1 -0
- package/src/components/modals/portal/portal.tsx +18 -0
- package/src/components/tabs/tabs-list.tsx +13 -10
- package/src/components/tabs/tabs.style.ts +48 -43
- package/src/components/tag/tag.stories.tsx +11 -12
- package/src/components/tag/tag.style.ts +9 -4
- package/src/components/tag/tag.tsx +2 -12
- package/src/components/tooltips/tooltip/tooltip.stories.tsx +5 -2
- package/src/components/tooltips/tooltip/tooltip.style.ts +37 -6
- package/src/components/tooltips/tooltip/tooltip.tsx +33 -19
- package/src/components/typography/typography.stories.tsx +3 -1
- package/src/components/typography/typography.tsx +21 -0
- package/src/contexts/theme-context/theme.animations.ts +91 -2
- package/src/contexts/theme-context/theme.defaults.ts +1 -1
- package/src/core/http-client.ts +49 -47
- package/src/core/variants/color.ts +3 -30
- package/src/core/variants/radius.ts +12 -41
- package/src/core/variants/size.ts +8 -33
- package/src/helpers/get-modal-position-relative-to-screen.ts +86 -0
- package/src/helpers/get-modal-position.ts +173 -28
- package/src/helpers/index.ts +1 -0
- package/src/hooks/index.ts +9 -3
- package/src/hooks/use-click-outside.ts +32 -0
- package/src/hooks/use-cookie.ts +124 -0
- package/src/hooks/use-dimensions.ts +11 -14
- package/src/hooks/use-dom-container.ts +32 -0
- package/src/hooks/use-event-listener.ts +4 -4
- package/src/hooks/use-geolocation.ts +63 -0
- package/src/hooks/use-in-view.ts +9 -11
- package/src/hooks/use-intersection-observer.ts +19 -0
- package/src/hooks/use-modal-in-container.ts +60 -52
- package/src/hooks/use-modal-transition.ts +54 -0
- package/src/hooks/use-modal.ts +21 -0
- package/src/hooks/use-mouse-position.ts +55 -7
- package/src/hooks/use-resize-observer.ts +18 -0
- package/src/stories/GettingStarted.mdx +2 -6
- package/svg/Name=npm, Category=social.svg +3 -0
- package/dist/chunk-M4KRSYE7.js +0 -3
- package/dist/chunk-M4KRSYE7.js.map +0 -1
- package/dist/chunk-U5XSMSKZ.js +0 -3
- package/dist/chunk-U5XSMSKZ.js.map +0 -1
- package/dist/get-modal-position-DPftPoU2.d.cts +0 -28
- package/dist/get-modal-position-DPftPoU2.d.ts +0 -28
- package/src/components/form/select/select-option.tsx +0 -84
- package/src/hooks/use-observer.ts +0 -18
- package/src/hooks/use-on-click-outside-ref.ts +0 -17
|
@@ -29,7 +29,6 @@ export type SharedSelectProps<T extends SelectItem> = InputFieldProps<{
|
|
|
29
29
|
|
|
30
30
|
export type MultiSelectProps<T extends SelectItem> = SharedSelectProps<T> & {
|
|
31
31
|
multiselect: true;
|
|
32
|
-
optionVariant?: `${SelectOptionVariant}`;
|
|
33
32
|
};
|
|
34
33
|
|
|
35
34
|
export type SingleSelectProps<T extends SelectItem> = SharedSelectProps<T> & {
|
|
@@ -49,7 +48,7 @@ export type ControllerGeneratorSelectProps<T extends SelectItem> =
|
|
|
49
48
|
|
|
50
49
|
export type OptionsProps<T extends SelectItem> = {
|
|
51
50
|
isOpen: boolean;
|
|
52
|
-
|
|
51
|
+
onClose: () => void;
|
|
53
52
|
onSearchQuery?: (value: string) => void;
|
|
54
53
|
searchQueryValue?: string;
|
|
55
54
|
searchQueryPlaceholder?: string;
|
|
@@ -60,34 +59,18 @@ export type OptionsProps<T extends SelectItem> = {
|
|
|
60
59
|
options: Array<T>;
|
|
61
60
|
selectOption: (option: T) => void;
|
|
62
61
|
unselectOption: (option: T) => void;
|
|
63
|
-
|
|
62
|
+
containerRef: React.RefObject<HTMLElement>;
|
|
64
63
|
Component: React.FC<OptionComponentProps<T>>;
|
|
65
64
|
multiselect?: boolean;
|
|
66
65
|
value: SelectValue<T>;
|
|
67
|
-
|
|
66
|
+
emptyMessage?: string;
|
|
67
|
+
maxHeight?: number;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
// SELECT OPTION
|
|
71
71
|
|
|
72
|
-
export enum SelectOptionVariant {
|
|
73
|
-
CHECKBOX = 'checkbox',
|
|
74
|
-
ICON = 'icon',
|
|
75
|
-
DEFAULT = 'default',
|
|
76
|
-
}
|
|
77
|
-
|
|
78
72
|
export type OptionComponentProps<T extends SelectItem> = {
|
|
79
73
|
data: T;
|
|
80
74
|
isSelected: boolean;
|
|
81
75
|
multiselect: boolean;
|
|
82
76
|
};
|
|
83
|
-
|
|
84
|
-
export type SelectOptionProps<T extends SelectItem> = {
|
|
85
|
-
id: string;
|
|
86
|
-
selected: boolean;
|
|
87
|
-
selectOption: (value: T) => void;
|
|
88
|
-
unselectOption: (value: T) => void;
|
|
89
|
-
data: T;
|
|
90
|
-
multiselect: boolean;
|
|
91
|
-
Component: React.FC<OptionComponentProps<T>>;
|
|
92
|
-
variant: `${SelectOptionVariant}`;
|
|
93
|
-
};
|
|
@@ -45,12 +45,14 @@ export const SliderStyle = styled.section<SliderStyleProps>`
|
|
|
45
45
|
&::-webkit-slider-thumb {
|
|
46
46
|
background: ${props => props.theme.colors.primary.main};
|
|
47
47
|
box-shadow: 0 0 0 6px ${props => props.theme.colors.primary.main}88;
|
|
48
|
+
cursor: grab;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
&:active {
|
|
52
53
|
&::-webkit-slider-thumb {
|
|
53
54
|
box-shadow: 0 0 0 12px ${props => props.theme.colors.primary.main}55;
|
|
55
|
+
cursor: grabbing;
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { IconT } from '.';
|
|
2
2
|
|
|
3
|
-
export type SocialIconsT = Record<
|
|
3
|
+
export type SocialIconsT = Record<
|
|
4
|
+
'codepen' | 'facebook' | 'github' | 'instagram' | 'linkedin' | 'npm' | 'whatsapp',
|
|
5
|
+
IconT
|
|
6
|
+
>;
|
|
4
7
|
|
|
5
8
|
export const SocialIcons: SocialIconsT = {
|
|
6
9
|
codepen: {
|
|
@@ -58,6 +61,19 @@ export const SocialIcons: SocialIconsT = {
|
|
|
58
61
|
</g>
|
|
59
62
|
),
|
|
60
63
|
},
|
|
64
|
+
npm: {
|
|
65
|
+
viewBox: '0 0 32 32',
|
|
66
|
+
svg: fill => (
|
|
67
|
+
<g>
|
|
68
|
+
<path
|
|
69
|
+
fillRule='evenodd'
|
|
70
|
+
clipRule='evenodd'
|
|
71
|
+
d='M0 9.93333C0 9.41787 0.421379 9 0.941176 9H31.0588C31.5786 9 32 9.41787 32 9.93333V19.3704C32 19.8858 31.5786 20.3037 31.0588 20.3037H15.5722V22.0667C15.5722 22.5821 15.1508 23 14.631 23H9.15508C8.63528 23 8.2139 22.5821 8.2139 22.0667V20.3037H0.941176C0.421379 20.3037 0 19.8858 0 19.3704V9.93333ZM5.9893 18.437V13.9778C5.9893 13.4623 5.56793 13.0444 5.04813 13.0444C4.52833 13.0444 4.10695 13.4623 4.10695 13.9778V18.437H1.88235V10.8667H8.2139V18.437H5.9893ZM10.0963 10.8667L10.0963 21.1333H13.6898V19.3704C13.6898 18.8549 14.1112 18.437 14.631 18.437H17.7968V10.8667H10.0963ZM19.6791 10.8667V18.437H21.9037V13.9778C21.9037 13.4623 22.3251 13.0444 22.8449 13.0444C23.3647 13.0444 23.7861 13.4623 23.7861 13.9778V18.437H26.0107V13.9778C26.0107 13.4623 26.4321 13.0444 26.9519 13.0444C27.4717 13.0444 27.893 13.4623 27.893 13.9778V18.437H30.1176V10.8667H19.6791ZM14.631 13.0444C15.1508 13.0444 15.5722 13.4623 15.5722 13.9778V15.3259C15.5722 15.8414 15.1508 16.2593 14.631 16.2593C14.1112 16.2593 13.6898 15.8414 13.6898 15.3259V13.9778C13.6898 13.4623 14.1112 13.0444 14.631 13.0444Z'
|
|
72
|
+
fill={fill}
|
|
73
|
+
/>
|
|
74
|
+
</g>
|
|
75
|
+
),
|
|
76
|
+
},
|
|
61
77
|
whatsapp: {
|
|
62
78
|
viewBox: '0 0 32 32',
|
|
63
79
|
svg: fill => (
|
package/src/components/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { InfinityScrollFooterStyle } from './infinity-scroll.style';
|
|
|
6
6
|
|
|
7
7
|
import { useDebounce, useInView } from '@polpo/hooks';
|
|
8
8
|
|
|
9
|
-
type InfinityScrollProps<T> = {
|
|
9
|
+
export type InfinityScrollProps<T> = {
|
|
10
10
|
isLoading: boolean;
|
|
11
11
|
hasNextPage: boolean;
|
|
12
12
|
loadMore: () => void;
|
|
@@ -22,18 +22,17 @@ const meta: Meta<typeof Line> = {
|
|
|
22
22
|
args: {
|
|
23
23
|
color: 'currentColor',
|
|
24
24
|
},
|
|
25
|
-
render: (args, { orientation }) => <Line {...args} orientation={orientation} />,
|
|
26
25
|
decorators: [
|
|
27
|
-
Story => (
|
|
26
|
+
(Story, { args }) => (
|
|
28
27
|
<Grid ac='start' gtr='auto 1fr auto auto' gap='0.5em' style={{ width: '300px', height: '300px' }}>
|
|
29
28
|
<Typography variant='small' noPadding>
|
|
30
29
|
Vertical
|
|
31
30
|
</Typography>
|
|
32
|
-
<Story
|
|
31
|
+
<Story args={{ ...args, orientation: LineOrientation.VERTICAL }} />
|
|
33
32
|
<Typography variant='small' noPadding>
|
|
34
33
|
Horizontal
|
|
35
34
|
</Typography>
|
|
36
|
-
<Story
|
|
35
|
+
<Story args={{ ...args, orientation: LineOrientation.HORIZONTAL }} />
|
|
37
36
|
</Grid>
|
|
38
37
|
),
|
|
39
38
|
],
|
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Grid } from '../../../layouts';
|
|
4
|
-
import { Button } from '../../buttons';
|
|
5
|
-
import { IconNames } from '../../icon';
|
|
6
|
-
import { Line } from '../../line';
|
|
7
|
-
import { Typography } from '../../typography';
|
|
8
|
-
|
|
9
3
|
import { ActionModal } from './action-modal';
|
|
10
4
|
|
|
5
|
+
import { Button, Grid, IconNames, Line, Typography } from '@polpo/ui';
|
|
6
|
+
|
|
11
7
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
12
8
|
|
|
13
9
|
const meta: Meta<typeof ActionModal> = {
|
|
14
10
|
title: 'Modals/ActionModal',
|
|
15
11
|
component: ActionModal,
|
|
16
12
|
argTypes: {
|
|
17
|
-
isOpen: { control: false },
|
|
18
|
-
onClose: { control: false },
|
|
19
13
|
actionRequired: { control: 'boolean' },
|
|
20
14
|
icon: { options: [undefined, ...IconNames.toSorted()] },
|
|
21
15
|
noCloseButton: { control: 'boolean' },
|
|
@@ -30,51 +24,58 @@ const meta: Meta<typeof ActionModal> = {
|
|
|
30
24
|
children: 'Action modal content',
|
|
31
25
|
},
|
|
32
26
|
decorators: [
|
|
33
|
-
Story => {
|
|
27
|
+
(Story, { args }) => {
|
|
34
28
|
const [isOpen, setIsOpen] = useState(false);
|
|
35
29
|
|
|
36
30
|
return (
|
|
37
31
|
<>
|
|
38
32
|
<Button onClick={() => setIsOpen(true)}>Open modal</Button>
|
|
39
|
-
<Story
|
|
33
|
+
<Story args={{ ...args, isOpen, onClose: () => setIsOpen(false) }} />
|
|
40
34
|
</>
|
|
41
35
|
);
|
|
42
36
|
},
|
|
43
37
|
],
|
|
44
|
-
render: (args, { isOpen, onClose }) => {
|
|
45
|
-
return <ActionModal {...args} isOpen={isOpen} onClose={onClose} />;
|
|
46
|
-
},
|
|
47
38
|
};
|
|
48
39
|
|
|
49
40
|
export default meta;
|
|
50
41
|
type Story = StoryObj<typeof ActionModal>;
|
|
51
42
|
|
|
52
|
-
export const
|
|
53
|
-
args: {},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const Confirmation: Story = {
|
|
43
|
+
export const Classic: Story = {
|
|
57
44
|
argTypes: {
|
|
58
45
|
children: { control: false },
|
|
59
46
|
},
|
|
60
47
|
args: {
|
|
61
|
-
backCard: true,
|
|
62
48
|
lineOnTop: true,
|
|
63
|
-
|
|
49
|
+
noPadding: true,
|
|
64
50
|
},
|
|
65
|
-
render:
|
|
51
|
+
render: args => {
|
|
66
52
|
return (
|
|
67
|
-
<ActionModal {...args}
|
|
68
|
-
<Typography variant='header4'
|
|
53
|
+
<ActionModal {...args}>
|
|
54
|
+
<Typography variant='header4' noPadding style={{ padding: '0 1rem' }}>
|
|
69
55
|
Action modal
|
|
70
56
|
</Typography>
|
|
71
|
-
<
|
|
72
|
-
<Grid
|
|
57
|
+
<Line />
|
|
58
|
+
<Grid gap='1em' style={{ padding: '0 1rem 1rem' }}>
|
|
59
|
+
<Typography>Are you sure want to execute an action?</Typography>
|
|
60
|
+
</Grid>
|
|
61
|
+
<Grid
|
|
62
|
+
ai='center'
|
|
63
|
+
columnSize='80px'
|
|
64
|
+
ji='center'
|
|
65
|
+
flow='column'
|
|
66
|
+
jc='end'
|
|
67
|
+
gap='1em'
|
|
68
|
+
style={{ padding: '0 1rem 1rem' }}
|
|
69
|
+
>
|
|
73
70
|
<ActionModal.ActionButton
|
|
74
71
|
size='small'
|
|
75
72
|
width='full'
|
|
76
73
|
variant='ghost'
|
|
77
|
-
onClick={() =>
|
|
74
|
+
onClick={() =>
|
|
75
|
+
new Promise(resolve => {
|
|
76
|
+
setTimeout(resolve, 2000);
|
|
77
|
+
})
|
|
78
|
+
}
|
|
78
79
|
>
|
|
79
80
|
No
|
|
80
81
|
</ActionModal.ActionButton>
|
|
@@ -82,7 +83,11 @@ export const Confirmation: Story = {
|
|
|
82
83
|
size='small'
|
|
83
84
|
width='full'
|
|
84
85
|
color='primary'
|
|
85
|
-
onClick={() =>
|
|
86
|
+
onClick={() =>
|
|
87
|
+
new Promise(resolve => {
|
|
88
|
+
setTimeout(resolve, 2000);
|
|
89
|
+
})
|
|
90
|
+
}
|
|
86
91
|
>
|
|
87
92
|
Yes
|
|
88
93
|
</ActionModal.ActionButton>
|
|
@@ -92,30 +97,40 @@ export const Confirmation: Story = {
|
|
|
92
97
|
},
|
|
93
98
|
};
|
|
94
99
|
|
|
95
|
-
export const
|
|
100
|
+
export const Confirmation: Story = {
|
|
96
101
|
argTypes: {
|
|
97
102
|
children: { control: false },
|
|
98
103
|
},
|
|
99
104
|
args: {
|
|
105
|
+
backCard: true,
|
|
100
106
|
lineOnTop: true,
|
|
101
|
-
|
|
107
|
+
icon: 'user',
|
|
102
108
|
},
|
|
103
|
-
render:
|
|
109
|
+
render: args => {
|
|
104
110
|
return (
|
|
105
|
-
<ActionModal {...args}
|
|
106
|
-
<Typography variant='header4'
|
|
111
|
+
<ActionModal {...args}>
|
|
112
|
+
<Typography variant='header4' align='center' noPadding>
|
|
107
113
|
Action modal
|
|
108
114
|
</Typography>
|
|
109
|
-
<
|
|
110
|
-
<Grid
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
<Typography>Are you sure want to execute an action?</Typography>
|
|
116
|
+
<Grid
|
|
117
|
+
ai='center'
|
|
118
|
+
columnSize='minmax(100px, 1fr)'
|
|
119
|
+
ji='center'
|
|
120
|
+
flow='column'
|
|
121
|
+
jc='center'
|
|
122
|
+
gap='1em'
|
|
123
|
+
style={{ margin: 'auto' }}
|
|
124
|
+
>
|
|
114
125
|
<ActionModal.ActionButton
|
|
115
126
|
size='small'
|
|
116
127
|
width='full'
|
|
117
128
|
variant='ghost'
|
|
118
|
-
onClick={() =>
|
|
129
|
+
onClick={() =>
|
|
130
|
+
new Promise(resolve => {
|
|
131
|
+
setTimeout(resolve, 2000);
|
|
132
|
+
})
|
|
133
|
+
}
|
|
119
134
|
>
|
|
120
135
|
No
|
|
121
136
|
</ActionModal.ActionButton>
|
|
@@ -123,7 +138,11 @@ export const Classic: Story = {
|
|
|
123
138
|
size='small'
|
|
124
139
|
width='full'
|
|
125
140
|
color='primary'
|
|
126
|
-
onClick={() =>
|
|
141
|
+
onClick={() =>
|
|
142
|
+
new Promise(resolve => {
|
|
143
|
+
setTimeout(resolve, 2000);
|
|
144
|
+
})
|
|
145
|
+
}
|
|
127
146
|
>
|
|
128
147
|
Yes
|
|
129
148
|
</ActionModal.ActionButton>
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { styled } from 'styled-components';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
position: fixed;
|
|
5
|
-
top: 50%;
|
|
6
|
-
left: 50%;
|
|
7
|
-
transform: translate(-50%, -50%);
|
|
8
|
-
z-index: 10000;
|
|
3
|
+
import { Modal } from '../modal';
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
position: relative;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.close-animation {
|
|
16
|
-
animation: bounceOut 500ms ease;
|
|
17
|
-
}
|
|
5
|
+
export const ModalStyle = styled(Modal)`
|
|
6
|
+
background: transparent;
|
|
18
7
|
|
|
19
|
-
.
|
|
20
|
-
animation
|
|
8
|
+
.modal-content {
|
|
9
|
+
&.shake-animation {
|
|
10
|
+
animation: headShake 600ms linear;
|
|
11
|
+
}
|
|
21
12
|
}
|
|
22
13
|
`;
|
|
23
14
|
|
|
@@ -25,6 +16,7 @@ export const ActionModalStyle = styled.section`
|
|
|
25
16
|
position: relative;
|
|
26
17
|
|
|
27
18
|
.action-modal-body {
|
|
19
|
+
box-shadow: 0 0 10px -5px;
|
|
28
20
|
border-radius: 10px;
|
|
29
21
|
overflow: hidden;
|
|
30
22
|
display: grid;
|
|
@@ -85,22 +77,18 @@ export const ActionModalStyle = styled.section`
|
|
|
85
77
|
}
|
|
86
78
|
|
|
87
79
|
.close-modal-button {
|
|
88
|
-
width:
|
|
89
|
-
height:
|
|
80
|
+
width: 1em;
|
|
81
|
+
height: 1em;
|
|
90
82
|
border-radius: 50%;
|
|
91
|
-
border: 1px solid;
|
|
92
83
|
cursor: pointer;
|
|
93
|
-
display: grid;
|
|
94
|
-
place-content: center;
|
|
95
84
|
background: ${props => props.theme.colors.background.main};
|
|
96
85
|
position: absolute;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
font-size: ${props => props.theme.constants.typography.
|
|
86
|
+
top: 10px;
|
|
87
|
+
right: 5px;
|
|
88
|
+
font-size: ${props => props.theme.constants.typography.header3.fontSize};
|
|
100
89
|
opacity: 0;
|
|
101
90
|
transition: opacity 300ms ease;
|
|
102
91
|
z-index: 1;
|
|
103
|
-
transform: translate(-50%, 50%);
|
|
104
92
|
}
|
|
105
93
|
|
|
106
94
|
&.line-on-top {
|
|
@@ -3,42 +3,45 @@ import { createContext, useCallback, useContext, useRef, useState } from 'react'
|
|
|
3
3
|
import { Button, ButtonProps } from '../../buttons';
|
|
4
4
|
import { Icon, IconNameT } from '../../icon';
|
|
5
5
|
import { Typography } from '../../typography';
|
|
6
|
-
import {
|
|
6
|
+
import { ModalProps } from '../modal';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { ModalStyle, ActionModalStyle } from './action-modal.style';
|
|
9
9
|
|
|
10
|
+
import { PositionContainer } from '@polpo/helpers';
|
|
10
11
|
import { useClassNames } from '@polpo/hooks';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
children: React.ReactNode;
|
|
14
|
-
isOpen: boolean;
|
|
13
|
+
type ActionModalContextType = {
|
|
15
14
|
onClose: () => void;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
noCloseButton?: boolean;
|
|
19
|
-
className?: string;
|
|
20
|
-
style?: React.CSSProperties;
|
|
21
|
-
lineOnTop?: boolean;
|
|
22
|
-
backCard?: boolean;
|
|
23
|
-
noPadding?: boolean;
|
|
15
|
+
isActionInProgress: boolean;
|
|
16
|
+
setIsActionInProgress: (isActionInProgress: boolean) => void;
|
|
24
17
|
};
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
onClose: () => void;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const ActionModalContext = createContext<ActionModalContextEntity | null>(null);
|
|
19
|
+
const ActionModalContext = createContext<ActionModalContextType | null>(null);
|
|
31
20
|
|
|
32
|
-
const
|
|
21
|
+
const useActionModalContext = () => {
|
|
33
22
|
const context = useContext(ActionModalContext);
|
|
34
23
|
|
|
35
|
-
if (context
|
|
36
|
-
throw new Error('
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error('useActionModalContext must be used within a ActionModal');
|
|
37
26
|
}
|
|
38
27
|
|
|
39
28
|
return context;
|
|
40
29
|
};
|
|
41
30
|
|
|
31
|
+
export type ActionModalProps = Omit<
|
|
32
|
+
ModalProps,
|
|
33
|
+
'id' | 'animation' | 'closeAnimationClassName' | 'position' | 'rootStyle' | 'className' | 'style'
|
|
34
|
+
> & {
|
|
35
|
+
actionRequired?: boolean;
|
|
36
|
+
icon?: IconNameT;
|
|
37
|
+
noCloseButton?: boolean;
|
|
38
|
+
lineOnTop?: boolean;
|
|
39
|
+
backCard?: boolean;
|
|
40
|
+
noPadding?: boolean;
|
|
41
|
+
className?: string;
|
|
42
|
+
style?: React.CSSProperties;
|
|
43
|
+
};
|
|
44
|
+
|
|
42
45
|
export const ActionModal = ({
|
|
43
46
|
children,
|
|
44
47
|
isOpen,
|
|
@@ -46,25 +49,17 @@ export const ActionModal = ({
|
|
|
46
49
|
actionRequired,
|
|
47
50
|
icon,
|
|
48
51
|
noCloseButton,
|
|
49
|
-
className = '',
|
|
50
|
-
style = {},
|
|
51
52
|
lineOnTop = false,
|
|
52
53
|
backCard = false,
|
|
53
54
|
noPadding = false,
|
|
55
|
+
className = '',
|
|
56
|
+
style = {},
|
|
57
|
+
...modalProps
|
|
54
58
|
}: ActionModalProps) => {
|
|
59
|
+
const [isActionInProgress, setIsActionInProgress] = useState(false);
|
|
55
60
|
const ref = useRef<HTMLElement>(null);
|
|
56
61
|
|
|
57
|
-
const handleOnClose = useCallback((callback: () => void) => {
|
|
58
|
-
ref.current?.classList.remove('open-animation');
|
|
59
|
-
ref.current?.classList.add('close-animation');
|
|
60
|
-
setTimeout(() => {
|
|
61
|
-
callback();
|
|
62
|
-
ref.current?.classList.remove('close-animation');
|
|
63
|
-
}, 490);
|
|
64
|
-
}, []);
|
|
65
|
-
|
|
66
62
|
const remainAction = useCallback(() => {
|
|
67
|
-
ref.current?.classList.remove('open-animation');
|
|
68
63
|
ref.current?.classList.add('shake-animation');
|
|
69
64
|
setTimeout(() => {
|
|
70
65
|
ref.current?.classList.remove('shake-animation');
|
|
@@ -77,43 +72,39 @@ export const ActionModal = ({
|
|
|
77
72
|
'no-padding': noPadding,
|
|
78
73
|
});
|
|
79
74
|
|
|
80
|
-
const contentClassName = useClassNames({
|
|
81
|
-
'action-modal-content': true,
|
|
82
|
-
[className]: Boolean(className),
|
|
83
|
-
});
|
|
84
|
-
|
|
85
75
|
return (
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
76
|
+
<ActionModalContext.Provider value={{ onClose, isActionInProgress, setIsActionInProgress }}>
|
|
77
|
+
<ModalStyle
|
|
78
|
+
id='action-modal'
|
|
79
|
+
animation='bounce'
|
|
80
|
+
opacity={0.8}
|
|
81
|
+
isOpen={isOpen}
|
|
82
|
+
onClose={onClose}
|
|
83
|
+
{...modalProps}
|
|
84
|
+
backdropOnClick={actionRequired ? remainAction : onClose}
|
|
85
|
+
position={PositionContainer.CENTER}
|
|
86
|
+
>
|
|
87
|
+
<section ref={ref} className='modal-content'>
|
|
88
|
+
<ActionModalStyle className={actionModalClassName}>
|
|
89
|
+
{!noCloseButton && !actionRequired && (
|
|
90
|
+
<section className='close-modal-button' onClick={() => onClose()}>
|
|
91
|
+
<Icon name='cross' inCircle scale={2} />
|
|
92
|
+
</section>
|
|
93
|
+
)}
|
|
94
|
+
{icon ? (
|
|
95
|
+
<Typography variant='header4' className='action-modal-icon'>
|
|
96
|
+
<Icon name={icon} />
|
|
97
|
+
</Typography>
|
|
98
|
+
) : null}
|
|
99
|
+
<section className='action-modal-body'>
|
|
100
|
+
<section className={`action-modal-content ${className}`} style={style}>
|
|
101
|
+
{children}
|
|
111
102
|
</section>
|
|
112
|
-
</
|
|
113
|
-
</
|
|
114
|
-
</
|
|
115
|
-
</
|
|
116
|
-
</
|
|
103
|
+
</section>
|
|
104
|
+
</ActionModalStyle>
|
|
105
|
+
</section>
|
|
106
|
+
</ModalStyle>
|
|
107
|
+
</ActionModalContext.Provider>
|
|
117
108
|
);
|
|
118
109
|
};
|
|
119
110
|
|
|
@@ -122,23 +113,30 @@ type ActionButtonProps = Omit<ButtonProps, 'onClick'> & {
|
|
|
122
113
|
};
|
|
123
114
|
|
|
124
115
|
const ActionButton = ({ onClick, children, isLoading: manualIsLoading, ...buttonProps }: ActionButtonProps) => {
|
|
125
|
-
const { onClose } =
|
|
116
|
+
const { onClose, isActionInProgress, setIsActionInProgress } = useActionModalContext();
|
|
126
117
|
const [isLoading, setIsLoading] = useState(false);
|
|
127
118
|
|
|
128
119
|
const handleAction = useCallback(() => {
|
|
129
120
|
setIsLoading(true);
|
|
121
|
+
setIsActionInProgress(true);
|
|
130
122
|
const result = onClick();
|
|
131
123
|
|
|
132
124
|
if (result instanceof Promise) {
|
|
133
125
|
result.then(() => {
|
|
134
126
|
onClose();
|
|
135
127
|
setIsLoading(false);
|
|
128
|
+
setIsActionInProgress(false);
|
|
136
129
|
});
|
|
137
130
|
} else {
|
|
138
131
|
onClose();
|
|
139
132
|
setIsLoading(false);
|
|
133
|
+
setIsActionInProgress(false);
|
|
140
134
|
}
|
|
141
|
-
}, [onClick, onClose]);
|
|
135
|
+
}, [onClick, onClose, setIsActionInProgress]);
|
|
136
|
+
|
|
137
|
+
if (!isLoading && isActionInProgress) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
142
140
|
|
|
143
141
|
return (
|
|
144
142
|
<Button {...buttonProps} onClick={handleAction} isLoading={manualIsLoading || isLoading}>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { Image } from '../../image';
|
|
5
|
-
import { Typography } from '../../typography';
|
|
3
|
+
import { AsideModal } from './aside-modal';
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import { PositionContainer } from '@polpo/helpers';
|
|
6
|
+
import { Button, Image, Typography } from '@polpo/ui';
|
|
8
7
|
|
|
9
8
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
10
9
|
|
|
@@ -12,30 +11,27 @@ const meta: Meta<typeof AsideModal> = {
|
|
|
12
11
|
title: 'Modals/AsideModal',
|
|
13
12
|
component: AsideModal,
|
|
14
13
|
argTypes: {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
position: {
|
|
15
|
+
control: 'inline-radio',
|
|
16
|
+
options: [PositionContainer.TOP, PositionContainer.LEFT, PositionContainer.RIGHT, PositionContainer.BOTTOM],
|
|
17
|
+
},
|
|
18
18
|
children: { control: 'text' },
|
|
19
19
|
size: { control: { type: 'range', min: 100, max: 500, step: 10 } },
|
|
20
20
|
className: { control: false },
|
|
21
21
|
style: { control: false },
|
|
22
|
-
zIndex: { control: false },
|
|
23
22
|
},
|
|
24
23
|
decorators: [
|
|
25
|
-
Story => {
|
|
24
|
+
(Story, { args }) => {
|
|
26
25
|
const [isOpen, setIsOpen] = useState(false);
|
|
27
26
|
|
|
28
27
|
return (
|
|
29
28
|
<>
|
|
30
29
|
<Button onClick={() => setIsOpen(true)}>Open modal</Button>
|
|
31
|
-
<Story
|
|
30
|
+
<Story args={{ ...args, isOpen, onClose: () => setIsOpen(false) }} />
|
|
32
31
|
</>
|
|
33
32
|
);
|
|
34
33
|
},
|
|
35
34
|
],
|
|
36
|
-
render: (args, { isOpen, onClose }) => {
|
|
37
|
-
return <AsideModal {...args} isOpen={isOpen} onClose={onClose} />;
|
|
38
|
-
},
|
|
39
35
|
};
|
|
40
36
|
|
|
41
37
|
export default meta;
|
|
@@ -48,9 +44,9 @@ export const Aside: Story = {
|
|
|
48
44
|
args: {
|
|
49
45
|
size: 500,
|
|
50
46
|
},
|
|
51
|
-
render:
|
|
47
|
+
render: args => {
|
|
52
48
|
return (
|
|
53
|
-
<AsideModal {...args}
|
|
49
|
+
<AsideModal {...args}>
|
|
54
50
|
<Typography variant='header4'>Aside modal</Typography>
|
|
55
51
|
<Typography>
|
|
56
52
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam amet at cupiditate dolorum eaque eligendi
|