polpo 0.1.0 → 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.
Files changed (128) hide show
  1. package/.storybook/theme.ts +2 -2
  2. package/.turbo/turbo-build.log +0 -77
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/README.md +2 -5
  5. package/dist/chunk-CFYQBHH5.js +3 -0
  6. package/dist/chunk-CFYQBHH5.js.map +1 -0
  7. package/dist/chunk-MAWW6AA7.js +3 -0
  8. package/dist/chunk-MAWW6AA7.js.map +1 -0
  9. package/dist/get-modal-position-drle0OjP.d.cts +49 -0
  10. package/dist/get-modal-position-drle0OjP.d.ts +49 -0
  11. package/dist/helpers.cjs +1 -1
  12. package/dist/helpers.cjs.map +1 -1
  13. package/dist/helpers.d.cts +9 -2
  14. package/dist/helpers.d.ts +9 -2
  15. package/dist/helpers.js +1 -1
  16. package/dist/hooks.cjs +1 -1
  17. package/dist/hooks.cjs.map +1 -1
  18. package/dist/hooks.d.cts +59 -21
  19. package/dist/hooks.d.ts +59 -21
  20. package/dist/hooks.js +1 -1
  21. package/dist/ui.cjs +601 -389
  22. package/dist/ui.cjs.map +1 -1
  23. package/dist/ui.d.cts +97 -77
  24. package/dist/ui.d.ts +97 -77
  25. package/dist/ui.js +585 -373
  26. package/dist/ui.js.map +1 -1
  27. package/dist/use-modal-in-container-DiNW1PE_.d.cts +34 -0
  28. package/dist/use-modal-in-container-neGo-kMk.d.ts +34 -0
  29. package/package.json +5 -5
  30. package/src/components/buttons/button/button.stories.tsx +4 -4
  31. package/src/components/buttons/button/button.style.ts +10 -5
  32. package/src/components/buttons/button/button.tsx +7 -19
  33. package/src/components/cards/flip-card/flip-card.tsx +1 -1
  34. package/src/components/cursor/cursor.stories.tsx +35 -0
  35. package/src/components/cursor/cursor.style.ts +73 -0
  36. package/src/components/cursor/cursor.tsx +49 -0
  37. package/src/components/cursor/index.ts +1 -0
  38. package/src/components/form/checkbox/checkbox.stories.tsx +51 -0
  39. package/src/components/form/checkbox/checkbox.style.ts +73 -37
  40. package/src/components/form/checkbox/checkbox.tsx +38 -4
  41. package/src/components/form/field/field.stories.tsx +5 -1
  42. package/src/components/form/field/field.style.ts +12 -0
  43. package/src/components/form/field/field.tsx +3 -1
  44. package/src/components/form/field/field.types.ts +6 -0
  45. package/src/components/form/input-color/input-color.style.ts +5 -4
  46. package/src/components/form/input-color/input-color.tsx +41 -44
  47. package/src/components/form/radio/radio.stories.tsx +29 -5
  48. package/src/components/form/radio/radio.style.ts +45 -24
  49. package/src/components/form/radio/radio.tsx +22 -3
  50. package/src/components/form/select/options.tsx +119 -67
  51. package/src/components/form/select/select.stories.tsx +103 -42
  52. package/src/components/form/select/select.style.ts +10 -92
  53. package/src/components/form/select/select.tsx +19 -42
  54. package/src/components/form/select/select.types.ts +4 -21
  55. package/src/components/form/slider/slider.style.ts +2 -0
  56. package/src/components/icon/icons/social.tsx +17 -1
  57. package/src/components/index.ts +1 -0
  58. package/src/components/infinity-scroll/infinity-scroll.tsx +1 -1
  59. package/src/components/line/line.stories.tsx +3 -4
  60. package/src/components/modals/action-modal/action-modal.stories.tsx +58 -39
  61. package/src/components/modals/action-modal/action-modal.style.ts +13 -25
  62. package/src/components/modals/action-modal/action-modal.tsx +68 -70
  63. package/src/components/modals/aside-modal/aside-modal.stories.tsx +11 -15
  64. package/src/components/modals/aside-modal/aside-modal.style.ts +17 -37
  65. package/src/components/modals/aside-modal/aside-modal.tsx +41 -43
  66. package/src/components/modals/confirmation-modal/confirmation-modal.stories.tsx +21 -9
  67. package/src/components/modals/index.ts +2 -0
  68. package/src/components/modals/menu/index.ts +1 -0
  69. package/src/components/modals/menu/menu.stories.tsx +69 -0
  70. package/src/components/modals/menu/menu.style.ts +62 -0
  71. package/src/components/modals/menu/menu.tsx +142 -0
  72. package/src/components/modals/modal/backdrop.tsx +70 -0
  73. package/src/components/modals/modal/index.ts +1 -0
  74. package/src/components/modals/modal/modal.stories.tsx +325 -0
  75. package/src/components/modals/modal/modal.style.ts +62 -2
  76. package/src/components/modals/modal/modal.tsx +82 -123
  77. package/src/components/modals/portal/index.ts +1 -0
  78. package/src/components/modals/portal/portal.tsx +18 -0
  79. package/src/components/tabs/tabs-list.tsx +13 -10
  80. package/src/components/tabs/tabs.style.ts +48 -43
  81. package/src/components/tag/tag.stories.tsx +11 -12
  82. package/src/components/tag/tag.style.ts +9 -4
  83. package/src/components/tag/tag.tsx +2 -12
  84. package/src/components/tooltips/tooltip/tooltip.stories.tsx +5 -2
  85. package/src/components/tooltips/tooltip/tooltip.style.ts +37 -6
  86. package/src/components/tooltips/tooltip/tooltip.tsx +33 -19
  87. package/src/components/typography/typography.stories.tsx +3 -1
  88. package/src/components/typography/typography.tsx +21 -0
  89. package/src/contexts/theme-context/theme.animations.ts +91 -2
  90. package/src/contexts/theme-context/theme.defaults.ts +1 -1
  91. package/src/core/http-client.ts +49 -47
  92. package/src/core/variants/color.ts +3 -30
  93. package/src/core/variants/radius.ts +12 -41
  94. package/src/core/variants/size.ts +8 -33
  95. package/src/helpers/get-modal-position-relative-to-screen.ts +86 -0
  96. package/src/helpers/get-modal-position.ts +173 -28
  97. package/src/helpers/index.ts +1 -0
  98. package/src/hooks/index.ts +9 -3
  99. package/src/hooks/use-click-outside.ts +32 -0
  100. package/src/hooks/use-cookie.ts +124 -0
  101. package/src/hooks/use-dimensions.ts +11 -14
  102. package/src/hooks/use-dom-container.ts +32 -0
  103. package/src/hooks/use-event-listener.ts +4 -4
  104. package/src/hooks/use-geolocation.ts +63 -0
  105. package/src/hooks/use-in-view.ts +9 -11
  106. package/src/hooks/use-intersection-observer.ts +19 -0
  107. package/src/hooks/use-modal-in-container.ts +60 -52
  108. package/src/hooks/use-modal-transition.ts +54 -0
  109. package/src/hooks/use-modal.ts +21 -0
  110. package/src/hooks/use-mouse-position.ts +55 -7
  111. package/src/hooks/use-resize-observer.ts +18 -0
  112. package/src/stories/GettingStarted.mdx +2 -6
  113. package/svg/Name=npm, Category=social.svg +3 -0
  114. package/tsconfig.json +1 -0
  115. package/vite.config.ts +1 -0
  116. package/.turbo/daemon/f5c5c8fb195b01d0-turbo.log.2024-05-26 +0 -0
  117. package/.turbo/turbo-build$colon$watch.log +0 -96
  118. package/.turbo/turbo-build-storybook.log +0 -0
  119. package/.turbo/turbo-lint$colon$fix.log +0 -2
  120. package/dist/chunk-M4KRSYE7.js +0 -3
  121. package/dist/chunk-M4KRSYE7.js.map +0 -1
  122. package/dist/chunk-U5XSMSKZ.js +0 -3
  123. package/dist/chunk-U5XSMSKZ.js.map +0 -1
  124. package/dist/get-modal-position-DPftPoU2.d.cts +0 -28
  125. package/dist/get-modal-position-DPftPoU2.d.ts +0 -28
  126. package/src/components/form/select/select-option.tsx +0 -84
  127. package/src/hooks/use-observer.ts +0 -18
  128. 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
- style?: React.CSSProperties;
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
- modalRef: React.RefObject<HTMLElement>;
62
+ containerRef: React.RefObject<HTMLElement>;
64
63
  Component: React.FC<OptionComponentProps<T>>;
65
64
  multiselect?: boolean;
66
65
  value: SelectValue<T>;
67
- variant: `${SelectOptionVariant}`;
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<'codepen' | 'facebook' | 'github' | 'instagram' | 'linkedin' | 'whatsapp', IconT>;
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 => (
@@ -1,6 +1,7 @@
1
1
  export * from './accordion';
2
2
  export * from './buttons';
3
3
  export * from './cards';
4
+ export * from './cursor';
4
5
  export * from './form';
5
6
  export * from './icon';
6
7
  export * from './image';
@@ -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 orientation={LineOrientation.VERTICAL} />
31
+ <Story args={{ ...args, orientation: LineOrientation.VERTICAL }} />
33
32
  <Typography variant='small' noPadding>
34
33
  Horizontal
35
34
  </Typography>
36
- <Story orientation={LineOrientation.HORIZONTAL} />
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 isOpen={isOpen} onClose={() => setIsOpen(false)} />
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 Default: Story = {
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
- icon: 'user',
49
+ noPadding: true,
64
50
  },
65
- render: (args, { isOpen, onClose }) => {
51
+ render: args => {
66
52
  return (
67
- <ActionModal {...args} isOpen={isOpen} onClose={onClose}>
68
- <Typography variant='header4' align='center' noPadding>
53
+ <ActionModal {...args}>
54
+ <Typography variant='header4' noPadding style={{ padding: '0 1rem' }}>
69
55
  Action modal
70
56
  </Typography>
71
- <Typography>Are you sure want to execute an action?</Typography>
72
- <Grid ai='center' gtc='repeat(2, minmax(100px, 1fr))' jc='center' gap='1em' style={{ margin: 'auto' }}>
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={() => alert('Action button clicked')}
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={() => alert('Action button clicked')}
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 Classic: Story = {
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
- noPadding: true,
107
+ icon: 'user',
102
108
  },
103
- render: (args, { isOpen, onClose }) => {
109
+ render: args => {
104
110
  return (
105
- <ActionModal {...args} isOpen={isOpen} onClose={onClose}>
106
- <Typography variant='header4' noPadding style={{ padding: '0 1rem' }}>
111
+ <ActionModal {...args}>
112
+ <Typography variant='header4' align='center' noPadding>
107
113
  Action modal
108
114
  </Typography>
109
- <Line />
110
- <Grid gap='1em' style={{ padding: '0 1rem 1rem' }}>
111
- <Typography>Are you sure want to execute an action?</Typography>
112
- </Grid>
113
- <Grid ai='center' gtc='repeat(2, 80px)' jc='end' gap='1em' style={{ padding: '0 1rem 1rem' }}>
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={() => alert('Action button clicked')}
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={() => alert('Action button clicked')}
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
- export const ActionModalContainerStyle = styled.section`
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
- .open-animation {
11
- animation: bounceIn 500ms ease;
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
- .shake-animation {
20
- animation: headShake 600ms linear;
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: 2em;
89
- height: 2em;
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
- left: 100%;
98
- bottom: 100%;
99
- font-size: ${props => props.theme.constants.typography.small.fontSize};
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 { Modal } from '../modal';
6
+ import { ModalProps } from '../modal';
7
7
 
8
- import { ActionModalContainerStyle, ActionModalStyle } from './action-modal.style';
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
- export type ActionModalProps = {
13
- children: React.ReactNode;
14
- isOpen: boolean;
13
+ type ActionModalContextType = {
15
14
  onClose: () => void;
16
- actionRequired?: boolean;
17
- icon?: IconNameT;
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
- type ActionModalContextEntity = {
27
- onClose: () => void;
28
- };
29
-
30
- const ActionModalContext = createContext<ActionModalContextEntity | null>(null);
19
+ const ActionModalContext = createContext<ActionModalContextType | null>(null);
31
20
 
32
- const useActionModal = () => {
21
+ const useActionModalContext = () => {
33
22
  const context = useContext(ActionModalContext);
34
23
 
35
- if (context === null) {
36
- throw new Error('useActionModal must be used with in a ActionModal');
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
- <Modal
87
- id='action-modal'
88
- opacity={0.8}
89
- isOpen={isOpen}
90
- zIndex={9999}
91
- onClick={actionRequired ? remainAction : () => handleOnClose(onClose)}
92
- >
93
- <ActionModalContext.Provider value={{ onClose: () => handleOnClose(onClose) }}>
94
- <ActionModalContainerStyle>
95
- <section ref={ref} className='open-animation'>
96
- <ActionModalStyle className={actionModalClassName}>
97
- {!noCloseButton && !actionRequired && (
98
- <section className='close-modal-button' onClick={() => handleOnClose(onClose)}>
99
- <Icon name='cross' />
100
- </section>
101
- )}
102
- {icon ? (
103
- <Typography variant='header4' className='action-modal-icon'>
104
- <Icon name={icon} />
105
- </Typography>
106
- ) : null}
107
- <section className='action-modal-body'>
108
- <section className={contentClassName} style={style}>
109
- {children}
110
- </section>
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
- </ActionModalStyle>
113
- </section>
114
- </ActionModalContainerStyle>
115
- </ActionModalContext.Provider>
116
- </Modal>
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 } = useActionModal();
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 { Button } from '../../buttons';
4
- import { Image } from '../../image';
5
- import { Typography } from '../../typography';
3
+ import { AsideModal } from './aside-modal';
6
4
 
7
- import { AsideModal, AsidePosition } from './aside-modal';
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
- isOpen: { control: false },
16
- onClose: { control: false },
17
- position: { control: 'inline-radio', options: Object.values(AsidePosition) },
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 isOpen={isOpen} onClose={() => setIsOpen(false)} />
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: (args, { isOpen, onClose }) => {
47
+ render: args => {
52
48
  return (
53
- <AsideModal {...args} isOpen={isOpen} onClose={onClose}>
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