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,143 @@
1
+ @reference "polpo-tailwind-config/styles";
2
+
3
+ .input-file-container {
4
+ display: grid;
5
+ gap: 10px;
6
+
7
+ .input-file--box {
8
+ border: 1px solid;
9
+ border-radius: 5px;
10
+ position: relative;
11
+ transition: all 0.3s ease;
12
+ display: grid;
13
+ grid-template-columns: 32px 1fr;
14
+ padding: 10px;
15
+ gap: 10px;
16
+ background: var(--color-secondary-500);
17
+ color: var(--color-secondary-50);
18
+ align-items: center;
19
+
20
+ .input-file--box--icon {
21
+ width: 32px;
22
+ height: 32px;
23
+ border-radius: 5px;
24
+ background: var(--color-primary-50);
25
+ color: var(--color-primary-500);
26
+ display: grid;
27
+ place-content: center;
28
+ }
29
+
30
+ input {
31
+ position: absolute;
32
+ top: 0;
33
+ left: 0;
34
+ width: 100%;
35
+ height: 100%;
36
+ cursor: copy;
37
+ opacity: 0;
38
+ }
39
+
40
+ &:hover {
41
+ background: var(--color-primary-50);
42
+ color: var(--color-primary-500);
43
+
44
+ .input-file--box--icon {
45
+ background: var(--color-primary-500);
46
+ color: var(--color-primary-50);
47
+ }
48
+ }
49
+ }
50
+
51
+ .input-file--files {
52
+ max-height: 280px;
53
+ overflow-y: auto;
54
+ border: 1px solid;
55
+ border-radius: 5px;
56
+
57
+ .input-file--files--content {
58
+ display: grid;
59
+ }
60
+ }
61
+
62
+ .error {
63
+ color: tomato;
64
+ }
65
+
66
+ .total-info {
67
+ font-size: 1em;
68
+ display: grid;
69
+ grid-auto-flow: column;
70
+ justify-content: end;
71
+ align-items: center;
72
+ gap: 0.5em;
73
+
74
+ .total-info--delete {
75
+ display: grid;
76
+ place-content: center;
77
+ height: 100%;
78
+ background: transparent;
79
+ cursor: pointer;
80
+ transition: all 0.3s ease;
81
+ aspect-ratio: 1 / 1;
82
+ border-radius: 50%;
83
+ width: 2em;
84
+ font-size: 0.8em;
85
+
86
+ &:hover {
87
+ background: var(--color-primary-500);
88
+ color: var(--color-primary-50);
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ .input-file-card {
95
+ display: grid;
96
+ grid-template-columns: 35px 1fr 35px;
97
+ gap: 1em;
98
+ align-items: center;
99
+ padding: 10px;
100
+ background: var(--color-background);
101
+
102
+ &:not(:last-child) {
103
+ border-bottom: 1px dashed;
104
+ }
105
+
106
+ &:hover {
107
+ .file-card--delete {
108
+ color: var(--color-primary-500);
109
+ }
110
+ }
111
+
112
+ .file-card--image {
113
+ background: var(--color-primary-500);
114
+ color: var(--color-primary-50);
115
+ width: 32px;
116
+ height: 32px;
117
+ border-radius: 5px;
118
+ display: grid;
119
+ place-content: center;
120
+ border: 1px solid;
121
+ }
122
+
123
+ .file-card--data {
124
+ display: grid;
125
+ font-size: 0.7em;
126
+ justify-content: start;
127
+ }
128
+
129
+ .file-card--delete {
130
+ border-radius: 5px;
131
+ display: grid;
132
+ place-content: center;
133
+ width: 32px;
134
+ height: 32px;
135
+ cursor: pointer;
136
+ transition: all 0.3s ease;
137
+
138
+ &:hover {
139
+ background: var(--color-primary-500);
140
+ color: var(--color-primary-50);
141
+ }
142
+ }
143
+ }
@@ -0,0 +1 @@
1
+ export * from './input-number.component';
@@ -0,0 +1,144 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+
3
+ import { useInputHandlers } from '../../../hooks';
4
+ import { Controller } from '../controller';
5
+ import { Field, InputFieldProps } from '../field';
6
+ import { ControllerGeneratorProps, UnControlledComponentProps } from '../form.types';
7
+
8
+ type FormatFunction = (value: number) => string;
9
+
10
+ const RegexDecimal = {
11
+ comma: /(?!^-)[^0-9,]/g,
12
+ dot: /(?!^-)[^0-9.]/g,
13
+ } as const;
14
+
15
+ const getCleanedValue = (input: string, decimalSeparator = '.'): string => {
16
+ const regex = decimalSeparator === '.' ? RegexDecimal.dot : RegexDecimal.comma;
17
+ const cleaned = input.replace(regex, '').replace(',', '.');
18
+
19
+ if (!cleaned) {
20
+ return '';
21
+ }
22
+
23
+ return cleaned;
24
+ };
25
+
26
+ const getFormattedValue = (value: number, format: FormatFunction) => {
27
+ if (!value || Number.isNaN(value)) {
28
+ return format(0);
29
+ }
30
+
31
+ return format(value);
32
+ };
33
+
34
+ type FormatConfig = Intl.NumberFormatOptions & {
35
+ locales: Intl.LocalesArgument;
36
+ };
37
+
38
+ type IProps = InputFieldProps<{
39
+ pattern?: string;
40
+ min?: number;
41
+ max?: number;
42
+ format?: FormatConfig | FormatFunction;
43
+ decimalSeparator?: string;
44
+ }>;
45
+
46
+ export const InputNumber = ({
47
+ name,
48
+ value,
49
+ setValue,
50
+ onBlur,
51
+ onFocus,
52
+ pattern,
53
+ className = '',
54
+ style = {},
55
+ autoFocus = false,
56
+ readOnly = false,
57
+ disabled = false,
58
+ placeholder = '',
59
+ autoComplete = 'off',
60
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
61
+ isDirty = false,
62
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
63
+ isTouched = false,
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
+ invalid = false,
66
+ error,
67
+ min,
68
+ max,
69
+ decimalSeparator,
70
+ format = {
71
+ locales: 'en-US',
72
+ style: 'decimal',
73
+ minimumFractionDigits: 0,
74
+ maximumFractionDigits: 2,
75
+ },
76
+ ...fieldProps
77
+ }: UnControlledComponentProps<IProps, number>) => {
78
+ const id = useMemo(() => crypto.randomUUID(), []);
79
+ const formatFunction = useMemo<FormatFunction>(() => {
80
+ if (typeof format === 'function') return format;
81
+
82
+ const { locales, ...formatConfig } = format;
83
+
84
+ return Intl.NumberFormat(locales, formatConfig).format;
85
+ }, [format]);
86
+ const [formattedValue, setFormattedValue] = useState(getFormattedValue(value, formatFunction));
87
+
88
+ const { handlers, isFocus } = useInputHandlers({
89
+ onChange: e => {
90
+ const cleaned = getCleanedValue(e.target.value, decimalSeparator);
91
+
92
+ if (cleaned.endsWith('.')) {
93
+ setFormattedValue(e.target.value);
94
+ } else {
95
+ setFormattedValue(getFormattedValue(Number(cleaned), formatFunction));
96
+ setValue(Number(cleaned));
97
+ }
98
+ },
99
+ onBlur: e => {
100
+ const cleaned = getCleanedValue(e.target.value, decimalSeparator);
101
+ setFormattedValue(getFormattedValue(Number(cleaned), formatFunction));
102
+ setValue(Number(cleaned));
103
+
104
+ if (onBlur) onBlur(e);
105
+ },
106
+ onFocus: onFocus,
107
+ });
108
+
109
+ useEffect(() => {
110
+ const formatted = getFormattedValue(value, formatFunction);
111
+
112
+ if (formattedValue !== formatted) {
113
+ setFormattedValue(formatted);
114
+ }
115
+ }, [formatFunction, formattedValue, value]);
116
+
117
+ return (
118
+ <Field id={id} error={error} isFocus={isFocus} {...fieldProps}>
119
+ <input
120
+ id={id}
121
+ type='text'
122
+ name={name}
123
+ className={className}
124
+ style={style}
125
+ value={formattedValue}
126
+ pattern={pattern}
127
+ autoFocus={autoFocus}
128
+ autoComplete={autoComplete}
129
+ placeholder={placeholder}
130
+ disabled={disabled}
131
+ readOnly={readOnly}
132
+ min={min}
133
+ max={max}
134
+ {...handlers}
135
+ />
136
+ </Field>
137
+ );
138
+ };
139
+
140
+ const InputController = ({ rules, ...props }: ControllerGeneratorProps<IProps, number>) => {
141
+ return <Controller Component={InputNumber} defaultValue={0} inputProps={props} rules={rules} />;
142
+ };
143
+
144
+ InputNumber.Controller = InputController;
@@ -0,0 +1 @@
1
+ export * from './input-password.component';
@@ -0,0 +1,77 @@
1
+ import { useMemo } from 'react';
2
+ import { IconType } from 'react-icons';
3
+ import { FiEye, FiEyeOff } from 'react-icons/fi';
4
+ import { IoIosWarning } from 'react-icons/io';
5
+
6
+ import { useInputHandlers, useToggleValues } from '../../../hooks';
7
+ import { Controller } from '../controller';
8
+ import { Field, InputFieldProps } from '../field';
9
+ import { ControllerGeneratorProps, UnControlledComponentProps } from '../form.types';
10
+
11
+ type InputPasswordProps = InputFieldProps<{
12
+ rightIcon?: never;
13
+ }>;
14
+
15
+ export const InputPassword = ({
16
+ name,
17
+ value,
18
+ setValue,
19
+ onBlur,
20
+ onFocus,
21
+ className = '',
22
+ style = {},
23
+ autoFocus = false,
24
+ readOnly = false,
25
+ disabled = false,
26
+ placeholder = '',
27
+ autoComplete = 'off',
28
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
+ isDirty = false,
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ isTouched = false,
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
+ invalid = false,
34
+ error,
35
+ ...fieldProps
36
+ }: UnControlledComponentProps<InputPasswordProps, string>) => {
37
+ const id = useMemo(() => crypto.randomUUID(), []);
38
+ const [type, toggle] = useToggleValues<'password' | 'text'>(['password', 'text']);
39
+ const { isFocus, handlers } = useInputHandlers({
40
+ onBlur: onBlur,
41
+ onChange: e => setValue(e.target.value),
42
+ onFocus: onFocus,
43
+ });
44
+
45
+ const icon = useMemo<IconType>(() => {
46
+ if (type === 'password') return FiEye;
47
+
48
+ if (type === 'text') return FiEyeOff;
49
+
50
+ return IoIosWarning;
51
+ }, [type]);
52
+
53
+ return (
54
+ <Field id={id} error={error} isFocus={isFocus} {...fieldProps} rightIcon={icon} onClickRightIcon={() => toggle()}>
55
+ <input
56
+ id={id}
57
+ type={type}
58
+ name={name}
59
+ className={className}
60
+ style={style}
61
+ value={value}
62
+ autoFocus={autoFocus}
63
+ autoComplete={autoComplete}
64
+ placeholder={placeholder}
65
+ disabled={disabled}
66
+ readOnly={readOnly}
67
+ {...handlers}
68
+ />
69
+ </Field>
70
+ );
71
+ };
72
+
73
+ const InputPasswordController = ({ rules, ...props }: ControllerGeneratorProps<InputPasswordProps, string>) => {
74
+ return <Controller Component={InputPassword} defaultValue='' inputProps={props} rules={rules} />;
75
+ };
76
+
77
+ InputPassword.Controller = InputPasswordController;
@@ -0,0 +1 @@
1
+ export * from './radio.component';
@@ -0,0 +1,92 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { useClassNames, useInputHandlers } from '../../../hooks';
4
+ import { ColorTypes, SizeTypes } from '../../component.types';
5
+ import { Typography } from '../../typography';
6
+ import { Controller } from '../controller';
7
+ import { ControllerGeneratorProps, UnControlledComponentProps } from '../form.types';
8
+
9
+ import './radio.styles.css';
10
+
11
+ type RadioProps = {
12
+ label?: string;
13
+ radioValue: string;
14
+ placeholder?: never;
15
+ color?: `${ColorTypes}`;
16
+ size?: `${SizeTypes}`;
17
+ };
18
+
19
+ export const Radio = ({
20
+ name,
21
+ value,
22
+ setValue,
23
+ onBlur,
24
+ onFocus,
25
+ className = '',
26
+ style = {},
27
+ autoFocus = false,
28
+ readOnly = false,
29
+ disabled = false,
30
+ autoComplete = 'off',
31
+ radioValue,
32
+ label,
33
+ color = ColorTypes.PRIMARY,
34
+ size = SizeTypes.REGULAR,
35
+ /*
36
+ * isDirty = false,
37
+ * isTouched = false,
38
+ * invalid = false,
39
+ * error,
40
+ */
41
+ }: UnControlledComponentProps<RadioProps, string>) => {
42
+ const id = useMemo(() => crypto.randomUUID(), []);
43
+ const { handlers } = useInputHandlers({
44
+ onChange: e => setValue(e.target.value),
45
+ onBlur: onBlur,
46
+ onFocus: onFocus,
47
+ });
48
+
49
+ const radioContainerClassName = useClassNames({
50
+ 'radio-container': true,
51
+ [className]: Boolean(className),
52
+ [`color-${color}`]: Boolean(color),
53
+ });
54
+
55
+ const radioClassName = useClassNames({
56
+ radio: true,
57
+ 'is-checked': radioValue === value,
58
+ [`size-${size}`]: Boolean(size),
59
+ });
60
+
61
+ return (
62
+ <section className={radioContainerClassName} style={style}>
63
+ <section className={radioClassName}>
64
+ <section className='radio-fill' />
65
+ <input
66
+ id={id}
67
+ type='radio'
68
+ name={name}
69
+ className={`radio-input ${className}`}
70
+ style={style}
71
+ value={radioValue}
72
+ checked={radioValue === value}
73
+ autoFocus={autoFocus}
74
+ autoComplete={autoComplete}
75
+ disabled={disabled || readOnly}
76
+ {...handlers}
77
+ />
78
+ </section>
79
+ {label ? (
80
+ <Typography variant='label-form' htmlFor={id} className='radio-label'>
81
+ {label}
82
+ </Typography>
83
+ ) : null}
84
+ </section>
85
+ );
86
+ };
87
+
88
+ const RadioController = ({ rules, ...props }: ControllerGeneratorProps<RadioProps, string>) => {
89
+ return <Controller Component={Radio} defaultValue='' inputProps={props} rules={rules} />;
90
+ };
91
+
92
+ Radio.Controller = RadioController;
@@ -0,0 +1,117 @@
1
+ @reference "polpo-tailwind-config/styles";
2
+
3
+ .radio {
4
+ border-radius: 50%;
5
+ background: var(--color-background-paper);
6
+ transition: all 300ms ease;
7
+ width: 1em;
8
+ height: 1em;
9
+ outline: 2px solid;
10
+ display: flex;
11
+ position: relative;
12
+ padding: 2px;
13
+
14
+ .radio-input {
15
+ position: absolute;
16
+ top: 0;
17
+ left: 0;
18
+ width: 100%;
19
+ height: 100%;
20
+ cursor: pointer;
21
+ opacity: 0;
22
+ z-index: 1;
23
+ }
24
+
25
+ &.size-small {
26
+ font-size: 1em;
27
+ }
28
+
29
+ &.size-regular {
30
+ font-size: 1.2em;
31
+ }
32
+
33
+ &.size-large {
34
+ font-size: 1.4em;
35
+ }
36
+ }
37
+
38
+ .radio-fill {
39
+ transition: all 200ms ease-out;
40
+ border-radius: inherit;
41
+ margin: auto;
42
+ width: 0;
43
+ height: 0;
44
+ }
45
+
46
+ .radio-container {
47
+ --color: var(--color-gray-800);
48
+
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 1em;
52
+ width: fit-content;
53
+
54
+ .radio-label {
55
+ cursor: pointer;
56
+ user-select: none;
57
+ }
58
+
59
+ .radio {
60
+ color: var(--color);
61
+
62
+ &.is-checked {
63
+ .radio-fill {
64
+ width: 100%;
65
+ height: 100%;
66
+ background: var(--color);
67
+ }
68
+ }
69
+ }
70
+
71
+ .radio:hover,
72
+ &:has(.radio-label:hover) .radio {
73
+ box-shadow: 0 0 0 0.4em hsl(from var(--color) h s l / 50%);
74
+
75
+ .radio-fill {
76
+ width: 20%;
77
+ height: 20%;
78
+ background: hsl(from var(--color) h s l / 50%);
79
+ }
80
+
81
+ &:is(.is-checked) {
82
+ .radio-fill {
83
+ width: 80%;
84
+ height: 80%;
85
+ background: var(--color);
86
+ }
87
+ }
88
+ }
89
+
90
+ &.color-primary {
91
+ --color: var(--color-primary-500);
92
+ }
93
+
94
+ &.color-secondary {
95
+ --color: var(--color-secondary-500);
96
+ }
97
+
98
+ &.color-tertiary {
99
+ --color: var(--color-tertiary-500);
100
+ }
101
+
102
+ &.color-active {
103
+ --color: var(--color-active-500);
104
+ }
105
+
106
+ &.color-warning {
107
+ --color: var(--color-warning-500);
108
+ }
109
+
110
+ &.color-alert {
111
+ --color: var(--color-alert-500);
112
+ }
113
+
114
+ &.color-info {
115
+ --color: var(--color-info-500);
116
+ }
117
+ }
@@ -0,0 +1 @@
1
+ export { Select } from './select.component';
@@ -0,0 +1,101 @@
1
+ import React, { useCallback, useMemo } from 'react';
2
+
3
+ import { Menu } from '../../modals';
4
+ import { Typography } from '../../typography';
5
+
6
+ import { useSelectContext } from './select.component';
7
+ import { SelectItem } from './select.types';
8
+
9
+ type OptionProps<T extends SelectItem> = {
10
+ children: React.ReactNode;
11
+ value: T;
12
+ };
13
+
14
+ export const Option = <T extends SelectItem>({ children, value }: OptionProps<T>) => {
15
+ const { multiselect, isEqualComparator, maxOptions, selectedValue, setValue, setIsOpen } = useSelectContext<T>();
16
+
17
+ const compareValuesIsEqual = useCallback(
18
+ (a: T, b: T): boolean => {
19
+ if (['number', 'string'].includes(typeof a)) {
20
+ return a === b;
21
+ }
22
+
23
+ return !!isEqualComparator && isEqualComparator(a, b);
24
+ },
25
+ [isEqualComparator],
26
+ );
27
+
28
+ const toggleOption = useCallback(
29
+ (isSelected: boolean) => {
30
+ if (isSelected) {
31
+ if (multiselect) {
32
+ if (maxOptions && Array.isArray(selectedValue) && selectedValue.length >= maxOptions) {
33
+ return;
34
+ }
35
+
36
+ setValue([...selectedValue, value]);
37
+ } else {
38
+ setValue(value);
39
+ setIsOpen(false);
40
+ }
41
+ } else {
42
+ if (multiselect) {
43
+ setValue(selectedValue.filter(item => !compareValuesIsEqual(item, value)));
44
+ } else {
45
+ setValue(null);
46
+ setIsOpen(false);
47
+ }
48
+ }
49
+ },
50
+ [multiselect, maxOptions, selectedValue, setValue, value, setIsOpen, compareValuesIsEqual],
51
+ );
52
+
53
+ const isSelected = useMemo(() => {
54
+ if (selectedValue === '' || selectedValue === null) {
55
+ return false;
56
+ }
57
+
58
+ if (!Array.isArray(selectedValue)) {
59
+ return compareValuesIsEqual(value, selectedValue);
60
+ }
61
+
62
+ if (['number', 'string'].includes(typeof value)) {
63
+ return selectedValue.includes(value);
64
+ }
65
+
66
+ return selectedValue.some(item => !!isEqualComparator && isEqualComparator(value, item));
67
+ }, [compareValuesIsEqual, isEqualComparator, selectedValue, value]);
68
+
69
+ const handleKeyDown = useCallback(
70
+ (e: React.KeyboardEvent) => {
71
+ if (['Enter', ' '].includes(e.key)) {
72
+ e.preventDefault();
73
+
74
+ toggleOption(!(isSelected && multiselect));
75
+ }
76
+ },
77
+ [toggleOption, isSelected, multiselect],
78
+ );
79
+
80
+ const optionLabel = useMemo(() => {
81
+ if (typeof children === 'string') {
82
+ return (
83
+ <Typography data-value={value} variant='label' nowrap>
84
+ {children}
85
+ </Typography>
86
+ );
87
+ }
88
+
89
+ return children;
90
+ }, [children, value]);
91
+
92
+ return (
93
+ <Menu.Option
94
+ label={optionLabel}
95
+ onKeyDown={handleKeyDown}
96
+ asCheckbox={multiselect}
97
+ selected={isSelected}
98
+ onClick={(selected: boolean) => toggleOption(multiselect ? selected : true)}
99
+ />
100
+ );
101
+ };