@soyfri/shared-library 1.5.0 → 2.0.0-beta.1
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.
- package/build.js +75 -38
- package/dist/components/ActionMenu/ActionMenu.cjs +107 -0
- package/dist/components/ActionMenu/ActionMenu.cjs.map +1 -0
- package/dist/components/ActionMenu/ActionMenu.d.ts +60 -0
- package/dist/components/ActionMenu/ActionMenu.js +107 -0
- package/dist/components/ActionMenu/ActionMenu.js.map +1 -0
- package/dist/components/ActionMenu/index.d.ts +2 -0
- package/dist/components/ActionMenu.d.ts +6 -0
- package/dist/components/AppBar/AppBar.cjs +346 -0
- package/dist/components/AppBar/AppBar.cjs.map +1 -0
- package/dist/components/AppBar/AppBar.d.ts +55 -0
- package/dist/components/AppBar/AppBar.js +346 -0
- package/dist/components/AppBar/AppBar.js.map +1 -0
- package/dist/components/AppBar/AppBar.sx.d.ts +12 -0
- package/dist/components/AppBar/AppBarBrand.d.ts +31 -0
- package/dist/components/AppBar/AppBarContext.d.ts +18 -0
- package/dist/components/AppBar/AppBarMenuToggle.d.ts +39 -0
- package/dist/components/AppBar/AppBarUserMenu.d.ts +65 -0
- package/dist/components/AppBar/index.d.ts +12 -0
- package/dist/components/AppBar.d.ts +6 -0
- package/dist/components/Autocomplete/Autocomplete.cjs +259 -54
- package/dist/components/Autocomplete/Autocomplete.cjs.map +1 -1
- package/dist/components/Autocomplete/Autocomplete.d.ts +64 -9
- package/dist/components/Autocomplete/Autocomplete.definitions.d.ts +6 -0
- package/dist/components/Autocomplete/Autocomplete.helpers.d.ts +18 -0
- package/dist/components/Autocomplete/Autocomplete.js +261 -56
- package/dist/components/Autocomplete/Autocomplete.js.map +1 -1
- package/dist/components/Autocomplete/Autocomplete.sx.d.ts +7 -0
- package/dist/components/Autocomplete/Autocomplete.types.d.ts +1 -0
- package/dist/components/Autocomplete/_parts/AutocompleteChips.d.ts +19 -0
- package/dist/components/Autocomplete/_parts/AutocompleteLoader.d.ts +9 -0
- package/dist/components/Autocomplete/_parts/AutocompleteOption.d.ts +19 -0
- package/dist/components/Autocomplete/index.d.ts +2 -1
- package/dist/components/Autocomplete.d.ts +4 -0
- package/dist/components/Avatar/Avatar.cjs +116 -79
- package/dist/components/Avatar/Avatar.cjs.map +1 -1
- package/dist/components/Avatar/Avatar.d.ts +16 -2
- package/dist/components/Avatar/Avatar.definitions.d.ts +11 -0
- package/dist/components/Avatar/Avatar.js +117 -80
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Card/Card.cjs +168 -9
- package/dist/components/Card/Card.cjs.map +1 -1
- package/dist/components/Card/Card.d.ts +78 -8
- package/dist/components/Card/Card.js +170 -11
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/components/Card/Card.sx.d.ts +17 -0
- package/dist/components/Card/index.d.ts +4 -1
- package/dist/components/Card.d.ts +4 -0
- package/dist/components/DatePicker/DatePicker.cjs +201 -3
- package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/components/DatePicker/DatePicker.d.ts +47 -9
- package/dist/components/DatePicker/DatePicker.definitions.d.ts +1 -0
- package/dist/components/DatePicker/DatePicker.helpers.d.ts +7 -0
- package/dist/components/DatePicker/DatePicker.js +200 -2
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.sx.d.ts +9 -0
- package/dist/components/DatePicker/DatePicker.types.d.ts +1 -0
- package/dist/components/DatePicker/index.d.ts +2 -1
- package/dist/components/DatePicker.d.ts +4 -0
- package/dist/components/DateTimePicker/DateTimePicker.cjs +152 -138
- package/dist/components/DateTimePicker/DateTimePicker.cjs.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.d.ts +46 -9
- package/dist/components/DateTimePicker/DateTimePicker.definitions.d.ts +1 -0
- package/dist/components/DateTimePicker/DateTimePicker.helpers.d.ts +11 -0
- package/dist/components/DateTimePicker/DateTimePicker.js +152 -138
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.sx.d.ts +7 -0
- package/dist/components/DateTimePicker/DateTimePicker.types.d.ts +1 -0
- package/dist/components/DateTimePicker/index.d.ts +2 -1
- package/dist/components/DateTimePicker.d.ts +4 -0
- package/dist/components/Drawer/Drawer.cjs +271 -0
- package/dist/components/Drawer/Drawer.cjs.map +1 -0
- package/dist/components/Drawer/Drawer.d.ts +51 -0
- package/dist/components/Drawer/Drawer.js +271 -0
- package/dist/components/Drawer/Drawer.js.map +1 -0
- package/dist/components/Drawer/Drawer.sx.d.ts +23 -0
- package/dist/components/Drawer/DrawerContext.d.ts +18 -0
- package/dist/components/Drawer/DrawerItem.d.ts +35 -0
- package/dist/components/Drawer/index.d.ts +6 -0
- package/dist/components/Drawer.d.ts +6 -0
- package/dist/components/Icon/Icon.cjs +44 -3
- package/dist/components/Icon/Icon.cjs.map +1 -1
- package/dist/components/Icon/Icon.d.ts +34 -1
- package/dist/components/Icon/Icon.js +44 -3
- package/dist/components/Icon/Icon.js.map +1 -1
- package/dist/components/Input/Input.cjs +173 -3
- package/dist/components/Input/Input.cjs.map +1 -1
- package/dist/components/Input/Input.d.ts +20 -15
- package/dist/components/Input/Input.definitions.d.ts +5 -2
- package/dist/components/Input/Input.helpers.d.ts +14 -0
- package/dist/components/Input/Input.js +172 -2
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/Input/Input.sx.d.ts +8 -0
- package/dist/components/Input/Input.types.d.ts +1 -0
- package/dist/components/Input/index.d.ts +2 -1
- package/dist/components/Input.d.ts +4 -0
- package/dist/components/InputGroup/InputGroup.cjs +104 -91
- package/dist/components/InputGroup/InputGroup.cjs.map +1 -1
- package/dist/components/InputGroup/InputGroup.d.ts +37 -1
- package/dist/components/InputGroup/InputGroup.definitions.d.ts +6 -0
- package/dist/components/InputGroup/InputGroup.js +106 -93
- package/dist/components/InputGroup/InputGroup.js.map +1 -1
- package/dist/components/Modal/Modal.cjs +226 -116
- package/dist/components/Modal/Modal.cjs.map +1 -1
- package/dist/components/Modal/Modal.d.ts +38 -2
- package/dist/components/Modal/Modal.js +227 -117
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Modal/ModalFooter.d.ts +9 -1
- package/dist/components/Modal/index.d.ts +5 -0
- package/dist/components/PageLoader/PageLoader.cjs +61 -0
- package/dist/components/PageLoader/PageLoader.cjs.map +1 -0
- package/dist/components/PageLoader/PageLoader.d.ts +38 -0
- package/dist/components/PageLoader/PageLoader.js +61 -0
- package/dist/components/PageLoader/PageLoader.js.map +1 -0
- package/dist/components/PageLoader/index.d.ts +2 -0
- package/dist/components/PageLoader.d.ts +6 -0
- package/dist/components/ScrollTopButton/ScrollTopButton.cjs +79 -0
- package/dist/components/ScrollTopButton/ScrollTopButton.cjs.map +1 -0
- package/dist/components/ScrollTopButton/ScrollTopButton.d.ts +48 -0
- package/dist/components/ScrollTopButton/ScrollTopButton.js +79 -0
- package/dist/components/ScrollTopButton/ScrollTopButton.js.map +1 -0
- package/dist/components/ScrollTopButton/index.d.ts +4 -0
- package/dist/components/ScrollTopButton/scrollToTop.d.ts +29 -0
- package/dist/components/ScrollTopButton.d.ts +6 -0
- package/dist/components/Select/Select.cjs +446 -4
- package/dist/components/Select/Select.cjs.map +1 -1
- package/dist/components/Select/Select.d.ts +33 -13
- package/dist/components/Select/Select.definitions.d.ts +3 -0
- package/dist/components/Select/Select.helpers.d.ts +28 -0
- package/dist/components/Select/Select.js +445 -3
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.sx.d.ts +7 -0
- package/dist/components/Select/Select.types.d.ts +1 -0
- package/dist/components/Select/_parts/SelectMenuItem.d.ts +20 -0
- package/dist/components/Select/_parts/SelectSearchHeader.d.ts +15 -0
- package/dist/components/Select/_parts/SelectValue.d.ts +22 -0
- package/dist/components/Select/index.d.ts +2 -1
- package/dist/components/Select.d.ts +4 -0
- package/dist/components/Stat/Stat.cjs +1 -1
- package/dist/components/Stat/Stat.js +1 -1
- package/dist/components/Stepper/Stepper.cjs +4 -1
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.d.ts +5 -0
- package/dist/components/Stepper/Stepper.js +4 -1
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/_shared/formField.sx.d.ts +33 -0
- package/dist/components/_shared/resolvePreset.d.ts +18 -0
- package/dist/formField.sx-CQ1mbk9M.cjs +76 -0
- package/dist/formField.sx-CQ1mbk9M.cjs.map +1 -0
- package/dist/formField.sx-DfVbMe0V.js +77 -0
- package/dist/formField.sx-DfVbMe0V.js.map +1 -0
- package/dist/hooks/Wizard/Wizard.cjs +7 -0
- package/dist/hooks/Wizard/Wizard.cjs.map +1 -0
- package/dist/hooks/Wizard/Wizard.js +7 -0
- package/dist/hooks/Wizard/Wizard.js.map +1 -0
- package/dist/hooks/Wizard/WizardContext.d.ts +67 -0
- package/dist/hooks/Wizard/index.d.ts +3 -0
- package/dist/hooks/Wizard/useWizard.d.ts +9 -0
- package/dist/hooks/Wizard.d.ts +2 -0
- package/dist/index.cjs +99 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +31 -2
- package/dist/index.js.map +1 -1
- package/dist/mui.d.ts +5 -0
- package/dist/resolvePreset-B-IB0ehH.js +15 -0
- package/dist/resolvePreset-B-IB0ehH.js.map +1 -0
- package/dist/resolvePreset-CT3kU-K2.cjs +14 -0
- package/dist/resolvePreset-CT3kU-K2.cjs.map +1 -0
- package/dist/styles.css +3 -112
- package/dist/theme/componentStyles.d.ts +32 -0
- package/dist/theme/tokens.d.ts +28 -0
- package/dist/useWizard-CWdIxZzX.cjs +94 -0
- package/dist/useWizard-CWdIxZzX.cjs.map +1 -0
- package/dist/useWizard-CWq--C3o.js +95 -0
- package/dist/useWizard-CWq--C3o.js.map +1 -0
- package/package.json +1 -1
- package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
- package/src/components/ActionMenu/ActionMenu.tsx +174 -0
- package/src/components/ActionMenu/index.ts +2 -0
- package/src/components/AppBar/AppBar.stories.tsx +272 -0
- package/src/components/AppBar/AppBar.sx.ts +32 -0
- package/src/components/AppBar/AppBar.tsx +123 -0
- package/src/components/AppBar/AppBarBrand.tsx +120 -0
- package/src/components/AppBar/AppBarContext.ts +25 -0
- package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
- package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
- package/src/components/AppBar/index.ts +25 -0
- package/src/components/Autocomplete/Autocomplete.definitions.ts +223 -0
- package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
- package/src/components/Autocomplete/Autocomplete.stories.tsx +363 -2
- package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
- package/src/components/Autocomplete/Autocomplete.tsx +312 -90
- package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
- package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
- package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
- package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
- package/src/components/Autocomplete/index.ts +12 -1
- package/src/components/Avatar/Avatar.definitions.ts +162 -0
- package/src/components/Avatar/Avatar.stories.tsx +205 -1
- package/src/components/Avatar/Avatar.tsx +166 -103
- package/src/components/Card/Card.stories.tsx +205 -16
- package/src/components/Card/Card.sx.ts +104 -0
- package/src/components/Card/Card.tsx +191 -35
- package/src/components/Card/index.ts +9 -1
- package/src/components/DatePicker/DatePicker.definitions.ts +24 -1
- package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +29 -2
- package/src/components/DatePicker/DatePicker.sx.ts +33 -0
- package/src/components/DatePicker/DatePicker.tsx +163 -139
- package/src/components/DatePicker/DatePicker.types.ts +10 -0
- package/src/components/DatePicker/index.ts +9 -1
- package/src/components/DateTimePicker/DateTimePicker.definitions.ts +24 -0
- package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
- package/src/components/DateTimePicker/DateTimePicker.stories.tsx +29 -1
- package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +200 -166
- package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
- package/src/components/DateTimePicker/index.ts +9 -1
- package/src/components/Drawer/Drawer.stories.tsx +270 -0
- package/src/components/Drawer/Drawer.sx.ts +106 -0
- package/src/components/Drawer/Drawer.tsx +214 -0
- package/src/components/Drawer/DrawerContext.ts +26 -0
- package/src/components/Drawer/DrawerItem.tsx +110 -0
- package/src/components/Drawer/index.ts +10 -0
- package/src/components/Flyout/Flyout.stories.tsx +26 -18
- package/src/components/Icon/Icon.stories.tsx +68 -1
- package/src/components/Icon/Icon.tsx +87 -6
- package/src/components/Input/Input.definitions.ts +74 -2
- package/src/components/Input/Input.helpers.ts +49 -0
- package/src/components/Input/Input.stories.tsx +116 -4
- package/src/components/Input/Input.sx.ts +42 -0
- package/src/components/Input/Input.tsx +117 -162
- package/src/components/Input/Input.types.ts +10 -0
- package/src/components/Input/index.ts +9 -1
- package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
- package/src/components/InputGroup/InputGroup.stories.tsx +159 -28
- package/src/components/InputGroup/InputGroup.tsx +159 -116
- package/src/components/Modal/Modal.stories.tsx +434 -6
- package/src/components/Modal/Modal.tsx +303 -121
- package/src/components/Modal/ModalFooter.tsx +22 -12
- package/src/components/Modal/index.ts +6 -1
- package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
- package/src/components/PageLoader/PageLoader.tsx +96 -0
- package/src/components/PageLoader/index.ts +2 -0
- package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
- package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
- package/src/components/ScrollTopButton/index.ts +8 -0
- package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
- package/src/components/Select/Select.definitions.ts +114 -0
- package/src/components/Select/Select.helpers.ts +71 -0
- package/src/components/Select/Select.stories.tsx +126 -8
- package/src/components/Select/Select.sx.ts +14 -0
- package/src/components/Select/Select.tsx +246 -285
- package/src/components/Select/Select.types.ts +15 -0
- package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
- package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
- package/src/components/Select/_parts/SelectValue.tsx +96 -0
- package/src/components/Select/index.ts +14 -1
- package/src/components/Stepper/Stepper.tsx +17 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +15 -3
- package/src/components/_shared/formField.sx.ts +118 -0
- package/src/components/_shared/resolvePreset.ts +35 -0
- package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
- package/src/hooks/Wizard/WizardContext.tsx +166 -0
- package/src/hooks/Wizard/index.ts +6 -0
- package/src/hooks/Wizard/useWizard.ts +13 -0
- package/src/index.ts +17 -1
- package/src/mui.ts +44 -0
- package/src/theme/componentStyles.ts +47 -0
- package/src/theme/tokens.ts +43 -0
- package/dist/DatePicker-BSNboVhN.js +0 -201
- package/dist/DatePicker-BSNboVhN.js.map +0 -1
- package/dist/DatePicker-BoqxWAhj.cjs +0 -200
- package/dist/DatePicker-BoqxWAhj.cjs.map +0 -1
- package/dist/Input-DFHs7cJ_.js +0 -171
- package/dist/Input-DFHs7cJ_.js.map +0 -1
- package/dist/Input-c8MwNNPg.cjs +0 -170
- package/dist/Input-c8MwNNPg.cjs.map +0 -1
- package/dist/Select-BO2N56sm.cjs +0 -411
- package/dist/Select-BO2N56sm.cjs.map +0 -1
- package/dist/Select-BcLkyHSE.js +0 -412
- package/dist/Select-BcLkyHSE.js.map +0 -1
- package/dist/index.css +0 -3
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
import type { SelectOption } from './Autocomplete';
|
|
3
|
+
|
|
4
|
+
/** Resuelve un value único a su SelectOption dentro del pool. */
|
|
5
|
+
export const resolveSingleValue = (
|
|
6
|
+
options: SelectOption[],
|
|
7
|
+
value: SelectOption['value'] | null | undefined,
|
|
8
|
+
): SelectOption | null => options.find((o) => o.value === value) ?? null;
|
|
9
|
+
|
|
10
|
+
/** Resuelve un array de values a sus SelectOption dentro del pool. */
|
|
11
|
+
export const resolveMultipleValue = (
|
|
12
|
+
options: SelectOption[],
|
|
13
|
+
value: SelectOption['value'][] | null | undefined,
|
|
14
|
+
): SelectOption[] => {
|
|
15
|
+
if (!Array.isArray(value)) return [];
|
|
16
|
+
return options.filter((o) => value.includes(o.value));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Merge de sx base + extra del consumer, respetando el contrato de MUI. */
|
|
20
|
+
export const mergeSx = (
|
|
21
|
+
base: SxProps<Theme>,
|
|
22
|
+
extra?: SxProps<Theme>,
|
|
23
|
+
): SxProps<Theme> => {
|
|
24
|
+
if (!extra) return base;
|
|
25
|
+
return [base, ...(Array.isArray(extra) ? extra : [extra])] as SxProps<Theme>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compara dos valores resueltos (single o multiple) por value para decidir
|
|
30
|
+
* si son estructuralmente iguales. Se usa para estabilizar la referencia que
|
|
31
|
+
* se pasa a MUI Autocomplete (MUI compara value por referencia con `!==`,
|
|
32
|
+
* y una nueva referencia — aunque el contenido sea igual — dispara
|
|
33
|
+
* `resetInputValue` y borra el texto mientras el usuario tipea).
|
|
34
|
+
*/
|
|
35
|
+
export const areResolvedValuesEqual = (
|
|
36
|
+
prev: SelectOption | SelectOption[] | null,
|
|
37
|
+
next: SelectOption | SelectOption[] | null,
|
|
38
|
+
multiple: boolean,
|
|
39
|
+
): boolean => {
|
|
40
|
+
if (multiple) {
|
|
41
|
+
if (!Array.isArray(prev) || !Array.isArray(next)) return false;
|
|
42
|
+
if (prev.length !== next.length) return false;
|
|
43
|
+
return prev.every((o, i) => o.value === next[i]?.value);
|
|
44
|
+
}
|
|
45
|
+
return (
|
|
46
|
+
(prev as SelectOption | null)?.value ===
|
|
47
|
+
(next as SelectOption | null)?.value
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Determina si un valor resuelto está vacío (para placeholder/shrink). */
|
|
52
|
+
export const isResolvedValueEmpty = (
|
|
53
|
+
resolvedValue: unknown,
|
|
54
|
+
multiple: boolean,
|
|
55
|
+
): boolean => {
|
|
56
|
+
if (multiple) {
|
|
57
|
+
return !Array.isArray(resolvedValue) || resolvedValue.length === 0;
|
|
58
|
+
}
|
|
59
|
+
return !resolvedValue;
|
|
60
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from "react";
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
3
|
+
import { Box, Typography, Avatar, Stack, Button } from "@mui/material";
|
|
4
|
+
import { useForm } from "react-hook-form";
|
|
4
5
|
|
|
5
6
|
import Autocomplete, { SelectOption } from "./Autocomplete";
|
|
6
7
|
import {
|
|
@@ -12,6 +13,12 @@ import {
|
|
|
12
13
|
MultipleWithLimitAutocompleteDefinition,
|
|
13
14
|
SimpleAutocompleteDefinition,
|
|
14
15
|
WithPlaceholderAutocompleteDefinition,
|
|
16
|
+
WithErrorAutocompleteDefinition,
|
|
17
|
+
LabelPositionAutocompleteDefinition,
|
|
18
|
+
CustomBorderRadiusAutocompleteDefinition,
|
|
19
|
+
EmptyWithPlaceholderAutocompleteDefinition,
|
|
20
|
+
RHFAutocompleteDefinition,
|
|
21
|
+
AsyncServiceAutocompleteDefinition,
|
|
15
22
|
} from "./Autocomplete.definitions";
|
|
16
23
|
|
|
17
24
|
// =============================================================================
|
|
@@ -385,3 +392,357 @@ export const ManyOptions: Story = {
|
|
|
385
392
|
},
|
|
386
393
|
},
|
|
387
394
|
};
|
|
395
|
+
|
|
396
|
+
// =============================================================================
|
|
397
|
+
// NUEVAS STORIES — API refactorizada
|
|
398
|
+
// =============================================================================
|
|
399
|
+
|
|
400
|
+
export const WithError: Story = {
|
|
401
|
+
render: () => {
|
|
402
|
+
const [value, setValue] = useState<string | null>(null);
|
|
403
|
+
return (
|
|
404
|
+
<Box sx={{ width: 300 }}>
|
|
405
|
+
<Autocomplete
|
|
406
|
+
label="Cantidad"
|
|
407
|
+
options={basicOptions}
|
|
408
|
+
value={value}
|
|
409
|
+
onChange={(val) => setValue(val as string | null)}
|
|
410
|
+
error={!value}
|
|
411
|
+
helperText={!value ? "Debes seleccionar una cantidad" : " "}
|
|
412
|
+
/>
|
|
413
|
+
</Box>
|
|
414
|
+
);
|
|
415
|
+
},
|
|
416
|
+
parameters: {
|
|
417
|
+
docs: {
|
|
418
|
+
description: {
|
|
419
|
+
story:
|
|
420
|
+
"Autocomplete en estado de error con `helperText`. Muestra la validación visual (borde + label + texto en rojo).",
|
|
421
|
+
},
|
|
422
|
+
source: { code: WithErrorAutocompleteDefinition.trim() },
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
export const LabelPosition: Story = {
|
|
428
|
+
render: () => {
|
|
429
|
+
const [a, setA] = useState<string | null>(null);
|
|
430
|
+
const [b, setB] = useState<string | null>(null);
|
|
431
|
+
return (
|
|
432
|
+
<Stack spacing={3} sx={{ width: 320 }}>
|
|
433
|
+
<Autocomplete
|
|
434
|
+
label="Outside (default)"
|
|
435
|
+
labelPosition="outside"
|
|
436
|
+
options={basicOptions}
|
|
437
|
+
value={a}
|
|
438
|
+
onChange={(val) => setA(val as string | null)}
|
|
439
|
+
/>
|
|
440
|
+
<Autocomplete
|
|
441
|
+
label="Floating"
|
|
442
|
+
labelPosition="floating"
|
|
443
|
+
options={basicOptions}
|
|
444
|
+
value={b}
|
|
445
|
+
onChange={(val) => setB(val as string | null)}
|
|
446
|
+
/>
|
|
447
|
+
</Stack>
|
|
448
|
+
);
|
|
449
|
+
},
|
|
450
|
+
parameters: {
|
|
451
|
+
docs: {
|
|
452
|
+
description: {
|
|
453
|
+
story:
|
|
454
|
+
"Comparación entre `labelPosition='outside'` (label arriba del input, consistente con Input/Select/DatePicker) y `labelPosition='floating'` (comportamiento nativo MUI dentro del notch).",
|
|
455
|
+
},
|
|
456
|
+
source: { code: LabelPositionAutocompleteDefinition.trim() },
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
export const CustomBorderRadius: Story = {
|
|
462
|
+
render: () => {
|
|
463
|
+
const [a, setA] = useState<string | null>(null);
|
|
464
|
+
const [b, setB] = useState<string | null>(null);
|
|
465
|
+
const [c, setC] = useState<string | null>(null);
|
|
466
|
+
return (
|
|
467
|
+
<Stack spacing={2} sx={{ width: 300 }}>
|
|
468
|
+
<Autocomplete
|
|
469
|
+
label="0"
|
|
470
|
+
borderRadius={0}
|
|
471
|
+
options={basicOptions}
|
|
472
|
+
value={a}
|
|
473
|
+
onChange={(v) => setA(v as string | null)}
|
|
474
|
+
/>
|
|
475
|
+
<Autocomplete
|
|
476
|
+
label="10 (default)"
|
|
477
|
+
borderRadius={10}
|
|
478
|
+
options={basicOptions}
|
|
479
|
+
value={b}
|
|
480
|
+
onChange={(v) => setB(v as string | null)}
|
|
481
|
+
/>
|
|
482
|
+
<Autocomplete
|
|
483
|
+
label="24 (pill)"
|
|
484
|
+
borderRadius={24}
|
|
485
|
+
options={basicOptions}
|
|
486
|
+
value={c}
|
|
487
|
+
onChange={(v) => setC(v as string | null)}
|
|
488
|
+
/>
|
|
489
|
+
</Stack>
|
|
490
|
+
);
|
|
491
|
+
},
|
|
492
|
+
parameters: {
|
|
493
|
+
docs: {
|
|
494
|
+
description: {
|
|
495
|
+
story:
|
|
496
|
+
"La prop `borderRadius` permite personalizar el radio del borde sin usar `sx` (acepta number en px o string).",
|
|
497
|
+
},
|
|
498
|
+
source: { code: CustomBorderRadiusAutocompleteDefinition.trim() },
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
export const EmptyWithPlaceholder: Story = {
|
|
504
|
+
render: () => {
|
|
505
|
+
const [value, setValue] = useState<string | null>(null);
|
|
506
|
+
const countries: SelectOption[] = [
|
|
507
|
+
{ value: "mx", label: "México" },
|
|
508
|
+
{ value: "co", label: "Colombia" },
|
|
509
|
+
{ value: "ar", label: "Argentina" },
|
|
510
|
+
];
|
|
511
|
+
return (
|
|
512
|
+
<Box sx={{ width: 300 }}>
|
|
513
|
+
<Autocomplete
|
|
514
|
+
label="País"
|
|
515
|
+
placeholder="Buscar país..."
|
|
516
|
+
options={countries}
|
|
517
|
+
value={value}
|
|
518
|
+
onChange={(val) => setValue(val as string | null)}
|
|
519
|
+
/>
|
|
520
|
+
<Typography sx={{ mt: 2 }} variant="caption" color="text.secondary">
|
|
521
|
+
Al estar vacío y sin foco, solo se ve el label como placeholder. Al
|
|
522
|
+
enfocar, el label sube y aparece el placeholder real.
|
|
523
|
+
</Typography>
|
|
524
|
+
</Box>
|
|
525
|
+
);
|
|
526
|
+
},
|
|
527
|
+
parameters: {
|
|
528
|
+
docs: {
|
|
529
|
+
description: {
|
|
530
|
+
story:
|
|
531
|
+
"Autocomplete vacío con placeholder. El placeholder solo se muestra cuando el campo está enfocado (mismo patrón que Select/Input).",
|
|
532
|
+
},
|
|
533
|
+
source: { code: EmptyWithPlaceholderAutocompleteDefinition.trim() },
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
export const WithReactHookForm: Story = {
|
|
539
|
+
render: () => {
|
|
540
|
+
type FormValues = { country: string | null };
|
|
541
|
+
const { control, handleSubmit, watch } = useForm<FormValues>({
|
|
542
|
+
defaultValues: { country: null },
|
|
543
|
+
});
|
|
544
|
+
const countries: SelectOption[] = [
|
|
545
|
+
{ value: "mx", label: "México" },
|
|
546
|
+
{ value: "co", label: "Colombia" },
|
|
547
|
+
{ value: "ar", label: "Argentina" },
|
|
548
|
+
];
|
|
549
|
+
|
|
550
|
+
const [submitted, setSubmitted] = useState<FormValues | null>(null);
|
|
551
|
+
|
|
552
|
+
return (
|
|
553
|
+
<Box
|
|
554
|
+
component="form"
|
|
555
|
+
onSubmit={handleSubmit((data) => setSubmitted(data))}
|
|
556
|
+
>
|
|
557
|
+
<Stack spacing={2} sx={{ width: 320 }}>
|
|
558
|
+
<Autocomplete
|
|
559
|
+
name="country"
|
|
560
|
+
control={control}
|
|
561
|
+
validation={{ required: "Campo requerido" }}
|
|
562
|
+
label="País"
|
|
563
|
+
placeholder="Seleccione..."
|
|
564
|
+
options={countries}
|
|
565
|
+
/>
|
|
566
|
+
<Button type="submit" variant="contained">
|
|
567
|
+
Guardar
|
|
568
|
+
</Button>
|
|
569
|
+
<Typography variant="caption" color="text.secondary">
|
|
570
|
+
Valor actual (watch): {JSON.stringify(watch("country"))}
|
|
571
|
+
</Typography>
|
|
572
|
+
{submitted && (
|
|
573
|
+
<Typography variant="caption" color="success.main">
|
|
574
|
+
Submit: {JSON.stringify(submitted)}
|
|
575
|
+
</Typography>
|
|
576
|
+
)}
|
|
577
|
+
</Stack>
|
|
578
|
+
</Box>
|
|
579
|
+
);
|
|
580
|
+
},
|
|
581
|
+
parameters: {
|
|
582
|
+
docs: {
|
|
583
|
+
description: {
|
|
584
|
+
story:
|
|
585
|
+
"Integración con React Hook Form usando `name`/`control`/`validation`. El error de validación se renderiza automáticamente como helperText.",
|
|
586
|
+
},
|
|
587
|
+
source: { code: RHFAutocompleteDefinition.trim() },
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// =============================================================================
|
|
593
|
+
// ASYNC — búsqueda remota contra un servicio
|
|
594
|
+
// =============================================================================
|
|
595
|
+
// Simulador de servicio: hace una "llamada" a una API fake con latencia.
|
|
596
|
+
const mockUsersDb: SelectOption[] = [
|
|
597
|
+
{ value: 1, label: "Andrea García" },
|
|
598
|
+
{ value: 2, label: "Andrés Pérez" },
|
|
599
|
+
{ value: 3, label: "Beatriz López" },
|
|
600
|
+
{ value: 4, label: "Carlos Ruiz" },
|
|
601
|
+
{ value: 5, label: "Camila Torres" },
|
|
602
|
+
{ value: 6, label: "Diego Fernández" },
|
|
603
|
+
{ value: 7, label: "Elena Morales" },
|
|
604
|
+
{ value: 8, label: "Fabián Núñez" },
|
|
605
|
+
];
|
|
606
|
+
|
|
607
|
+
const fakeFetchUsers = (query: string): Promise<SelectOption[]> =>
|
|
608
|
+
new Promise((resolve) => {
|
|
609
|
+
setTimeout(() => {
|
|
610
|
+
if (!query) return resolve([]);
|
|
611
|
+
const q = query.toLowerCase();
|
|
612
|
+
resolve(mockUsersDb.filter((u) => u.label.toLowerCase().includes(q)));
|
|
613
|
+
}, 400);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// Debounce casero (para no agregar lodash como dep de la story).
|
|
617
|
+
function debounce<T extends (...args: any[]) => void>(fn: T, ms: number) {
|
|
618
|
+
let t: ReturnType<typeof setTimeout>;
|
|
619
|
+
return (...args: Parameters<T>) => {
|
|
620
|
+
clearTimeout(t);
|
|
621
|
+
t = setTimeout(() => fn(...args), ms);
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export const AsyncService: Story = {
|
|
626
|
+
render: () => {
|
|
627
|
+
const [value, setValue] = useState<number | null>(null);
|
|
628
|
+
const [input, setInput] = useState("");
|
|
629
|
+
const [options, setOptions] = useState<SelectOption[]>([]);
|
|
630
|
+
const [loading, setLoading] = useState(false);
|
|
631
|
+
|
|
632
|
+
const search = useMemo(
|
|
633
|
+
() =>
|
|
634
|
+
debounce(async (q: string) => {
|
|
635
|
+
if (!q) {
|
|
636
|
+
setOptions([]);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
setLoading(true);
|
|
640
|
+
try {
|
|
641
|
+
const res = await fakeFetchUsers(q);
|
|
642
|
+
setOptions(res);
|
|
643
|
+
} finally {
|
|
644
|
+
setLoading(false);
|
|
645
|
+
}
|
|
646
|
+
}, 300),
|
|
647
|
+
[],
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
useEffect(() => {
|
|
651
|
+
search(input);
|
|
652
|
+
}, [input, search]);
|
|
653
|
+
|
|
654
|
+
return (
|
|
655
|
+
<Box sx={{ width: 320 }}>
|
|
656
|
+
<Autocomplete<number>
|
|
657
|
+
label="Usuario"
|
|
658
|
+
placeholder="Buscar usuario..."
|
|
659
|
+
options={options}
|
|
660
|
+
value={value}
|
|
661
|
+
onChange={(v) => setValue(v as number | null)}
|
|
662
|
+
inputValue={input}
|
|
663
|
+
onInputChange={(_, v) => setInput(v)}
|
|
664
|
+
loading={loading}
|
|
665
|
+
// Desactiva filtro cliente: confiamos en el servicio.
|
|
666
|
+
filterOptions={(x) => x}
|
|
667
|
+
/>
|
|
668
|
+
<Typography sx={{ mt: 2 }} variant="caption" color="text.secondary">
|
|
669
|
+
{"Escribe para buscar. El servicio se consulta con debounce (300ms). filterOptions={(x) => x} desactiva el filtro cliente."}
|
|
670
|
+
</Typography>
|
|
671
|
+
</Box>
|
|
672
|
+
);
|
|
673
|
+
},
|
|
674
|
+
parameters: {
|
|
675
|
+
docs: {
|
|
676
|
+
description: {
|
|
677
|
+
story:
|
|
678
|
+
"Búsqueda asíncrona contra un servicio. El consumer maneja el estado de `options`/`loading` y reacciona a `onInputChange` para llamar al servicio. Usar `filterOptions={(x) => x}` para desactivar el filtro cliente.",
|
|
679
|
+
},
|
|
680
|
+
source: { code: AsyncServiceAutocompleteDefinition.trim() },
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
export const AsyncServiceMultiple: Story = {
|
|
686
|
+
render: () => {
|
|
687
|
+
const [value, setValue] = useState<number[]>([]);
|
|
688
|
+
const [input, setInput] = useState("");
|
|
689
|
+
const [options, setOptions] = useState<SelectOption[]>([]);
|
|
690
|
+
const [loading, setLoading] = useState(false);
|
|
691
|
+
|
|
692
|
+
const search = useMemo(
|
|
693
|
+
() =>
|
|
694
|
+
debounce(async (q: string) => {
|
|
695
|
+
if (!q) {
|
|
696
|
+
setOptions([]);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
setLoading(true);
|
|
700
|
+
try {
|
|
701
|
+
const res = await fakeFetchUsers(q);
|
|
702
|
+
setOptions(res);
|
|
703
|
+
} finally {
|
|
704
|
+
setLoading(false);
|
|
705
|
+
}
|
|
706
|
+
}, 300),
|
|
707
|
+
[],
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
useEffect(() => {
|
|
711
|
+
search(input);
|
|
712
|
+
}, [input, search]);
|
|
713
|
+
|
|
714
|
+
return (
|
|
715
|
+
<Box sx={{ width: 420 }}>
|
|
716
|
+
<Autocomplete<number>
|
|
717
|
+
multiple
|
|
718
|
+
maxChipsToShow={4}
|
|
719
|
+
label="Usuarios"
|
|
720
|
+
placeholder="Buscar y seleccionar varios..."
|
|
721
|
+
options={options}
|
|
722
|
+
value={value}
|
|
723
|
+
onChange={(v) => setValue(v as number[])}
|
|
724
|
+
inputValue={input}
|
|
725
|
+
onInputChange={(_, v) => setInput(v)}
|
|
726
|
+
loading={loading}
|
|
727
|
+
filterOptions={(x) => x}
|
|
728
|
+
/>
|
|
729
|
+
<Typography sx={{ mt: 2 }} variant="caption" color="text.secondary">
|
|
730
|
+
Selecciona varios usuarios buscando con diferentes queries. Los chips
|
|
731
|
+
persisten aunque el search cambie las `options` (cache interno).
|
|
732
|
+
</Typography>
|
|
733
|
+
<Typography sx={{ mt: 1 }} variant="caption" display="block">
|
|
734
|
+
Seleccionados: {JSON.stringify(value)}
|
|
735
|
+
</Typography>
|
|
736
|
+
</Box>
|
|
737
|
+
);
|
|
738
|
+
},
|
|
739
|
+
parameters: {
|
|
740
|
+
docs: {
|
|
741
|
+
description: {
|
|
742
|
+
story:
|
|
743
|
+
"Búsqueda asíncrona **con selección múltiple**. El componente mantiene un cache interno de opciones ya vistas, así los chips de los items seleccionados NO desaparecen cuando el usuario busca otros términos. Flujo: buscar 'an' → seleccionar Andrea → buscar 'be' → seleccionar Beatriz → los dos chips siguen visibles.",
|
|
744
|
+
},
|
|
745
|
+
source: { code: AsyncServiceAutocompleteDefinition.trim() },
|
|
746
|
+
},
|
|
747
|
+
},
|
|
748
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
import { buildFormFieldSx } from '../_shared/formField.sx';
|
|
4
|
+
import type { LabelPosition } from './Autocomplete';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builder del sx del Autocomplete. Reutiliza `buildFormFieldSx` y añade
|
|
8
|
+
* los paddings propios del Autocomplete (chips + input interno).
|
|
9
|
+
*/
|
|
10
|
+
export const buildAutocompleteSx = (
|
|
11
|
+
borderRadius: number | string,
|
|
12
|
+
labelPosition: LabelPosition,
|
|
13
|
+
): SxProps<Theme> =>
|
|
14
|
+
buildFormFieldSx({
|
|
15
|
+
borderRadius,
|
|
16
|
+
labelPosition,
|
|
17
|
+
extraOutsideSx: {
|
|
18
|
+
// El input interno del Autocomplete.
|
|
19
|
+
'& .MuiAutocomplete-input': {
|
|
20
|
+
paddingTop: '4px',
|
|
21
|
+
paddingBottom: '4px',
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Contenedor del input (donde viven los chips en modo multiple).
|
|
25
|
+
'& .MuiAutocomplete-inputRoot': {
|
|
26
|
+
paddingTop: '4px',
|
|
27
|
+
paddingBottom: '4px',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|