anteriority-ui 1.0.0

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 (199) hide show
  1. package/.github/workflows/ci.yml +31 -0
  2. package/.github/workflows/publish.yml +45 -0
  3. package/.npmrc.bak +1 -0
  4. package/.storybook/main.js +15 -0
  5. package/.storybook/preview.jsx +26 -0
  6. package/AnteriorityUI.md +2400 -0
  7. package/CLAUDE.md +91 -0
  8. package/README.md +182 -0
  9. package/assets/anteriorityUI/AnteriorityUI.logo.jpeg +0 -0
  10. package/assets/favicon/anteriorityUI.ico +0 -0
  11. package/common/api/client.js +43 -0
  12. package/common/storage/LocalStorage.js +13 -0
  13. package/common/styles/typography.css +5 -0
  14. package/common/styles/variables.css +192 -0
  15. package/common/theme/muiTheme.js +9 -0
  16. package/features/password-reset/api.js +39 -0
  17. package/features/user-profile/api.js +5 -0
  18. package/hooks/useAdminPasswordReset.js +133 -0
  19. package/index.js +57 -0
  20. package/package.json +57 -0
  21. package/pnpm-workspace.yaml +3 -0
  22. package/screens/components/AlertModal/AlertModal.jsx +259 -0
  23. package/screens/components/AlertModal/AlertModal.module.css +185 -0
  24. package/screens/components/AlertModal/AlertModal.stories.jsx +87 -0
  25. package/screens/components/Avatar/index.jsx +0 -0
  26. package/screens/components/Avatar/index.module.css +0 -0
  27. package/screens/components/Avatar/utils.js +0 -0
  28. package/screens/components/Badge/index.jsx +33 -0
  29. package/screens/components/Badge/index.module.css +18 -0
  30. package/screens/components/Badge/index.stories.jsx +40 -0
  31. package/screens/components/Barnner/index.jsx +21 -0
  32. package/screens/components/Barnner/index.module.css +33 -0
  33. package/screens/components/Barnner/index.stories.jsx +39 -0
  34. package/screens/components/Barnner/utils.js +0 -0
  35. package/screens/components/BlueLink/index.jsx +27 -0
  36. package/screens/components/BlueLink/index.module.css +11 -0
  37. package/screens/components/BlueLink/index.stories.jsx +29 -0
  38. package/screens/components/Breadcrumbs/index.jsx +62 -0
  39. package/screens/components/Breadcrumbs/index.module.css +158 -0
  40. package/screens/components/Breadcrumbs/index.stories.jsx +37 -0
  41. package/screens/components/Button/index.jsx +169 -0
  42. package/screens/components/Button/index.module.css +168 -0
  43. package/screens/components/Button/index.stories.jsx +71 -0
  44. package/screens/components/Button/utils.js +28 -0
  45. package/screens/components/CalendarComponent/CustomDatePicker.jsx +248 -0
  46. package/screens/components/CalendarComponent/CustomDatePicker.module.css +323 -0
  47. package/screens/components/CalendarComponent/CustomDatePicker.stories.jsx +45 -0
  48. package/screens/components/CalendarPicker/Constants.js +12 -0
  49. package/screens/components/CalendarPicker/index.jsx +263 -0
  50. package/screens/components/CalendarPicker/index.module.css +147 -0
  51. package/screens/components/CalendarPicker/index.stories.jsx +45 -0
  52. package/screens/components/CalendarPicker/utils.js +42 -0
  53. package/screens/components/CardV1/index.jsx +165 -0
  54. package/screens/components/CardV1/index.module.css +232 -0
  55. package/screens/components/CardV1/index.stories.jsx +62 -0
  56. package/screens/components/CardV1/utils.js +49 -0
  57. package/screens/components/ChatWindow/index.jsx +251 -0
  58. package/screens/components/ChatWindow/index.module.css +311 -0
  59. package/screens/components/ChatWindow/index.stories.jsx +62 -0
  60. package/screens/components/Checks/index.jsx +31 -0
  61. package/screens/components/Checks/index.module.css +32 -0
  62. package/screens/components/Checks/index.stories.jsx +32 -0
  63. package/screens/components/Container/index.jsx +36 -0
  64. package/screens/components/Container/index.module.css +24 -0
  65. package/screens/components/Container/index.stories.jsx +32 -0
  66. package/screens/components/CopyText/index.jsx +42 -0
  67. package/screens/components/CopyText/index.module.css +52 -0
  68. package/screens/components/CopyText/index.stories.jsx +24 -0
  69. package/screens/components/CountdownBar/constants.js +3 -0
  70. package/screens/components/CountdownBar/index.jsx +81 -0
  71. package/screens/components/CountdownBar/index.module.css +45 -0
  72. package/screens/components/CountdownBar/index.stories.jsx +43 -0
  73. package/screens/components/CountdownBar/utils.js +119 -0
  74. package/screens/components/DateTag/index.jsx +33 -0
  75. package/screens/components/DateTag/index.module.css +12 -0
  76. package/screens/components/DateTag/index.stories.jsx +29 -0
  77. package/screens/components/DesplegablePanel/constants.js +0 -0
  78. package/screens/components/DesplegablePanel/index.jsx +61 -0
  79. package/screens/components/DesplegablePanel/index.module.css +120 -0
  80. package/screens/components/DesplegablePanel/index.stories.jsx +44 -0
  81. package/screens/components/DesplegablePanel/utils.js +0 -0
  82. package/screens/components/DrawPanel/index.jsx +217 -0
  83. package/screens/components/DrawPanel/index.module.css +252 -0
  84. package/screens/components/DrawPanel/index.stories.jsx +66 -0
  85. package/screens/components/DrawPanel/utils.js +0 -0
  86. package/screens/components/Filter/index.jsx +105 -0
  87. package/screens/components/Filter/index.module.css +91 -0
  88. package/screens/components/Filter/index.stories.jsx +46 -0
  89. package/screens/components/Filter/utils.js +0 -0
  90. package/screens/components/FormControls/FormControls.module.css +86 -0
  91. package/screens/components/FormControls/index.jsx +51 -0
  92. package/screens/components/FormControls/index.stories.jsx +36 -0
  93. package/screens/components/Frame/index.jsx +54 -0
  94. package/screens/components/Frame/index.module.css +102 -0
  95. package/screens/components/Frame/index.stories.jsx +37 -0
  96. package/screens/components/Frame/utils.js +0 -0
  97. package/screens/components/GreenHighlight/index.jsx +11 -0
  98. package/screens/components/GreenHighlight/index.module.css +7 -0
  99. package/screens/components/GreenHighlight/index.stories.jsx +26 -0
  100. package/screens/components/GreenHighlight/utils.jsx +45 -0
  101. package/screens/components/HoursPicker/Constants.js +26 -0
  102. package/screens/components/HoursPicker/index.jsx +350 -0
  103. package/screens/components/HoursPicker/index.module.css +99 -0
  104. package/screens/components/HoursPicker/index.stories.jsx +44 -0
  105. package/screens/components/HoursPicker/utils.js +48 -0
  106. package/screens/components/InfoTooltip/index.jsx +59 -0
  107. package/screens/components/InfoTooltip/index.module.css +98 -0
  108. package/screens/components/InfoTooltip/index.stories.jsx +35 -0
  109. package/screens/components/Input/index.jsx +36 -0
  110. package/screens/components/Input/index.module.css +49 -0
  111. package/screens/components/Input/index.stories.jsx +37 -0
  112. package/screens/components/Input/utils.js +0 -0
  113. package/screens/components/Loading/index.jsx +76 -0
  114. package/screens/components/Loading/index.module.css +193 -0
  115. package/screens/components/Loading/index.stories.jsx +38 -0
  116. package/screens/components/Loading/utils.js +0 -0
  117. package/screens/components/MarkdownContent/index.jsx +16 -0
  118. package/screens/components/MarkdownContent/index.module.css +149 -0
  119. package/screens/components/MarkdownContent/index.stories.jsx +30 -0
  120. package/screens/components/MarkdownEditor/index.jsx +430 -0
  121. package/screens/components/MarkdownEditor/index.module.css +192 -0
  122. package/screens/components/MarkdownEditor/index.stories.jsx +50 -0
  123. package/screens/components/Material-UI/Components/InputWithIcon/index.jsx +87 -0
  124. package/screens/components/Material-UI/Components/InputWithIcon/index.stories.jsx +49 -0
  125. package/screens/components/Material-UI/Components/MenuPopover/index.jsx +117 -0
  126. package/screens/components/Material-UI/Components/MenuPopover/index.stories.jsx +36 -0
  127. package/screens/components/Material-UI/Components/MultilinePlaceholder/index.jsx +44 -0
  128. package/screens/components/Material-UI/Components/MultilinePlaceholder/index.stories.jsx +45 -0
  129. package/screens/components/Material-UI/Components/Paginador/index.jsx +79 -0
  130. package/screens/components/Material-UI/Components/Paginador/index.stories.jsx +49 -0
  131. package/screens/components/Material-UI/Components/Search/index.jsx +81 -0
  132. package/screens/components/Material-UI/Components/Search/index.stories.jsx +68 -0
  133. package/screens/components/Material-UI/Components/Selector/index.jsx +100 -0
  134. package/screens/components/Material-UI/Components/Selector/index.module.css +0 -0
  135. package/screens/components/Material-UI/Components/Selector/index.stories.jsx +52 -0
  136. package/screens/components/Material-UI/Components/Selector/utils.js +0 -0
  137. package/screens/components/Material-UI/Components/TextField/index.jsx +61 -0
  138. package/screens/components/Material-UI/Components/TextField/index.stories.jsx +53 -0
  139. package/screens/components/ModalTooltip/ModalTooltip.jsx +122 -0
  140. package/screens/components/ModalTooltip/ModalTooltip.module.css +65 -0
  141. package/screens/components/ModalTooltip/ModalTooltip.stories.jsx +39 -0
  142. package/screens/components/Notification/Error/ErrorMessage.jsx +30 -0
  143. package/screens/components/Notification/Error/ErrorMessage.module.css +57 -0
  144. package/screens/components/Notification/Error/ErrorMessage.stories.jsx +37 -0
  145. package/screens/components/PasswordResetPanel/index.jsx +325 -0
  146. package/screens/components/PasswordResetPanel/index.stories.jsx +26 -0
  147. package/screens/components/PlaceholderButton/index.jsx +169 -0
  148. package/screens/components/PlaceholderButton/index.module.css +162 -0
  149. package/screens/components/PlaceholderButton/index.stories.jsx +69 -0
  150. package/screens/components/PlaceholderButton/utils.js +42 -0
  151. package/screens/components/Profile/Avatar/index.jsx +125 -0
  152. package/screens/components/Profile/Avatar/index.module.css +86 -0
  153. package/screens/components/Profile/Avatar/index.stories.jsx +48 -0
  154. package/screens/components/Profile/Avatar/utils.js +18 -0
  155. package/screens/components/Profile/index.jsx +58 -0
  156. package/screens/components/Profile/index.module.css +0 -0
  157. package/screens/components/Profile/index.stories.jsx +38 -0
  158. package/screens/components/Profile/utils.js +0 -0
  159. package/screens/components/ScrollBar/index.jsx +62 -0
  160. package/screens/components/ScrollBar/index.module.css +78 -0
  161. package/screens/components/ScrollBar/index.stories.jsx +64 -0
  162. package/screens/components/ScrollBar/utils.js +127 -0
  163. package/screens/components/Sidebar/constants.js +16 -0
  164. package/screens/components/Sidebar/index.jsx +399 -0
  165. package/screens/components/Sidebar/index.module.css +405 -0
  166. package/screens/components/Sidebar/index.stories.jsx +86 -0
  167. package/screens/components/Sidebar/utils.js +75 -0
  168. package/screens/components/Skeleton/index.jsx +11 -0
  169. package/screens/components/Skeleton/index.module.css +26 -0
  170. package/screens/components/Skeleton/index.stories.jsx +42 -0
  171. package/screens/components/SubSidebarLayout/index.jsx +44 -0
  172. package/screens/components/SubSidebarLayout/index.module.css +80 -0
  173. package/screens/components/SubSidebarLayout/index.stories.jsx +51 -0
  174. package/screens/components/SummaryCard/index.jsx +50 -0
  175. package/screens/components/SummaryCard/index.module.css +30 -0
  176. package/screens/components/SummaryCard/index.stories.jsx +29 -0
  177. package/screens/components/Swich/constants.js +4 -0
  178. package/screens/components/Swich/index.jsx +85 -0
  179. package/screens/components/Swich/index.module.css +68 -0
  180. package/screens/components/Swich/index.stories.jsx +44 -0
  181. package/screens/components/Swich/utils.js +0 -0
  182. package/screens/components/TableA/index.jsx +156 -0
  183. package/screens/components/TableA/index.module.css +105 -0
  184. package/screens/components/TableA/index.stories.jsx +116 -0
  185. package/screens/components/TableA/utils.js +11 -0
  186. package/screens/components/TableB/index.jsx +222 -0
  187. package/screens/components/TableB/index.module.css +295 -0
  188. package/screens/components/TableB/index.stories.jsx +110 -0
  189. package/screens/components/TableB/utils.js +11 -0
  190. package/screens/components/ToggleCard/index.jsx +64 -0
  191. package/screens/components/ToggleCard/index.module.css +111 -0
  192. package/screens/components/ToggleCard/index.stories.jsx +49 -0
  193. package/screens/components/UserEditPopover/index.jsx +102 -0
  194. package/screens/components/UserEditPopover/index.module.css +97 -0
  195. package/screens/components/UserEditPopover/index.stories.jsx +49 -0
  196. package/scripts/build.mjs +129 -0
  197. package/scripts/check-case.mjs +127 -0
  198. package/scripts/verify-dist.mjs +45 -0
  199. package/vitest.config.js +36 -0
@@ -0,0 +1,49 @@
1
+ import React, { useState } from 'react';
2
+ import Paginador from './index';
3
+
4
+ export default {
5
+ title: 'Components/Material-UI/Paginador',
6
+ component: Paginador,
7
+ };
8
+
9
+ const ControlledTemplate = (args) => {
10
+ const [page, setPage] = useState(args.page || 1);
11
+ const [pageSize, setPageSize] = useState(args.pageSize);
12
+ return (
13
+ <Paginador
14
+ {...args}
15
+ page={page}
16
+ onChange={(e, value) => setPage(value)}
17
+ pageSize={pageSize}
18
+ onPageSizeChange={args.onPageSizeChange && setPageSize}
19
+ />
20
+ );
21
+ };
22
+
23
+ export const Default = {
24
+ render: (args) => <ControlledTemplate {...args} />,
25
+ args: {
26
+ count: 10,
27
+ page: 1,
28
+ },
29
+ };
30
+
31
+ export const WithPageSizeSelector = {
32
+ render: (args) => <ControlledTemplate {...args} />,
33
+ args: {
34
+ count: 15,
35
+ page: 2,
36
+ pageSize: 10,
37
+ pageSizeOptions: [5, 10, 20, 50],
38
+ onPageSizeChange: () => {},
39
+ },
40
+ };
41
+
42
+ export const Disabled = {
43
+ render: (args) => <ControlledTemplate {...args} />,
44
+ args: {
45
+ count: 10,
46
+ page: 3,
47
+ disabled: true,
48
+ },
49
+ };
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import TextField from '@mui/material/TextField';
3
+ import Autocomplete from '@mui/material/Autocomplete';
4
+ import CircularProgress from '@mui/material/CircularProgress';
5
+
6
+ export default function Search({
7
+ open,
8
+ onOpen,
9
+ onClose,
10
+ options = [],
11
+ loading = false,
12
+ value,
13
+ onChange,
14
+ inputValue,
15
+ onInputChange,
16
+ getOptionLabel = (option) => option?.label || "",
17
+ isOptionEqualToValue = (option, val) => option?.value === val?.value,
18
+ label = "Buscar",
19
+ placeholder = "Buscar...",
20
+ freeSolo = false,
21
+ ...props
22
+ }) {
23
+ // Fallback to plain TextField if we are just doing local free text search without options
24
+ if (freeSolo && options.length === 0) {
25
+ return (
26
+ <TextField
27
+ value={value}
28
+ onChange={(e) => {
29
+ if (onChange) onChange(e.target.value);
30
+ }}
31
+ label={label}
32
+ placeholder={placeholder}
33
+ size="small"
34
+ fullWidth
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ return (
41
+ <Autocomplete
42
+ fullWidth
43
+ open={open}
44
+ onOpen={onOpen}
45
+ onClose={onClose}
46
+ value={value}
47
+ onChange={(event, newValue) => {
48
+ if (onChange) onChange(newValue);
49
+ }}
50
+ inputValue={inputValue}
51
+ onInputChange={(event, newInputValue) => {
52
+ if (onInputChange) onInputChange(newInputValue);
53
+ }}
54
+ isOptionEqualToValue={isOptionEqualToValue}
55
+ getOptionLabel={getOptionLabel}
56
+ options={options}
57
+ loading={loading}
58
+ renderInput={(params) => (
59
+ <TextField
60
+ {...params}
61
+ label={label}
62
+ placeholder={placeholder}
63
+ size="small"
64
+ slotProps={{
65
+ ...params.slotProps,
66
+ input: {
67
+ ...params.slotProps?.input,
68
+ endAdornment: (
69
+ <React.Fragment>
70
+ {loading ? <CircularProgress color="inherit" size={20} /> : null}
71
+ {params.slotProps?.input?.endAdornment}
72
+ </React.Fragment>
73
+ ),
74
+ },
75
+ }}
76
+ />
77
+ )}
78
+ {...props}
79
+ />
80
+ );
81
+ }
@@ -0,0 +1,68 @@
1
+ import React, { useState } from 'react';
2
+ import Search from './index';
3
+
4
+ export default {
5
+ title: 'Components/Material-UI/Search',
6
+ component: Search,
7
+ };
8
+
9
+ const options = [
10
+ { label: 'Ana Gomez', value: 1 },
11
+ { label: 'Luis Perez', value: 2 },
12
+ { label: 'Marta Ruiz', value: 3 },
13
+ ];
14
+
15
+ export const WithOptions = {
16
+ render: (args) => {
17
+ const [value, setValue] = useState(null);
18
+ const [inputValue, setInputValue] = useState('');
19
+ return (
20
+ <Search
21
+ {...args}
22
+ value={value}
23
+ onChange={setValue}
24
+ inputValue={inputValue}
25
+ onInputChange={setInputValue}
26
+ />
27
+ );
28
+ },
29
+ args: {
30
+ options,
31
+ label: 'Buscar usuario',
32
+ placeholder: 'Escribe un nombre...',
33
+ },
34
+ };
35
+
36
+ export const Loading = {
37
+ render: (args) => {
38
+ const [value, setValue] = useState(null);
39
+ const [inputValue, setInputValue] = useState('');
40
+ return (
41
+ <Search
42
+ {...args}
43
+ value={value}
44
+ onChange={setValue}
45
+ inputValue={inputValue}
46
+ onInputChange={setInputValue}
47
+ />
48
+ );
49
+ },
50
+ args: {
51
+ options: [],
52
+ loading: true,
53
+ label: 'Buscar usuario',
54
+ },
55
+ };
56
+
57
+ export const FreeSolo = {
58
+ render: (args) => {
59
+ const [value, setValue] = useState('');
60
+ return <Search {...args} value={value} onChange={setValue} />;
61
+ },
62
+ args: {
63
+ freeSolo: true,
64
+ options: [],
65
+ label: 'Buscar',
66
+ placeholder: 'Escribe texto libre...',
67
+ },
68
+ };
@@ -0,0 +1,100 @@
1
+ import * as React from 'react';
2
+ import Box from '@mui/material/Box';
3
+ import TextField from '@mui/material/TextField';
4
+ import Autocomplete from '@mui/material/Autocomplete';
5
+
6
+ export default function Selector({
7
+ label,
8
+ value,
9
+ onChange,
10
+ options = [],
11
+ id = "custom-autocomplete",
12
+ style = {},
13
+ className = "",
14
+ fullWidth = true,
15
+ placeholder,
16
+ required,
17
+ requiredColor = "red",
18
+ disableClearable = false
19
+ }) {
20
+ const normalizeVal = (val) => String(val || '').toLowerCase().replace(/[_\s\/-]+/g, '');
21
+
22
+ const selectedOption = (value !== "" && value !== undefined && value !== null)
23
+ ? options.find(opt =>
24
+ String(opt.value) === String(value) ||
25
+ normalizeVal(opt.value) === normalizeVal(value) ||
26
+ normalizeVal(opt.label) === normalizeVal(value)
27
+ ) || null
28
+ : null;
29
+
30
+ const displayLabel = label ? (
31
+ <span>
32
+ {label}
33
+ {required && <span style={{ color: requiredColor, marginLeft: '4px' }}>**</span>}
34
+ </span>
35
+ ) : undefined;
36
+
37
+ return (
38
+ <Autocomplete
39
+ id={id}
40
+ sx={{ width: fullWidth ? '100%' : 300, m: 0, ...style }}
41
+ className={className}
42
+ options={options}
43
+ autoHighlight
44
+ openOnFocus
45
+ disableClearable={disableClearable}
46
+ getOptionLabel={(option) => option.label || ""}
47
+ value={selectedOption}
48
+ onChange={(event, newValue) => {
49
+ onChange({ target: { value: newValue ? newValue.value : "" } });
50
+ }}
51
+ isOptionEqualToValue={(option, val) =>
52
+ String(option.value) === String(val?.value) ||
53
+ normalizeVal(option.value) === normalizeVal(val?.value) ||
54
+ normalizeVal(option.label) === normalizeVal(val?.label)
55
+ }
56
+ slotProps={{
57
+ popper: {
58
+ sx: { zIndex: 100000 }
59
+ }
60
+ }}
61
+ renderOption={(props, option) => {
62
+ const { key, ...optionProps } = props;
63
+ return (
64
+ <Box
65
+ key={key}
66
+ component="li"
67
+ sx={{ display: 'flex', alignItems: 'center', gap: '8px', ...optionProps.sx }}
68
+ {...optionProps}
69
+ >
70
+ {option.icon && <img src={option.icon} alt="" style={{ width: 24, height: 24, objectFit: 'contain' }} />}
71
+ {option.label}
72
+ </Box>
73
+ );
74
+ }}
75
+ renderInput={(params) => (
76
+ <TextField
77
+ {...params}
78
+ label={displayLabel}
79
+ placeholder={placeholder}
80
+ size="small"
81
+ inputProps={{
82
+ ...params.inputProps,
83
+ autoComplete: 'off',
84
+ form: {
85
+ autocomplete: 'off',
86
+ },
87
+ }}
88
+ slotProps={{
89
+ ...params.slotProps,
90
+ htmlInput: {
91
+ ...params.slotProps?.htmlInput,
92
+ autoComplete: 'off',
93
+ name: 'no-autofill-' + Math.random(),
94
+ },
95
+ }}
96
+ />
97
+ )}
98
+ />
99
+ );
100
+ }
@@ -0,0 +1,52 @@
1
+ import React, { useState } from 'react';
2
+ import Selector from './index';
3
+
4
+ export default {
5
+ title: 'Components/Material-UI/Selector',
6
+ component: Selector,
7
+ };
8
+
9
+ const options = [
10
+ { value: 'perro', label: 'Perro' },
11
+ { value: 'gato', label: 'Gato' },
12
+ { value: 'ave', label: 'Ave' },
13
+ ];
14
+
15
+ const ControlledTemplate = (args) => {
16
+ const [value, setValue] = useState(args.value || '');
17
+ return (
18
+ <Selector
19
+ {...args}
20
+ value={value}
21
+ onChange={(e) => setValue(e.target.value)}
22
+ />
23
+ );
24
+ };
25
+
26
+ export const Default = {
27
+ render: (args) => <ControlledTemplate {...args} />,
28
+ args: {
29
+ label: 'Especie',
30
+ options,
31
+ placeholder: 'Selecciona una especie',
32
+ },
33
+ };
34
+
35
+ export const Required = {
36
+ render: (args) => <ControlledTemplate {...args} />,
37
+ args: {
38
+ label: 'Especie',
39
+ options,
40
+ required: true,
41
+ placeholder: 'Selecciona una especie',
42
+ },
43
+ };
44
+
45
+ export const WithValue = {
46
+ render: (args) => <ControlledTemplate {...args} />,
47
+ args: {
48
+ label: 'Especie',
49
+ options,
50
+ value: 'gato',
51
+ },
52
+ };
@@ -0,0 +1,61 @@
1
+ import React, { forwardRef } from 'react';
2
+ import MuiTextField from '@mui/material/TextField';
3
+ import { formatHighlightedText } from '../../../GreenHighlight/utils';
4
+
5
+ const TextField = forwardRef(({
6
+ label,
7
+ value,
8
+ onChange,
9
+ error,
10
+ helperText,
11
+ type = "text",
12
+ variant = "outlined",
13
+ size = "small",
14
+ fullWidth = true,
15
+ placeholder,
16
+ required,
17
+ requiredColor = 'red',
18
+ bgColor = '#fff',
19
+ textColor = '#0f172a',
20
+ ...props
21
+ }, ref) => {
22
+ const formattedLabel = typeof label === 'string' ? formatHighlightedText(label) : label;
23
+ const displayLabel = formattedLabel ? (
24
+ <span>
25
+ {formattedLabel}
26
+ {required && <span style={{ color: requiredColor, marginLeft: '4px' }}>**</span>}
27
+ </span>
28
+ ) : undefined;
29
+
30
+ const displayHelperText = typeof helperText === 'string' ? formatHighlightedText(helperText) : helperText;
31
+
32
+ return (
33
+ <MuiTextField
34
+ label={displayLabel}
35
+ value={value}
36
+ onChange={onChange}
37
+ error={!!error}
38
+ helperText={error || displayHelperText}
39
+ type={type}
40
+ variant={variant}
41
+ size={size}
42
+ fullWidth={fullWidth}
43
+ placeholder={placeholder}
44
+ sx={{
45
+ m: 0,
46
+ '& .MuiInputBase-root': { backgroundColor: bgColor },
47
+ '& input:-webkit-autofill': {
48
+ WebkitBoxShadow: `0 0 0 1000px ${bgColor} inset`,
49
+ WebkitTextFillColor: textColor,
50
+ caretColor: textColor,
51
+ },
52
+ }}
53
+ inputRef={ref}
54
+ {...props}
55
+ />
56
+ );
57
+ });
58
+
59
+ TextField.displayName = 'TextField';
60
+
61
+ export default TextField;
@@ -0,0 +1,53 @@
1
+ import React, { useState } from 'react';
2
+ import TextField from './index';
3
+
4
+ export default {
5
+ title: 'Components/Material-UI/TextField',
6
+ component: TextField,
7
+ };
8
+
9
+ const ControlledTemplate = (args) => {
10
+ const [value, setValue] = useState(args.value || '');
11
+ return (
12
+ <TextField
13
+ {...args}
14
+ value={value}
15
+ onChange={(e) => setValue(e.target.value)}
16
+ />
17
+ );
18
+ };
19
+
20
+ export const Default = {
21
+ render: (args) => <ControlledTemplate {...args} />,
22
+ args: {
23
+ label: 'Nombre',
24
+ placeholder: 'Escribe tu nombre...',
25
+ },
26
+ };
27
+
28
+ export const Required = {
29
+ render: (args) => <ControlledTemplate {...args} />,
30
+ args: {
31
+ label: 'Correo electrónico',
32
+ placeholder: 'usuario@correo.com',
33
+ required: true,
34
+ },
35
+ };
36
+
37
+ export const WithError = {
38
+ render: (args) => <ControlledTemplate {...args} />,
39
+ args: {
40
+ label: 'Contraseña',
41
+ type: 'password',
42
+ error: true,
43
+ helperText: 'La contraseña debe tener al menos 6 caracteres',
44
+ },
45
+ };
46
+
47
+ export const Disabled = {
48
+ args: {
49
+ label: 'Correo electrónico (Autocompletado)',
50
+ value: 'usuario@correo.com',
51
+ disabled: true,
52
+ },
53
+ };
@@ -0,0 +1,122 @@
1
+ import React, { useState, useRef, useEffect, useCallback } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import styles from "./ModalTooltip.module.css";
4
+
5
+ const ModalTooltip = ({
6
+ text,
7
+ children,
8
+ position = "top",
9
+ delay = 150,
10
+ style,
11
+ bgColor,
12
+ textColor,
13
+ ...props
14
+ }) => {
15
+ const [visible, setVisible] = useState(false);
16
+ const [activePos, setActivePos] = useState(position);
17
+ const [coords, setCoords] = useState({ top: 0, left: 0 });
18
+
19
+ const triggerRef = useRef(null);
20
+ const tooltipRef = useRef(null);
21
+ const timeoutRef = useRef(null);
22
+
23
+ // Cálculo preciso de posición con prevención de colisiones con los bordes del viewport
24
+ const updatePosition = useCallback(() => {
25
+ if (!triggerRef.current) return;
26
+
27
+ const triggerRect = triggerRef.current.getBoundingClientRect();
28
+ const gap = 8; // Distancia entre el trigger y el tooltip
29
+ let currentPos = position;
30
+
31
+ // Dimensiones reales del tooltip
32
+ const tooltipWidth = tooltipRef.current ? tooltipRef.current.offsetWidth : 200;
33
+ const tooltipHeight = tooltipRef.current ? tooltipRef.current.offsetHeight : 36;
34
+
35
+ // Inversión automática si colisiona arriba/abajo
36
+ if (position === "top" && triggerRect.top - tooltipHeight - gap < 0) {
37
+ currentPos = "bottom";
38
+ } else if (position === "bottom" && triggerRect.bottom + tooltipHeight + gap > window.innerHeight) {
39
+ currentPos = "top";
40
+ }
41
+
42
+ setActivePos(currentPos);
43
+
44
+ // Calcular Top
45
+ let top = currentPos === "bottom"
46
+ ? triggerRect.bottom + window.scrollY + gap
47
+ : triggerRect.top + window.scrollY - gap;
48
+
49
+ // Calcular Left con clamp para que nunca se salga horizontalmente
50
+ let left = triggerRect.left + window.scrollX + triggerRect.width / 2;
51
+ const minLeft = tooltipWidth / 2 + 10;
52
+ const maxLeft = window.innerWidth - tooltipWidth / 2 - 10;
53
+ left = Math.max(minLeft, Math.min(left, maxLeft));
54
+
55
+ setCoords({ top, left });
56
+ }, [position]);
57
+
58
+ // Listener global de scroll y resize cuando está visible
59
+ useEffect(() => {
60
+ if (!visible) return;
61
+
62
+ updatePosition();
63
+
64
+ const handleScrollOrResize = () => updatePosition();
65
+ window.addEventListener("scroll", handleScrollOrResize, true);
66
+ window.addEventListener("resize", handleScrollOrResize);
67
+
68
+ return () => {
69
+ window.removeEventListener("scroll", handleScrollOrResize, true);
70
+ window.removeEventListener("resize", handleScrollOrResize);
71
+ };
72
+ }, [visible, updatePosition]);
73
+
74
+ const handleMouseEnter = () => {
75
+ timeoutRef.current = setTimeout(() => {
76
+ setVisible(true);
77
+ }, delay);
78
+ };
79
+
80
+ const handleMouseLeave = () => {
81
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
82
+ setVisible(false);
83
+ };
84
+
85
+ return (
86
+ <span
87
+ ref={triggerRef}
88
+ className={styles.container}
89
+ onMouseEnter={handleMouseEnter}
90
+ onMouseLeave={handleMouseLeave}
91
+ style={{
92
+ display: "inline-flex",
93
+ alignItems: "center",
94
+ position: "relative",
95
+ ...style
96
+ }}
97
+ {...props}
98
+ >
99
+ {children}
100
+ {visible &&
101
+ typeof document !== "undefined" &&
102
+ createPortal(
103
+ <div
104
+ ref={tooltipRef}
105
+ className={`${styles.tooltip} ${styles[activePos]}`}
106
+ style={{
107
+ top: `${coords.top}px`,
108
+ left: `${coords.left}px`,
109
+ ...(bgColor ? { '--modaltooltip-bg': bgColor } : {}),
110
+ ...(textColor ? { '--modaltooltip-text': textColor } : {}),
111
+ }}
112
+ >
113
+ {text}
114
+ <span className={styles.arrow} />
115
+ </div>,
116
+ document.body
117
+ )}
118
+ </span>
119
+ );
120
+ };
121
+
122
+ export default ModalTooltip;
@@ -0,0 +1,65 @@
1
+ .container {
2
+ cursor: pointer;
3
+ }
4
+
5
+ .tooltip {
6
+ position: absolute;
7
+ z-index: 99999;
8
+ width: max-content;
9
+ max-width: 320px;
10
+ background-color: var(--modaltooltip-bg, #0f172a); /* Slate 900 */
11
+ color: var(--modaltooltip-text, #f8fafc);
12
+ padding: 6px 12px;
13
+ border-radius: 8px;
14
+ font-size: 12px;
15
+ font-weight: 500;
16
+ line-height: 1.4;
17
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.25), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
18
+ border: 1px solid rgba(255, 255, 255, 0.1);
19
+ pointer-events: none;
20
+ animation: tooltipFadeIn 0.18s cubic-bezier(0.16, 1, 0.3, 1) forwards;
21
+ }
22
+
23
+ /* Posicionamiento y Transformaciones */
24
+ .top {
25
+ transform: translate(-50%, -100%);
26
+ }
27
+
28
+ .bottom {
29
+ transform: translate(-50%, 0);
30
+ }
31
+
32
+ /* Flecha direccional */
33
+ .arrow {
34
+ position: absolute;
35
+ width: 8px;
36
+ height: 8px;
37
+ background-color: var(--modaltooltip-bg, #0f172a);
38
+ transform: rotate(45deg);
39
+ left: 50%;
40
+ margin-left: -4px;
41
+ }
42
+
43
+ .top .arrow {
44
+ bottom: -4px;
45
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
46
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
47
+ }
48
+
49
+ .bottom .arrow {
50
+ top: -4px;
51
+ border-left: 1px solid rgba(255, 255, 255, 0.1);
52
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
53
+ }
54
+
55
+ /* Animación de entrada con Scale suave */
56
+ @keyframes tooltipFadeIn {
57
+ from {
58
+ opacity: 0;
59
+ scale: 0.92;
60
+ }
61
+ to {
62
+ opacity: 1;
63
+ scale: 1;
64
+ }
65
+ }
@@ -0,0 +1,39 @@
1
+ import ModalTooltip from './ModalTooltip';
2
+
3
+ export default {
4
+ title: 'Components/ModalTooltip',
5
+ component: ModalTooltip,
6
+ };
7
+
8
+ export const Top = {
9
+ args: {
10
+ text: 'Tooltip on top',
11
+ position: 'top',
12
+ children: <button type="button">Hover me</button>,
13
+ },
14
+ };
15
+
16
+ export const Bottom = {
17
+ args: {
18
+ text: 'Tooltip on bottom',
19
+ position: 'bottom',
20
+ children: <button type="button">Hover me</button>,
21
+ },
22
+ };
23
+
24
+ export const WithDelay = {
25
+ args: {
26
+ text: 'Shows after a longer delay',
27
+ position: 'top',
28
+ delay: 600,
29
+ children: <button type="button">Slow tooltip</button>,
30
+ },
31
+ };
32
+
33
+ export const OnText = {
34
+ args: {
35
+ text: 'More information about this label',
36
+ position: 'right',
37
+ children: <span style={{ textDecoration: 'underline dotted' }}>Hover this text</span>,
38
+ },
39
+ };