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
@@ -0,0 +1,325 @@
1
+ import { Button } from '../../buttons';
2
+
3
+ import { Modal as ModalComponent, ModalProps } from './modal';
4
+
5
+ import { PositionContainer } from '@polpo/helpers';
6
+ import { useModal } from '@polpo/hooks';
7
+ import { Grid, Line, ModalBackdrop, Typography } from '@polpo/ui';
8
+
9
+ import type { Meta, StoryObj } from '@storybook/react';
10
+
11
+ const Modal = ({ children, ...props }: ModalProps) => (
12
+ <ModalComponent backdrop='none' {...props} style={{ borderRadius: '1em' }}>
13
+ <Grid style={{ width: '400px', maxWidth: '100%', padding: '1em' }}>
14
+ <Typography variant='header4' color='primary'>
15
+ {children}
16
+ </Typography>
17
+ <Line />
18
+ <Typography>
19
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. At, commodi dolore, doloremque doloribus esse
20
+ excepturi impedit in, ipsam ipsum iste magni nihil officiis quasi quia quod similique tenetur? Aliquid cum dolor
21
+ incidunt nihil?
22
+ </Typography>
23
+ </Grid>
24
+ </ModalComponent>
25
+ );
26
+
27
+ const meta: Meta<typeof ModalComponent> = {
28
+ title: 'Modals/Modal',
29
+ component: ModalComponent,
30
+ argTypes: {
31
+ animation: { control: { type: 'inline-radio', options: ['none', 'fade-down', 'bounce'] } },
32
+ windowOffset: { control: { type: 'range', min: 0, max: 100 } },
33
+ offset: { control: { type: 'range', min: 0, max: 100 } },
34
+ position: { control: 'inline-radio', options: Object.values(PositionContainer) },
35
+ closeOnClickOutside: { control: 'boolean' },
36
+ opacity: { control: { type: 'range', min: 0, max: 1, step: 0.1 } },
37
+ backdrop: { control: { type: 'inline-radio', options: Object.values(ModalBackdrop) } },
38
+ transitionDuration: { control: false },
39
+ backdropOnClick: { control: false },
40
+ zIndex: { control: false },
41
+ id: { control: false },
42
+ children: { control: false },
43
+ isOpen: { control: false },
44
+ onClose: { control: false },
45
+ className: { control: false },
46
+ style: { control: false },
47
+ rootStyle: { control: false },
48
+ closeAnimationClassName: { control: false },
49
+ containerRef: { control: false },
50
+ modalRef: { control: false },
51
+ },
52
+ args: {
53
+ offset: 20,
54
+ windowOffset: 10,
55
+ },
56
+ };
57
+
58
+ export default meta;
59
+ type Story = StoryObj<typeof ModalComponent>;
60
+
61
+ export const AllContainerPositions: Story = {
62
+ argTypes: {
63
+ position: { control: false },
64
+ },
65
+ render: args => {
66
+ const modal1 = useModal<HTMLButtonElement>();
67
+ const modal2 = useModal<HTMLButtonElement>();
68
+ const modal3 = useModal<HTMLButtonElement>();
69
+ const modal4 = useModal<HTMLButtonElement>();
70
+ const modal5 = useModal<HTMLButtonElement>();
71
+ const modal6 = useModal<HTMLButtonElement>();
72
+ const modal7 = useModal<HTMLButtonElement>();
73
+ const modal8 = useModal<HTMLButtonElement>();
74
+ const modal9 = useModal<HTMLButtonElement>();
75
+ const modal10 = useModal<HTMLButtonElement>();
76
+ const modal11 = useModal<HTMLButtonElement>();
77
+ const modal12 = useModal<HTMLButtonElement>();
78
+
79
+ return (
80
+ <Grid gtc='repeat(3, 1fr)' gap='20px'>
81
+ <Button width='full' ref={modal1.containerRef} onClick={modal1.openModal}>
82
+ top left
83
+ </Button>
84
+ <Modal
85
+ {...args}
86
+ containerRef={modal1.containerRef}
87
+ id='container-modal'
88
+ isOpen={modal1.isOpen}
89
+ onClose={modal1.closeModal}
90
+ position='top left'
91
+ >
92
+ top left
93
+ </Modal>
94
+ <Button width='full' ref={modal2.containerRef} onClick={modal2.openModal}>
95
+ top center
96
+ </Button>
97
+ <Modal
98
+ {...args}
99
+ containerRef={modal2.containerRef}
100
+ id='container-modal'
101
+ isOpen={modal2.isOpen}
102
+ onClose={modal2.closeModal}
103
+ position='top center'
104
+ >
105
+ top center
106
+ </Modal>
107
+ <Button width='full' ref={modal3.containerRef} onClick={modal3.openModal}>
108
+ top right
109
+ </Button>
110
+ <Modal
111
+ {...args}
112
+ containerRef={modal3.containerRef}
113
+ id='container-modal'
114
+ isOpen={modal3.isOpen}
115
+ onClose={modal3.closeModal}
116
+ position='top right'
117
+ >
118
+ top right
119
+ </Modal>
120
+
121
+ <Button width='full' ref={modal4.containerRef} onClick={modal4.openModal}>
122
+ left top
123
+ </Button>
124
+ <Modal
125
+ {...args}
126
+ containerRef={modal4.containerRef}
127
+ id='container-modal'
128
+ isOpen={modal4.isOpen}
129
+ onClose={modal4.closeModal}
130
+ position='left top'
131
+ >
132
+ left top
133
+ </Modal>
134
+ <span />
135
+ <Button width='full' ref={modal5.containerRef} onClick={modal5.openModal}>
136
+ right top
137
+ </Button>
138
+ <Modal
139
+ {...args}
140
+ containerRef={modal5.containerRef}
141
+ id='container-modal'
142
+ isOpen={modal5.isOpen}
143
+ onClose={modal5.closeModal}
144
+ position='right top'
145
+ >
146
+ right top
147
+ </Modal>
148
+
149
+ <Button width='full' ref={modal6.containerRef} onClick={modal6.openModal}>
150
+ left center
151
+ </Button>
152
+ <Modal
153
+ {...args}
154
+ containerRef={modal6.containerRef}
155
+ id='container-modal'
156
+ isOpen={modal6.isOpen}
157
+ onClose={modal6.closeModal}
158
+ position='left center'
159
+ >
160
+ left center
161
+ </Modal>
162
+ <span />
163
+ <Button width='full' ref={modal7.containerRef} onClick={modal7.openModal}>
164
+ right center
165
+ </Button>
166
+ <Modal
167
+ {...args}
168
+ containerRef={modal7.containerRef}
169
+ id='container-modal'
170
+ isOpen={modal7.isOpen}
171
+ onClose={modal7.closeModal}
172
+ position='right center'
173
+ >
174
+ right center
175
+ </Modal>
176
+
177
+ <Button width='full' ref={modal8.containerRef} onClick={modal8.openModal}>
178
+ left bottom
179
+ </Button>
180
+ <Modal
181
+ {...args}
182
+ containerRef={modal8.containerRef}
183
+ id='container-modal'
184
+ isOpen={modal8.isOpen}
185
+ onClose={modal8.closeModal}
186
+ position='left bottom'
187
+ >
188
+ left bottom
189
+ </Modal>
190
+ <span />
191
+ <Button width='full' ref={modal9.containerRef} onClick={modal9.openModal}>
192
+ right bottom
193
+ </Button>
194
+ <Modal
195
+ {...args}
196
+ containerRef={modal9.containerRef}
197
+ id='container-modal'
198
+ isOpen={modal9.isOpen}
199
+ onClose={modal9.closeModal}
200
+ position='right bottom'
201
+ >
202
+ right bottom
203
+ </Modal>
204
+
205
+ <Button width='full' ref={modal10.containerRef} onClick={modal10.openModal}>
206
+ bottom left
207
+ </Button>
208
+ <Modal
209
+ {...args}
210
+ containerRef={modal10.containerRef}
211
+ id='container-modal'
212
+ isOpen={modal10.isOpen}
213
+ onClose={modal10.closeModal}
214
+ position='bottom left'
215
+ >
216
+ bottom left
217
+ </Modal>
218
+ <Button width='full' ref={modal11.containerRef} onClick={modal11.openModal}>
219
+ bottom center
220
+ </Button>
221
+ <Modal
222
+ {...args}
223
+ containerRef={modal11.containerRef}
224
+ id='container-modal'
225
+ isOpen={modal11.isOpen}
226
+ onClose={modal11.closeModal}
227
+ position='bottom center'
228
+ >
229
+ bottom center
230
+ </Modal>
231
+ <Button width='full' ref={modal12.containerRef} onClick={modal12.openModal}>
232
+ bottom right
233
+ </Button>
234
+ <Modal
235
+ {...args}
236
+ containerRef={modal12.containerRef}
237
+ id='container-modal'
238
+ isOpen={modal12.isOpen}
239
+ onClose={modal12.closeModal}
240
+ position='bottom right'
241
+ >
242
+ bottom right
243
+ </Modal>
244
+ </Grid>
245
+ );
246
+ },
247
+ };
248
+
249
+ export const AllScreenPositions: Story = {
250
+ argTypes: {
251
+ position: { control: false },
252
+ },
253
+ render: args => {
254
+ const modal1 = useModal();
255
+ const modal2 = useModal();
256
+ const modal3 = useModal();
257
+ const modal4 = useModal();
258
+ const modal5 = useModal();
259
+ const modal6 = useModal();
260
+ const modal7 = useModal();
261
+ const modal8 = useModal();
262
+ const modal9 = useModal();
263
+
264
+ return (
265
+ <Grid gtc='repeat(3, 1fr)' gap='20px'>
266
+ <Button width='full' onClick={modal1.openModal}>
267
+ top left
268
+ </Button>
269
+ <Modal {...args} id='screen-modal' isOpen={modal1.isOpen} onClose={modal1.closeModal} position='top left'>
270
+ top left
271
+ </Modal>
272
+ <Button width='full' onClick={modal2.openModal}>
273
+ top center
274
+ </Button>
275
+ <Modal {...args} id='screen-modal' isOpen={modal2.isOpen} onClose={modal2.closeModal} position='top center'>
276
+ top center
277
+ </Modal>
278
+ <Button width='full' onClick={modal3.openModal}>
279
+ top right
280
+ </Button>
281
+ <Modal {...args} id='screen-modal' isOpen={modal3.isOpen} onClose={modal3.closeModal} position='top right'>
282
+ top right
283
+ </Modal>
284
+
285
+ <Button width='full' onClick={modal4.openModal}>
286
+ left center
287
+ </Button>
288
+ <Modal {...args} id='screen-modal' isOpen={modal4.isOpen} onClose={modal4.closeModal} position='left center'>
289
+ left center
290
+ </Modal>
291
+ <Button width='full' onClick={modal5.openModal}>
292
+ center
293
+ </Button>
294
+ <Modal {...args} id='screen-modal' isOpen={modal5.isOpen} onClose={modal5.closeModal} position='center'>
295
+ center
296
+ </Modal>
297
+ <Button width='full' onClick={modal6.openModal}>
298
+ right center
299
+ </Button>
300
+ <Modal {...args} id='screen-modal' isOpen={modal6.isOpen} onClose={modal6.closeModal} position='right center'>
301
+ right center
302
+ </Modal>
303
+
304
+ <Button width='full' onClick={modal7.openModal}>
305
+ bottom left
306
+ </Button>
307
+ <Modal {...args} id='screen-modal' isOpen={modal7.isOpen} onClose={modal7.closeModal} position='bottom left'>
308
+ bottom left
309
+ </Modal>
310
+ <Button width='full' onClick={modal8.openModal}>
311
+ bottom center
312
+ </Button>
313
+ <Modal {...args} id='screen-modal' isOpen={modal8.isOpen} onClose={modal8.closeModal} position='bottom center'>
314
+ bottom center
315
+ </Modal>
316
+ <Button width='full' onClick={modal9.openModal}>
317
+ bottom right
318
+ </Button>
319
+ <Modal {...args} id='screen-modal' isOpen={modal9.isOpen} onClose={modal9.closeModal} position='bottom right'>
320
+ bottom right
321
+ </Modal>
322
+ </Grid>
323
+ );
324
+ },
325
+ };
@@ -1,10 +1,70 @@
1
1
  import styled from 'styled-components';
2
2
 
3
- export const ModalOverlayStyle = styled.section`
4
- animation: fadeIn 300ms ease;
3
+ export const ModalStyle = styled.section`
4
+ position: fixed;
5
+ z-index: 1001;
6
+ pointer-events: none;
7
+ `;
8
+
9
+ export const ModalContentStyle = styled.section`
10
+ background: ${props => props.theme.colors.background.paper};
11
+ pointer-events: initial;
12
+
13
+ &.animation-fade-down {
14
+ animation: fadeInDown 200ms ease;
15
+
16
+ &.modal-close {
17
+ animation: fadeOutUp 200ms ease;
18
+ transform: translateY(-10px);
19
+ opacity: 0;
20
+ }
21
+ }
22
+
23
+ &.animation-bounce {
24
+ animation: bounceIn 500ms ease;
25
+
26
+ &.modal-close {
27
+ animation: bounceOut 500ms ease;
28
+ transform: scale3d(0.3, 0.3, 0.3);
29
+ opacity: 0;
30
+ }
31
+ }
32
+ `;
33
+
34
+ export const BackdropStyle = styled.section`
5
35
  position: fixed;
6
36
  width: 100%;
7
37
  height: 100%;
8
38
  top: 0;
9
39
  left: 0;
40
+ z-index: 1000;
41
+ animation: backdropOpen 500ms cubic-bezier(0.215, 0.61, 0.355, 1);
42
+
43
+ &.backdrop-close {
44
+ animation: backdropClose 500ms cubic-bezier(0.215, 0.61, 0.355, 1);
45
+ opacity: 0;
46
+ transform: translateY(-10px);
47
+ }
48
+
49
+ @keyframes backdropOpen {
50
+ from {
51
+ opacity: 0;
52
+ transform: translateY(-10px);
53
+ }
54
+ to {
55
+ opacity: 1;
56
+ transform: translateY(0);
57
+ }
58
+ }
59
+
60
+ @keyframes backdropClose {
61
+ from {
62
+ opacity: 1;
63
+ transform: translateY(0);
64
+ }
65
+ to {
66
+ opacity: 0;
67
+ transform: translateY(-10px);
68
+ }
69
+ }
10
70
  `;
@@ -1,132 +1,91 @@
1
- import React, { ForwardedRef, forwardRef, useEffect, useMemo } from 'react';
2
- import ReactDOM from 'react-dom';
3
- import { useTheme } from 'styled-components';
4
-
5
- import { ModalOverlayStyle } from './modal.style';
6
-
7
- import { useConstant } from '@polpo/hooks';
8
-
9
- export enum ModalBackdrop {
10
- OPAQUE = 'opaque',
11
- TRANSPARENT = 'transparent',
12
- BLUR = 'blur',
13
- NONE = 'none',
14
- }
15
-
16
- export type ModalCommonProps = {
17
- isOpen: boolean;
18
- children: React.ReactNode;
19
- id: string;
20
- };
21
-
22
- export type ModalNoOverlayProps = ModalCommonProps & {
23
- opacity?: never;
24
- backdrop?: never;
25
- onClick?: never;
26
- zIndex?: never;
27
- className?: never;
28
- style?: never;
29
- };
30
-
31
- export type ModalOverlayProps = ModalCommonProps & {
32
- opacity?: number;
33
- backdrop?: `${ModalBackdrop}`;
34
- onClick?: () => void;
35
- zIndex?: React.CSSProperties['zIndex'];
36
- className?: string;
37
- style?: React.CSSProperties;
38
- };
39
-
40
- export type ModalProps = ModalOverlayProps | ModalNoOverlayProps;
41
-
42
- export const ModalComponent = (
43
- {
44
- isOpen,
45
- children,
46
- id,
47
- opacity = 0.6,
48
- backdrop = ModalBackdrop.BLUR,
49
- onClick,
50
- zIndex,
51
- className = '',
52
- style = {},
53
- }: ModalProps,
54
- ref: ForwardedRef<HTMLElement>,
55
- ) => {
56
- const containerID = useConstant(`modal-${id}`);
57
- const theme = useTheme();
58
-
59
- useEffect(() => {
60
- let modalContainer = document.getElementById(containerID);
61
-
62
- if (!modalContainer) {
63
- modalContainer = document.createElement('div');
64
- modalContainer.setAttribute('id', containerID);
65
-
66
- if (typeof ref === 'function') {
67
- ref(modalContainer);
68
- } else if (ref) {
69
- ref.current = modalContainer;
70
- }
71
-
72
- document.body.appendChild(modalContainer);
1
+ import React, { CSSProperties, useLayoutEffect, useRef } from 'react';
2
+
3
+ import { Backdrop, BackdropProps } from './backdrop';
4
+ import { ModalContentStyle, ModalStyle } from './modal.style';
5
+
6
+ import { ModalState, useClassNames, useModalInContainer, UseModalInContainerParams } from '@polpo/hooks';
7
+ import { Portal } from '@polpo/ui';
8
+
9
+ export type ModalProps = Omit<BackdropProps, 'modalState'> &
10
+ Omit<UseModalInContainerParams, 'modalRef' | 'onClose'> & {
11
+ id: string;
12
+ children: React.ReactNode;
13
+ isOpen: boolean;
14
+ onClose: () => void;
15
+ className?: string;
16
+ style?: React.CSSProperties;
17
+ rootStyle?: CSSProperties;
18
+ animation?: 'none' | 'fade-down' | 'bounce';
19
+ closeAnimationClassName?: string;
20
+ modalRef?: React.RefObject<HTMLElement>;
21
+ };
22
+
23
+ export const Modal = ({
24
+ id,
25
+ children,
26
+ isOpen,
27
+ onClose,
28
+ className = '',
29
+ style = {},
30
+ rootStyle = {},
31
+ animation = 'fade-down',
32
+ closeAnimationClassName = 'modal-close',
33
+ modalRef: modalRefProp,
34
+ closeOnClickOutside,
35
+ transitionDuration = 300,
36
+ windowOffset = 10,
37
+ offset = 20,
38
+ position,
39
+ containerRef,
40
+ zIndex = 1000,
41
+ ...backdropProps
42
+ }: ModalProps) => {
43
+ const modalRef = useRef<HTMLElement>(null);
44
+ const { openModal, closeModal, modalState, isVisible } = useModalInContainer({
45
+ modalRef: modalRefProp ?? modalRef,
46
+ containerRef,
47
+ closeOnClickOutside,
48
+ offset,
49
+ windowOffset,
50
+ position,
51
+ transitionDuration,
52
+ onClose,
53
+ });
54
+
55
+ const modalContentClassName = useClassNames({
56
+ [className]: true,
57
+ [`animation-${animation}`]: Boolean(animation) && animation !== 'none',
58
+ [closeAnimationClassName]: modalState === ModalState.CLOSING || modalState === ModalState.CLOSED,
59
+ });
60
+
61
+ useLayoutEffect(() => {
62
+ if (modalState === ModalState.CLOSED && isOpen) {
63
+ openModal();
64
+ } else if (modalState === ModalState.OPEN && !isOpen) {
65
+ closeModal();
73
66
  }
67
+ }, [isOpen, openModal, closeModal, modalState]);
74
68
 
75
- /*
76
- * return () => {
77
- * if (modalContainer.parentNode === document.body) {
78
- * document.body.removeChild(modalContainer);
79
- * }
80
- * };
81
- */
82
- }, [containerID, ref]);
83
-
84
- const backgroundStyles = useMemo(() => {
85
- switch (backdrop) {
86
- case ModalBackdrop.OPAQUE:
87
- return {
88
- background: `${theme.colors.background.paper}${(opacity * 255)?.toString(16)}`,
89
- };
90
- case ModalBackdrop.TRANSPARENT:
91
- return {
92
- background: 'transparent',
93
- };
94
- case ModalBackdrop.BLUR:
95
- return {
96
- background: `${theme.colors.background.paper}${(opacity * 255)?.toString(16)}`,
97
- backdropFilter: 'blur(5px)',
98
- };
99
- case ModalBackdrop.NONE:
100
- return {
101
- display: 'none',
102
- };
103
- default:
104
- return {};
105
- }
106
- }, [backdrop, theme.colors.background.paper, opacity]);
107
-
108
- const root = document.getElementById(containerID);
109
-
110
- if (!isOpen || !root) {
69
+ if (!isVisible) {
111
70
  return null;
112
71
  }
113
72
 
114
- return ReactDOM.createPortal(
115
- <>
116
- <ModalOverlayStyle
117
- tabIndex={-1}
118
- onClick={onClick}
119
- className={className}
73
+ return (
74
+ <Portal id={`modal-${id}`}>
75
+ <Backdrop {...backdropProps} modalState={modalState} zIndex={zIndex} />
76
+ <ModalStyle
77
+ ref={modalRefProp ?? modalRef}
120
78
  style={{
121
- zIndex,
122
- ...backgroundStyles,
123
- ...style,
79
+ maxWidth: `calc(100dvw - ${windowOffset * 2}px)`,
80
+ maxHeight: `calc(100dvh - ${windowOffset * 2}px)`,
81
+ ...rootStyle,
82
+ zIndex: +zIndex + 1,
124
83
  }}
125
- />
126
- {children}
127
- </>,
128
- root,
84
+ >
85
+ <ModalContentStyle style={style} className={modalContentClassName}>
86
+ {children}
87
+ </ModalContentStyle>
88
+ </ModalStyle>
89
+ </Portal>
129
90
  );
130
91
  };
131
-
132
- export const Modal = forwardRef(ModalComponent);
@@ -0,0 +1 @@
1
+ export * from './portal';
@@ -0,0 +1,18 @@
1
+ import { ForwardedRef, forwardRef, ReactNode, useMemo } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+
4
+ import { useDomContainer } from '@polpo/hooks';
5
+
6
+ type PortalProps = {
7
+ id: string;
8
+ children: ReactNode;
9
+ };
10
+
11
+ const PortalComponent = ({ children, id }: PortalProps, ref: ForwardedRef<HTMLElement>) => {
12
+ const uuid = useMemo(() => crypto.randomUUID(), []);
13
+ const root = useDomContainer(`${id}-${uuid}`, ref);
14
+
15
+ return createPortal(children, root);
16
+ };
17
+
18
+ export const Portal = forwardRef(PortalComponent);
@@ -1,13 +1,13 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useTheme } from 'styled-components';
3
3
 
4
- import { ThemeColor } from '../../contexts';
5
- import { RadiusVariants, SizeVariants, useRadiusClassName, useSizeClassName } from '../../core/variants';
4
+ import { RadiusVariants, SizeVariants } from '../../core/variants';
6
5
 
7
6
  import { Tabs } from './tabs';
8
- import { TabListStyle, TabListStyleProps } from './tabs.style';
7
+ import { TabListStyle, TabListColorStyle } from './tabs.style';
9
8
 
10
9
  import { useClassNames } from '@polpo/hooks';
10
+ import { ThemeColor } from '@polpo/ui';
11
11
 
12
12
  const DefaultRect = {
13
13
  top: 0,
@@ -34,7 +34,7 @@ export enum TabListDirection {
34
34
  VERTICAL = 'vertical',
35
35
  }
36
36
 
37
- const getColor = (color?: ThemeColor): TabListStyleProps | null => {
37
+ const getColor = (color?: ThemeColor): TabListColorStyle | null => {
38
38
  if (color) {
39
39
  return {
40
40
  $color: color.main,
@@ -78,11 +78,7 @@ export const TabsList = ({
78
78
  const selectedTabRef = useRef<HTMLSpanElement>(null);
79
79
  const [isSelectorActive, setIsSelectorActive] = useState(false);
80
80
  const [selector, setSelector] = useState(DefaultRect);
81
- const tabRadius = useRadiusClassName(radius);
82
- const tabSize = useSizeClassName(size);
83
81
  const containerClassNames = useClassNames({
84
- [tabRadius]: true,
85
- [tabSize]: true,
86
82
  'solid-variant': variant === TabListVariant.SOLID,
87
83
  'ghost-variant': variant === TabListVariant.GHOST,
88
84
  'flat-variant': variant === TabListVariant.FLAT,
@@ -112,13 +108,20 @@ export const TabsList = ({
112
108
  }
113
109
  }, [isSelectorActive, variant, openTab]);
114
110
 
115
- const containerColors: TabListStyleProps = (color && getColor(theme.colors[color])) || {
111
+ const containerColors: TabListColorStyle = (color && getColor(theme.colors[color])) || {
116
112
  $color: theme.colors.text.main,
117
113
  $colorContrast: theme.colors.background.main,
118
114
  };
119
115
 
120
116
  return (
121
- <TabListStyle {...containerColors} className={containerClassNames} ref={containerRef} style={style}>
117
+ <TabListStyle
118
+ {...containerColors}
119
+ className={containerClassNames}
120
+ ref={containerRef}
121
+ style={style}
122
+ $size={size}
123
+ $radius={radius}
124
+ >
122
125
  {Boolean(variant) && <span className={`tabs-selector ${isSelectorActive ? 'active' : ''}`} style={selector} />}
123
126
  {tabs.map(({ id, label }) => (
124
127
  <Tabs.Tab key={id} id={id} ref={id === openTab ? selectedTabRef : null}>