polpo 0.1.13 → 0.1.16

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 (204) hide show
  1. package/.turbo/cache/eslint/.cache_16wwsjl +1 -0
  2. package/.turbo/cache/eslint/.cache_rpznjo +1 -0
  3. package/.turbo/turbo-build$colon$code.log +49 -0
  4. package/.turbo/turbo-build$colon$css.log +2 -0
  5. package/.turbo/turbo-build.log +43 -0
  6. package/.turbo/turbo-lint.log +2 -0
  7. package/dist/components.cjs +1 -3655
  8. package/dist/components.css +2 -2173
  9. package/dist/components.js +1 -3576
  10. package/dist/helpers.cjs +1 -311
  11. package/dist/helpers.d.cts +4 -1
  12. package/dist/helpers.d.ts +4 -1
  13. package/dist/helpers.js +1 -276
  14. package/dist/hooks.cjs +1 -1065
  15. package/dist/hooks.js +1 -998
  16. package/dist/layouts.cjs +1 -146
  17. package/dist/layouts.css +2 -5
  18. package/dist/layouts.js +1 -119
  19. package/dist/types.cjs +1 -19
  20. package/dist/types.js +0 -1
  21. package/eslint.config.js +4 -0
  22. package/package.json +16 -11
  23. package/postcss.config.js +3 -0
  24. package/src/components/accordion/accordion-item.tsx +123 -0
  25. package/src/components/accordion/accordion.component.tsx +80 -0
  26. package/src/components/accordion/accordion.styles.css +41 -0
  27. package/src/components/accordion/index.ts +2 -0
  28. package/src/components/button/button.component.tsx +50 -0
  29. package/src/components/button/button.styles.css +317 -0
  30. package/src/components/button/index.ts +1 -0
  31. package/src/components/cards/flip-card/flip-card.component.tsx +66 -0
  32. package/src/components/cards/flip-card/flip-card.styles.css +50 -0
  33. package/src/components/cards/flip-card/index.ts +1 -0
  34. package/src/components/cards/hover-card/hover-card.component.tsx +76 -0
  35. package/src/components/cards/hover-card/hover-card.styles.css +13 -0
  36. package/src/components/cards/hover-card/index.ts +1 -0
  37. package/src/components/cards/index.ts +3 -0
  38. package/src/components/cards/slide-card/index.ts +1 -0
  39. package/src/components/cards/slide-card/slide-card.component.tsx +27 -0
  40. package/src/components/component.types.ts +31 -0
  41. package/src/components/cursor/cursor.component.tsx +49 -0
  42. package/src/components/cursor/cursor.styles.css +73 -0
  43. package/src/components/cursor/index.ts +1 -0
  44. package/src/components/form/checkbox/checkbox.component.tsx +95 -0
  45. package/src/components/form/checkbox/checkbox.styles.css +162 -0
  46. package/src/components/form/checkbox/index.ts +1 -0
  47. package/src/components/form/controller/controller.component.tsx +42 -0
  48. package/src/components/form/controller/index.ts +1 -0
  49. package/src/components/form/date-picker/date-picker.component.tsx +66 -0
  50. package/src/components/form/date-picker/index.ts +1 -0
  51. package/src/components/form/field/field.component.tsx +84 -0
  52. package/src/components/form/field/field.styles.css +98 -0
  53. package/src/components/form/field/field.types.ts +34 -0
  54. package/src/components/form/field/index.ts +2 -0
  55. package/src/components/form/form.types.ts +37 -0
  56. package/src/components/form/index.ts +14 -0
  57. package/src/components/form/input/index.ts +1 -0
  58. package/src/components/form/input/input.component.tsx +75 -0
  59. package/src/components/form/input-color/index.ts +1 -0
  60. package/src/components/form/input-color/input-color.component.tsx +170 -0
  61. package/src/components/form/input-color/input-color.styles.css +92 -0
  62. package/src/components/form/input-file/index.ts +1 -0
  63. package/src/components/form/input-file/input-file.component.tsx +221 -0
  64. package/src/components/form/input-file/input-file.styles.css +143 -0
  65. package/src/components/form/input-number/index.ts +1 -0
  66. package/src/components/form/input-number/input-number.component.tsx +144 -0
  67. package/src/components/form/input-password/index.ts +1 -0
  68. package/src/components/form/input-password/input-password.component.tsx +77 -0
  69. package/src/components/form/radio/index.ts +1 -0
  70. package/src/components/form/radio/radio.component.tsx +92 -0
  71. package/src/components/form/radio/radio.styles.css +117 -0
  72. package/src/components/form/select/index.ts +1 -0
  73. package/src/components/form/select/option.tsx +101 -0
  74. package/src/components/form/select/options.tsx +165 -0
  75. package/src/components/form/select/select.component.tsx +317 -0
  76. package/src/components/form/select/select.styles.css +115 -0
  77. package/src/components/form/select/select.types.ts +97 -0
  78. package/src/components/form/slider/index.ts +1 -0
  79. package/src/components/form/slider/slider.component.tsx +117 -0
  80. package/src/components/form/slider/slider.styles.css +94 -0
  81. package/src/components/form/switch/index.ts +1 -0
  82. package/src/components/form/switch/switch.component.tsx +166 -0
  83. package/src/components/form/switch/switch.styles.css +165 -0
  84. package/src/components/form/textarea/index.ts +1 -0
  85. package/src/components/form/textarea/textarea.component.tsx +80 -0
  86. package/src/components/form/textarea/textarea.styles.css +7 -0
  87. package/src/components/image/image.tsx +7 -0
  88. package/src/components/image/index.ts +1 -0
  89. package/src/components/index.ts +15 -0
  90. package/src/components/infinity-scroll/index.ts +1 -0
  91. package/src/components/infinity-scroll/infinity-scroll.component.tsx +51 -0
  92. package/src/components/infinity-scroll/infinity-scroll.styles.css +25 -0
  93. package/src/components/line/index.ts +1 -0
  94. package/src/components/line/line.component.tsx +77 -0
  95. package/src/components/line/line.styles.css +61 -0
  96. package/src/components/modals/action-modal/action-modal.component.tsx +150 -0
  97. package/src/components/modals/action-modal/action-modal.styles.css +115 -0
  98. package/src/components/modals/action-modal/index.ts +1 -0
  99. package/src/components/modals/aside-modal/aside-modal.component.tsx +63 -0
  100. package/src/components/modals/aside-modal/aside-modal.styles.css +86 -0
  101. package/src/components/modals/aside-modal/index.ts +1 -0
  102. package/src/components/modals/confirmation-modal/confirmation-modal.component.tsx +43 -0
  103. package/src/components/modals/confirmation-modal/confirmation-modal.styles.css +17 -0
  104. package/src/components/modals/confirmation-modal/index.ts +1 -0
  105. package/src/components/modals/index.ts +6 -0
  106. package/src/components/modals/menu/index.ts +1 -0
  107. package/src/components/modals/menu/menu.component.tsx +194 -0
  108. package/src/components/modals/menu/menu.styles.css +101 -0
  109. package/src/components/modals/modal/index.ts +2 -0
  110. package/src/components/modals/modal/modal.backdrop.tsx +67 -0
  111. package/src/components/modals/modal/modal.component.tsx +104 -0
  112. package/src/components/modals/modal/modal.styles.css +70 -0
  113. package/src/components/modals/portal/index.ts +1 -0
  114. package/src/components/modals/portal/portal.component.tsx +19 -0
  115. package/src/components/ripple/index.ts +1 -0
  116. package/src/components/ripple/ripple.component.tsx +70 -0
  117. package/src/components/ripple/ripple.styles.css +33 -0
  118. package/src/components/smart-table/index.ts +2 -0
  119. package/src/components/smart-table/smart-table.column.tsx +62 -0
  120. package/src/components/smart-table/smart-table.component.tsx +116 -0
  121. package/src/components/smart-table/smart-table.helpers.tsx +58 -0
  122. package/src/components/smart-table/smart-table.hooks.ts +27 -0
  123. package/src/components/smart-table/smart-table.row.tsx +28 -0
  124. package/src/components/smart-table/smart-table.styles.css +102 -0
  125. package/src/components/smart-table/smart-table.types.ts +42 -0
  126. package/src/components/tabs/index.ts +1 -0
  127. package/src/components/tabs/tabs-list.tsx +120 -0
  128. package/src/components/tabs/tabs.styles.css +164 -0
  129. package/src/components/tabs/tabs.tsx +119 -0
  130. package/src/components/tag/index.ts +1 -0
  131. package/src/components/tag/tag.component.tsx +34 -0
  132. package/src/components/tag/tag.styles.css +50 -0
  133. package/src/components/tooltips/click-to-copy/click-to-copy.component.tsx +41 -0
  134. package/src/components/tooltips/click-to-copy/index.ts +1 -0
  135. package/src/components/tooltips/index.ts +2 -0
  136. package/src/components/tooltips/tooltip/index.ts +1 -0
  137. package/src/components/tooltips/tooltip/tooltip.component.tsx +64 -0
  138. package/src/components/tooltips/tooltip/tooltip.styles.css +91 -0
  139. package/src/components/typography/index.ts +1 -0
  140. package/src/components/typography/typography.component.tsx +81 -0
  141. package/src/components/typography/typography.constants.ts +53 -0
  142. package/src/components/typography/typography.styles.css +122 -0
  143. package/src/helpers/cn.ts +6 -0
  144. package/src/helpers/format-bytes.ts +11 -0
  145. package/src/helpers/format-dates.ts +47 -0
  146. package/src/helpers/get-modal-position-relative-to-screen.ts +86 -0
  147. package/src/helpers/get-modal-position.ts +211 -0
  148. package/src/helpers/index.ts +6 -0
  149. package/src/helpers/text/index.ts +1 -0
  150. package/src/helpers/text/to-capitalize.ts +17 -0
  151. package/src/hooks/index.ts +30 -0
  152. package/src/hooks/use-async.ts +88 -0
  153. package/src/hooks/use-classnames.ts +13 -0
  154. package/src/hooks/use-click-outside.ts +32 -0
  155. package/src/hooks/use-constant.ts +3 -0
  156. package/src/hooks/use-cookie.ts +124 -0
  157. package/src/hooks/use-debounce-state.ts +13 -0
  158. package/src/hooks/use-debounce.ts +15 -0
  159. package/src/hooks/use-dimensions.ts +19 -0
  160. package/src/hooks/use-dom-container.ts +35 -0
  161. package/src/hooks/use-event-listener.ts +71 -0
  162. package/src/hooks/use-file-reader.ts +69 -0
  163. package/src/hooks/use-geolocation.ts +63 -0
  164. package/src/hooks/use-hover.ts +17 -0
  165. package/src/hooks/use-in-view.ts +18 -0
  166. package/src/hooks/use-input-handlers.ts +52 -0
  167. package/src/hooks/use-intersection-observer.ts +19 -0
  168. package/src/hooks/use-media-query.ts +25 -0
  169. package/src/hooks/use-modal-in-container.ts +85 -0
  170. package/src/hooks/use-modal-transition.ts +64 -0
  171. package/src/hooks/use-modal.ts +21 -0
  172. package/src/hooks/use-mouse-position.ts +64 -0
  173. package/src/hooks/use-online-status.ts +12 -0
  174. package/src/hooks/use-render-count.ts +11 -0
  175. package/src/hooks/use-resize-observer.ts +18 -0
  176. package/src/hooks/use-safe-dispatch.ts +22 -0
  177. package/src/hooks/use-scroll.ts +31 -0
  178. package/src/hooks/use-state-history.ts +22 -0
  179. package/src/hooks/use-toggle-values.ts +14 -0
  180. package/src/hooks/use-toggle.ts +11 -0
  181. package/src/hooks/use-viewport.ts +38 -0
  182. package/src/index.ts +5 -0
  183. package/src/layouts/flex/flex.component.tsx +75 -0
  184. package/src/layouts/flex/index.ts +1 -0
  185. package/src/layouts/grid/grid.component.tsx +89 -0
  186. package/src/layouts/grid/grid.styles.css +5 -0
  187. package/src/layouts/grid/index.ts +1 -0
  188. package/src/layouts/index.ts +2 -0
  189. package/src/types/generics.ts +68 -0
  190. package/src/types/index.ts +1 -0
  191. package/tsconfig.json +10 -0
  192. package/tsup.config.cjs +36 -0
  193. package/dist/components.cjs.map +0 -1
  194. package/dist/components.css.map +0 -1
  195. package/dist/components.js.map +0 -1
  196. package/dist/helpers.cjs.map +0 -1
  197. package/dist/helpers.js.map +0 -1
  198. package/dist/hooks.cjs.map +0 -1
  199. package/dist/hooks.js.map +0 -1
  200. package/dist/layouts.cjs.map +0 -1
  201. package/dist/layouts.css.map +0 -1
  202. package/dist/layouts.js.map +0 -1
  203. package/dist/types.cjs.map +0 -1
  204. package/dist/types.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+
3
+ export enum TypographyColors {
4
+ PRIMARY = 'primary',
5
+ SECONDARY = 'secondary',
6
+ TERTIARY = 'tertiary',
7
+ ACTIVE = 'active',
8
+ WARNING = 'warning',
9
+ ALERT = 'alert',
10
+ INFO = 'info',
11
+ }
12
+
13
+ export enum TypographyVariant {
14
+ HERO = 'hero',
15
+ HEADER1 = 'header1',
16
+ HEADER2 = 'header2',
17
+ HEADER3 = 'header3',
18
+ HEADER4 = 'header4',
19
+ BODY = 'body',
20
+ LABEL = 'label',
21
+ LABEL_FORM = 'label-form',
22
+ SMALL = 'small',
23
+ }
24
+
25
+ export const TypographyVariantsElements: Record<TypographyVariant, React.HTMLElementType> = {
26
+ [TypographyVariant.HERO]: 'h1',
27
+ [TypographyVariant.HEADER1]: 'h1',
28
+ [TypographyVariant.HEADER2]: 'h2',
29
+ [TypographyVariant.HEADER3]: 'h3',
30
+ [TypographyVariant.HEADER4]: 'h4',
31
+ [TypographyVariant.BODY]: 'p',
32
+ [TypographyVariant.LABEL]: 'span',
33
+ [TypographyVariant.LABEL_FORM]: 'label',
34
+ [TypographyVariant.SMALL]: 'small',
35
+ };
36
+
37
+ export const TypographyVariantsClassNames: Record<TypographyVariant, string> = {
38
+ [TypographyVariant.HERO]: 'hero',
39
+ [TypographyVariant.HEADER1]: 'header1',
40
+ [TypographyVariant.HEADER2]: 'header2',
41
+ [TypographyVariant.HEADER3]: 'header3',
42
+ [TypographyVariant.HEADER4]: 'header4',
43
+ [TypographyVariant.BODY]: 'body',
44
+ [TypographyVariant.LABEL]: 'label',
45
+ [TypographyVariant.LABEL_FORM]: 'label',
46
+ [TypographyVariant.SMALL]: 'small',
47
+ };
48
+
49
+ export enum TypographyWeight {
50
+ LIGHT = 'light',
51
+ REGULAR = 'regular',
52
+ BOLD = 'bold',
53
+ }
@@ -0,0 +1,122 @@
1
+ @reference "polpo-tailwind-config/styles";
2
+
3
+ .typography {
4
+ margin: 0;
5
+ padding: 0.5em 0 0.4em;
6
+
7
+ &.primary {
8
+ color: var(--color-primary-500);
9
+ }
10
+
11
+ &.secondary {
12
+ color: var(--color-secondary-500);
13
+ }
14
+
15
+ &.tertiary {
16
+ color: var(--color-tertiary-500);
17
+ }
18
+
19
+ &.active {
20
+ color: var(--color-active-500);
21
+ }
22
+
23
+ &.alert {
24
+ color: var(--color-alert-500);
25
+ }
26
+
27
+ &.warning {
28
+ color: var(--color-warning-500);
29
+ }
30
+
31
+ &.info {
32
+ color: var(--color-info-500);
33
+ }
34
+
35
+ &.recommended-width {
36
+ max-width: 70ch;
37
+ }
38
+
39
+ &.code-family {
40
+ font-family: monospace;
41
+ }
42
+
43
+ &.no-padding {
44
+ padding: 0;
45
+ }
46
+
47
+ &.nowrap {
48
+ overflow: hidden;
49
+ text-overflow: ellipsis;
50
+ white-space: nowrap;
51
+ }
52
+
53
+ &.nowrap-max-lines {
54
+ overflow: hidden;
55
+ display: -webkit-box;
56
+ -webkit-box-orient: vertical;
57
+ }
58
+
59
+ &.nowrap-max-lines-2 {
60
+ -webkit-line-clamp: 2;
61
+ line-clamp: 2;
62
+ }
63
+
64
+ &.nowrap-max-lines-3 {
65
+ -webkit-line-clamp: 3;
66
+ line-clamp: 3;
67
+ }
68
+
69
+ &.nowrap-max-lines-4 {
70
+ -webkit-line-clamp: 4;
71
+ line-clamp: 4;
72
+ }
73
+
74
+ &.nowrap-max-lines-5 {
75
+ -webkit-line-clamp: 5;
76
+ line-clamp: 5;
77
+ }
78
+
79
+ &.hero {
80
+ @apply text-hero;
81
+ }
82
+
83
+ &.header1 {
84
+ @apply text-header1;
85
+ }
86
+
87
+ &.header2 {
88
+ @apply text-header2;
89
+ }
90
+
91
+ &.header3 {
92
+ @apply text-header3;
93
+ }
94
+
95
+ &.header4 {
96
+ @apply text-header4;
97
+ }
98
+
99
+ &.body {
100
+ @apply text-body;
101
+ }
102
+
103
+ &.label {
104
+ @apply text-label;
105
+ }
106
+
107
+ &.small {
108
+ @apply text-small;
109
+ }
110
+
111
+ &.bold {
112
+ font-weight: 700;
113
+ }
114
+
115
+ &.regular {
116
+ font-weight: 400;
117
+ }
118
+
119
+ &.light {
120
+ font-weight: 300;
121
+ }
122
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export const cn = (...inputs: Array<ClassValue>) => {
5
+ return twMerge(clsx(inputs));
6
+ };
@@ -0,0 +1,11 @@
1
+ export const formatBytes = (bytes: number, decimals = 2) => {
2
+ if (bytes === 0) return '0 Bytes';
3
+
4
+ const k = 1000;
5
+ const dm = decimals < 0 ? 0 : decimals;
6
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
7
+
8
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
9
+
10
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
11
+ };
@@ -0,0 +1,47 @@
1
+ const MONTHS_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2
+
3
+ export const formatDate = (date: string) => {
4
+ const parsedDate = new Date(date);
5
+
6
+ if (Number.isNaN(parsedDate.getTime())) {
7
+ return date;
8
+ }
9
+
10
+ return `${MONTHS_SHORT[parsedDate.getMonth()]} ${parsedDate.getFullYear()}`;
11
+ };
12
+
13
+ export const timeBetween = (date_start: string, date_end: string) => {
14
+ const start = new Date(date_start);
15
+
16
+ if (Number.isNaN(start.getTime())) {
17
+ return '';
18
+ }
19
+
20
+ const end = Date.parse(date_end) ? new Date(date_end) : new Date();
21
+
22
+ const diffMs = Math.abs(end.getTime() - start.getTime());
23
+
24
+ const minutes = Math.floor(diffMs / (1000 * 60));
25
+ const hours = Math.floor(diffMs / (1000 * 60 * 60));
26
+ const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));
27
+ const months = Math.floor(days / 30);
28
+ const years = Math.floor(days / 365);
29
+
30
+ if (years > 0) {
31
+ return `${years} year${years > 1 ? 's' : ''}`;
32
+ }
33
+
34
+ if (months > 0) {
35
+ return `${months} month${months > 1 ? 's' : ''}`;
36
+ }
37
+
38
+ if (days > 0) {
39
+ return `${days} day${days > 1 ? 's' : ''}`;
40
+ }
41
+
42
+ if (hours > 0) {
43
+ return `${hours} hour${hours > 1 ? 's' : ''}`;
44
+ }
45
+
46
+ return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
47
+ };
@@ -0,0 +1,86 @@
1
+ import { PositionContainer } from './get-modal-position';
2
+
3
+ export type GetModalPositionRelativeToScreenParams = {
4
+ position: PositionContainer;
5
+ windowOffset: number;
6
+ };
7
+
8
+ export const getModalPositionRelativeToScreen = ({
9
+ position,
10
+ windowOffset,
11
+ }: GetModalPositionRelativeToScreenParams): Record<string, string> => {
12
+ switch (position) {
13
+ case PositionContainer.CENTER:
14
+ return {
15
+ top: '50%',
16
+ left: '50%',
17
+ transform: 'translate(-50%, -50%)',
18
+ };
19
+
20
+ case PositionContainer.TOP:
21
+ case PositionContainer.TOP_CENTER:
22
+ return {
23
+ top: `${windowOffset}px`,
24
+ left: '50%',
25
+ transform: 'translateX(-50%)',
26
+ };
27
+
28
+ case PositionContainer.TOP_LEFT:
29
+ case PositionContainer.LEFT_TOP:
30
+ return {
31
+ top: `${windowOffset}px`,
32
+ left: `${windowOffset}px`,
33
+ };
34
+
35
+ case PositionContainer.TOP_RIGHT:
36
+ case PositionContainer.RIGHT_TOP:
37
+ return {
38
+ top: `${windowOffset}px`,
39
+ right: `${windowOffset}px`,
40
+ };
41
+
42
+ case PositionContainer.BOTTOM:
43
+ case PositionContainer.BOTTOM_CENTER:
44
+ return {
45
+ bottom: `${windowOffset}px`,
46
+ left: '50%',
47
+ transform: 'translateX(-50%)',
48
+ };
49
+
50
+ case PositionContainer.LEFT_BOTTOM:
51
+ case PositionContainer.BOTTOM_LEFT:
52
+ return {
53
+ bottom: `${windowOffset}px`,
54
+ left: `${windowOffset}px`,
55
+ };
56
+
57
+ case PositionContainer.RIGHT_BOTTOM:
58
+ case PositionContainer.BOTTOM_RIGHT:
59
+ return {
60
+ bottom: `${windowOffset}px`,
61
+ right: `${windowOffset}px`,
62
+ };
63
+
64
+ case PositionContainer.LEFT:
65
+ case PositionContainer.LEFT_CENTER:
66
+ return {
67
+ top: '50%',
68
+ left: `${windowOffset}px`,
69
+ transform: 'translateY(-50%)',
70
+ };
71
+
72
+ case PositionContainer.RIGHT:
73
+ case PositionContainer.RIGHT_CENTER:
74
+ return {
75
+ top: '50%',
76
+ right: `${windowOffset}px`,
77
+ transform: 'translateY(-50%)',
78
+ };
79
+ }
80
+
81
+ return {
82
+ top: '50%',
83
+ left: '50%',
84
+ transform: 'translate(-50%, -50%)',
85
+ };
86
+ };
@@ -0,0 +1,211 @@
1
+ export enum PositionContainer {
2
+ CENTER = 'center',
3
+ TOP = 'top',
4
+ TOP_LEFT = 'top left',
5
+ TOP_RIGHT = 'top right',
6
+ TOP_CENTER = 'top center',
7
+ LEFT = 'left',
8
+ LEFT_TOP = 'left top',
9
+ LEFT_BOTTOM = 'left bottom',
10
+ LEFT_CENTER = 'left center',
11
+ RIGHT = 'right',
12
+ RIGHT_TOP = 'right top',
13
+ RIGHT_BOTTOM = 'right bottom',
14
+ RIGHT_CENTER = 'right center',
15
+ BOTTOM = 'bottom',
16
+ BOTTOM_LEFT = 'bottom left',
17
+ BOTTOM_RIGHT = 'bottom right',
18
+ BOTTOM_CENTER = 'bottom center',
19
+ }
20
+
21
+ export type PositionObject = {
22
+ x: number;
23
+ y: number;
24
+ top: number;
25
+ left: number;
26
+ w: number;
27
+ h: number;
28
+ };
29
+
30
+ export type ModalPosition = {
31
+ left: number;
32
+ top: number;
33
+ };
34
+
35
+ export type GetModalPositionParams = {
36
+ c: PositionObject;
37
+ m: PositionObject;
38
+ offset: number;
39
+ position: PositionContainer;
40
+ };
41
+
42
+ /*
43
+ * @description Calculates the position of the modal relative to the container
44
+ *
45
+ * @param c - The container's position object
46
+ * @param m - The modal's position object
47
+ * @param offset - The offset between the container and the modal
48
+ * @param position - The position of the modal
49
+ *
50
+ * -----------------------------------------------------------------------------
51
+ * @returns The position of the modal relative to the container
52
+ */
53
+ export const getModalPosition = ({ c, m, offset, position }: GetModalPositionParams): ModalPosition => {
54
+ // Default bottom
55
+ let top = c.y + c.h + offset;
56
+ let left = c.x - (m.w - c.w) * (50 / 100);
57
+
58
+ switch (position) {
59
+ case PositionContainer.TOP:
60
+ case PositionContainer.TOP_CENTER:
61
+ top = c.y - m.h - offset;
62
+ left = c.x - (m.w - c.w) / 2;
63
+
64
+ break;
65
+ case PositionContainer.TOP_LEFT:
66
+ top = c.y - m.h - offset;
67
+ left = c.x - m.w + c.w;
68
+
69
+ break;
70
+
71
+ case PositionContainer.TOP_RIGHT:
72
+ top = c.y - m.h - offset;
73
+ left = c.x;
74
+
75
+ break;
76
+
77
+ case PositionContainer.BOTTOM:
78
+ case PositionContainer.BOTTOM_CENTER:
79
+ top = c.y + c.h + offset;
80
+ left = c.x - (m.w - c.w) / 2;
81
+
82
+ break;
83
+
84
+ case PositionContainer.BOTTOM_LEFT:
85
+ top = c.y + c.h + offset;
86
+ left = c.x - m.w + c.w;
87
+
88
+ break;
89
+
90
+ case PositionContainer.BOTTOM_RIGHT:
91
+ top = c.y + c.h + offset;
92
+ left = c.x;
93
+
94
+ break;
95
+
96
+ case PositionContainer.LEFT:
97
+ case PositionContainer.LEFT_CENTER:
98
+ top = c.y - (m.h - c.h) / 2;
99
+ left = c.x - m.w - offset;
100
+
101
+ break;
102
+
103
+ case PositionContainer.LEFT_TOP:
104
+ top = c.y - m.h + c.h;
105
+ left = c.x - m.w - offset;
106
+
107
+ break;
108
+
109
+ case PositionContainer.LEFT_BOTTOM:
110
+ top = c.y;
111
+ left = c.x - m.w - offset;
112
+
113
+ break;
114
+
115
+ case PositionContainer.RIGHT:
116
+ case PositionContainer.RIGHT_CENTER:
117
+ top = c.y - (m.h - c.h) / 2;
118
+ left = c.x + c.w + offset;
119
+
120
+ break;
121
+
122
+ case PositionContainer.RIGHT_TOP:
123
+ top = c.y - m.h + c.h;
124
+ left = c.x + c.w + offset;
125
+
126
+ break;
127
+
128
+ case PositionContainer.RIGHT_BOTTOM:
129
+ top = c.y;
130
+ left = c.x + c.w + offset;
131
+
132
+ break;
133
+ }
134
+
135
+ return {
136
+ left: Math.round(left),
137
+ top: Math.round(top),
138
+ };
139
+ };
140
+
141
+ export const getOppositePosition = (
142
+ { top, left }: ModalPosition,
143
+ position: PositionContainer,
144
+ windowOffset: number,
145
+ m: PositionObject,
146
+ ) => {
147
+ const positions = position.split(' ');
148
+ const newPosition = [];
149
+ const rightOffset = left + m.w + windowOffset - window.innerWidth;
150
+ const bottomOffset = top + m.h + windowOffset - window.innerHeight;
151
+
152
+ for (const p of positions) {
153
+ if (p === PositionContainer.TOP && top < windowOffset) {
154
+ newPosition.push(PositionContainer.BOTTOM);
155
+ } else if (p === PositionContainer.LEFT && left < windowOffset) {
156
+ newPosition.push(PositionContainer.RIGHT);
157
+ } else if (p === PositionContainer.BOTTOM && bottomOffset > 0) {
158
+ newPosition.push(PositionContainer.TOP);
159
+ } else if (p === PositionContainer.RIGHT && rightOffset > 0) {
160
+ newPosition.push(PositionContainer.LEFT);
161
+ } else {
162
+ newPosition.push(p);
163
+ }
164
+ }
165
+
166
+ return newPosition.join(' ') as PositionContainer;
167
+ };
168
+
169
+ export const fixModalPosition = ({ top, left }: ModalPosition, m: PositionObject, windowOffset: number) => {
170
+ const rightOffset = left + m.w + windowOffset - window.innerWidth;
171
+ const bottomOffset = top + m.h + windowOffset - window.innerHeight;
172
+
173
+ left = rightOffset > 0 ? left - rightOffset : left;
174
+ top = bottomOffset > 0 ? top - bottomOffset : top;
175
+
176
+ left = left < windowOffset ? windowOffset : left;
177
+ top = top < windowOffset ? windowOffset : top;
178
+
179
+ return { top, left };
180
+ };
181
+
182
+ type getModalPositionRelativeToContainerParams = GetModalPositionParams & {
183
+ windowOffset: number;
184
+ };
185
+
186
+ export const getModalPositionRelativeToContainer = ({
187
+ c,
188
+ m,
189
+ offset,
190
+ windowOffset,
191
+ position,
192
+ }: getModalPositionRelativeToContainerParams): Record<string, string> => {
193
+ const params = { c, m, offset, position };
194
+ let modalContainerStyle = getModalPosition(params);
195
+
196
+ const oppositePosition = getOppositePosition(modalContainerStyle, position, windowOffset, m);
197
+
198
+ if (oppositePosition !== position) {
199
+ modalContainerStyle = getModalPosition({
200
+ ...params,
201
+ position: oppositePosition,
202
+ });
203
+ }
204
+
205
+ const fixedPosition = fixModalPosition(modalContainerStyle, m, windowOffset);
206
+
207
+ return {
208
+ left: `${fixedPosition.left}px`,
209
+ top: `${fixedPosition.top}px`,
210
+ };
211
+ };
@@ -0,0 +1,6 @@
1
+ export * from './cn';
2
+ export * from './format-bytes';
3
+ export * from './format-dates';
4
+ export * from './get-modal-position';
5
+ export * from './get-modal-position-relative-to-screen';
6
+ export * from './text';
@@ -0,0 +1 @@
1
+ export * from './to-capitalize';
@@ -0,0 +1,17 @@
1
+ export type ToCapitalizeOptions = Partial<{
2
+ separator: string;
3
+ join: string;
4
+ variant: 'first-word' | 'each-word';
5
+ }>;
6
+
7
+ export const toCapitalize = (text: string, options: ToCapitalizeOptions = {}) => {
8
+ const { separator = ' ', variant = 'each-word', join = separator } = options;
9
+
10
+ if (variant === 'first-word') {
11
+ return text[0].toUpperCase() + text.slice(1);
12
+ }
13
+
14
+ const words = text.split(separator);
15
+
16
+ return words.map(word => word[0].toUpperCase() + word.slice(1)).join(join);
17
+ };
@@ -0,0 +1,30 @@
1
+ export * from './use-async';
2
+ export * from './use-classnames';
3
+ export * from './use-click-outside';
4
+ export * from './use-constant';
5
+ export * from './use-cookie';
6
+ export * from './use-debounce';
7
+ export * from './use-debounce-state';
8
+ export * from './use-dimensions';
9
+ export * from './use-dom-container';
10
+ export * from './use-event-listener';
11
+ export * from './use-file-reader';
12
+ export * from './use-geolocation';
13
+ export * from './use-hover';
14
+ export * from './use-in-view';
15
+ export * from './use-input-handlers';
16
+ export * from './use-intersection-observer';
17
+ export * from './use-media-query';
18
+ export * from './use-modal';
19
+ export * from './use-modal-in-container';
20
+ export * from './use-modal-transition';
21
+ export * from './use-mouse-position';
22
+ export * from './use-online-status';
23
+ export * from './use-render-count';
24
+ export * from './use-resize-observer';
25
+ export * from './use-safe-dispatch';
26
+ export * from './use-scroll';
27
+ export * from './use-state-history';
28
+ export * from './use-toggle';
29
+ export * from './use-toggle-values';
30
+ export * from './use-viewport';
@@ -0,0 +1,88 @@
1
+ import { useCallback, useMemo, useReducer } from 'react';
2
+
3
+ import { useSafeDispatch } from './use-safe-dispatch';
4
+
5
+ type AsyncState<T> =
6
+ | {
7
+ status: 'idle' | 'pending';
8
+ data?: null;
9
+ error?: null;
10
+ }
11
+ | {
12
+ status: 'resolved';
13
+ data: T;
14
+ error: null;
15
+ }
16
+ | {
17
+ status: 'rejected';
18
+ data: null;
19
+ error: Error;
20
+ };
21
+
22
+ type AsyncAction<T> =
23
+ | { type: 'reset' }
24
+ | { type: 'pending' }
25
+ | { type: 'resolved'; data: T }
26
+ | { type: 'rejected'; error: Error };
27
+
28
+ const asyncReducer = <T>(_state: AsyncState<T>, action: AsyncAction<T>): AsyncState<T> => {
29
+ switch (action.type) {
30
+ case 'pending': {
31
+ return { status: 'pending' as const, data: null, error: null };
32
+ }
33
+ case 'resolved': {
34
+ return { status: 'resolved' as const, data: action.data, error: null };
35
+ }
36
+ case 'rejected': {
37
+ return { status: 'rejected' as const, data: null, error: action.error };
38
+ }
39
+ default: {
40
+ throw new Error(`Unhandled action: ${JSON.stringify(action)}`);
41
+ }
42
+ }
43
+ };
44
+
45
+ export const useAsync = <T>(
46
+ initialState?: AsyncState<T>,
47
+ ): AsyncState<T> & {
48
+ setData: (data: T) => void;
49
+ setError: (error: Error) => void;
50
+ run: (promise: Promise<T>) => void;
51
+ } => {
52
+ const [asyncState, unsafeDispatch] = useReducer<AsyncState<T>, [AsyncAction<T>]>(asyncReducer, {
53
+ status: 'idle',
54
+ data: null,
55
+ error: null,
56
+ ...initialState,
57
+ });
58
+ const dispatch = useSafeDispatch(unsafeDispatch);
59
+
60
+ const run = useCallback(
61
+ (promise: Promise<T>) => {
62
+ dispatch({ type: 'pending' });
63
+ promise.then(
64
+ data => {
65
+ dispatch({ type: 'resolved', data });
66
+ },
67
+ (error: Error) => {
68
+ dispatch({ type: 'rejected', error });
69
+ },
70
+ );
71
+ },
72
+ [dispatch],
73
+ );
74
+
75
+ const setData = useCallback((data: T) => dispatch({ type: 'resolved', data }), [dispatch]);
76
+
77
+ const setError = useCallback((error: Error) => dispatch({ type: 'rejected', error }), [dispatch]);
78
+
79
+ return useMemo(
80
+ () => ({
81
+ setData,
82
+ setError,
83
+ run,
84
+ ...asyncState,
85
+ }),
86
+ [asyncState, run, setData, setError],
87
+ );
88
+ };
@@ -0,0 +1,13 @@
1
+ import { useMemo } from 'react';
2
+
3
+ export const useClassNames = (classes: Record<string, boolean>): string =>
4
+ useMemo<string>(
5
+ () =>
6
+ Object.entries(classes)
7
+ .reduce<Array<string>>(
8
+ (compiledClassNames, [classname, value]) => (value ? [...compiledClassNames, classname] : compiledClassNames),
9
+ [],
10
+ )
11
+ .join(' '),
12
+ [classes],
13
+ );