@soyfri/shared-library 2.0.0-beta.1 → 2.0.0-beta.10
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 +38 -75
- package/dist/README.md +243 -0
- package/dist/components/Drawer/Drawer.cjs +14 -17
- package/dist/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts +8 -1
- package/dist/components/Drawer/Drawer.js +14 -17
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/Input/Input.definitions.d.ts +1 -0
- package/dist/components/RadioGroup/RadioGroup.cjs +202 -0
- package/dist/components/RadioGroup/RadioGroup.cjs.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts +53 -0
- package/dist/components/RadioGroup/RadioGroup.definitions.d.ts +6 -0
- package/dist/components/RadioGroup/RadioGroup.js +202 -0
- package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.sx.d.ts +20 -0
- package/dist/components/RadioGroup/RadioGroup.types.d.ts +1 -0
- package/dist/components/RadioGroup/index.d.ts +2 -0
- package/dist/components/RadioGroup.d.ts +6 -0
- package/dist/components/Stepper/Stepper.cjs +136 -23
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js +137 -24
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/Switch/Switch.cjs +181 -0
- package/dist/components/Switch/Switch.cjs.map +1 -0
- package/dist/components/Switch/Switch.d.ts +43 -0
- package/dist/components/Switch/Switch.definitions.d.ts +7 -0
- package/dist/components/Switch/Switch.js +181 -0
- package/dist/components/Switch/Switch.js.map +1 -0
- package/dist/components/Switch/Switch.sx.d.ts +22 -0
- package/dist/components/Switch/Switch.types.d.ts +1 -0
- package/dist/components/Switch/index.d.ts +2 -0
- package/dist/components/Switch.d.ts +6 -0
- package/dist/index.cjs +24 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -1
- package/dist/mui.d.ts +1 -0
- package/dist/package.json +207 -0
- package/dist/theme/componentStyles.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Drawer/Drawer.stories.tsx +168 -0
- package/src/components/Drawer/Drawer.tsx +26 -18
- package/src/components/Input/Input.definitions.ts +24 -0
- package/src/components/Input/Input.stories.tsx +29 -0
- package/src/components/RadioGroup/RadioGroup.definitions.ts +177 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +231 -0
- package/src/components/RadioGroup/RadioGroup.sx.ts +75 -0
- package/src/components/RadioGroup/RadioGroup.tsx +196 -0
- package/src/components/RadioGroup/RadioGroup.types.ts +10 -0
- package/src/components/RadioGroup/index.ts +9 -0
- package/src/components/Stepper/Stepper.stories.tsx +72 -0
- package/src/components/Stepper/Stepper.tsx +139 -4
- package/src/components/Switch/Switch.definitions.ts +134 -0
- package/src/components/Switch/Switch.stories.tsx +213 -0
- package/src/components/Switch/Switch.sx.ts +81 -0
- package/src/components/Switch/Switch.tsx +172 -0
- package/src/components/Switch/Switch.types.ts +10 -0
- package/src/components/Switch/index.ts +9 -0
- package/src/mui.ts +10 -0
- package/src/theme/componentStyles.ts +3 -1
- package/storybook-static/addon-visual-tests-assets/visual-test-illustration.mp4 +0 -0
- package/storybook-static/assets/AccountCircle-BDZFsbTw.js +1 -0
- package/storybook-static/assets/ActionMenu-EynP8yU1.js +19 -0
- package/storybook-static/assets/ActionMenu.stories-DqSqRGix.js +185 -0
- package/storybook-static/assets/Alert-3zvTPc0p.js +1 -0
- package/storybook-static/assets/AppBar.stories-DcX3M5th.js +172 -0
- package/storybook-static/assets/Autocomplete.stories-CXJm8FOT.js +788 -0
- package/storybook-static/assets/Avatar-NbFfkZws.js +1 -0
- package/storybook-static/assets/Avatar.stories-CwOYCzqU.js +390 -0
- package/storybook-static/assets/Box-BnhEcfFm.js +1 -0
- package/storybook-static/assets/Button-D9h7OggD.js +1 -0
- package/storybook-static/assets/Button-DBpqmVB_.js +1 -0
- package/storybook-static/assets/Button.stories-F20dmnjq.js +320 -0
- package/storybook-static/assets/ButtonBase-qyaMEhe4.js +74 -0
- package/storybook-static/assets/Card.stories-B3NpAhO0.js +154 -0
- package/storybook-static/assets/CheckCircleOutline-CEj5mDsl.js +1 -0
- package/storybook-static/assets/Chip-C3vKPpzR.js +1 -0
- package/storybook-static/assets/Chip.stories-sxcfHVo9.js +333 -0
- package/storybook-static/assets/CircularProgress-DC7ZNWwl.js +28 -0
- package/storybook-static/assets/Clear-4kYcKvT3.js +1 -0
- package/storybook-static/assets/ClipBoard-DvLBdNHe.js +1 -0
- package/storybook-static/assets/ClipBoard.stories-BGUo47r6.js +108 -0
- package/storybook-static/assets/Close-CgHeRgmh.js +1 -0
- package/storybook-static/assets/Close-Cy8nELYU.js +1 -0
- package/storybook-static/assets/Color-AVL7NMMY-BJKvwERm.js +1 -0
- package/storybook-static/assets/ContentCopy-BfLTDb10.js +1 -0
- package/storybook-static/assets/DatePicker-Clkpr-Ku.js +1 -0
- package/storybook-static/assets/DatePicker.stories-EaUCMkh3.js +444 -0
- package/storybook-static/assets/DateRangePicker.stories-BMlkj-8K.js +390 -0
- package/storybook-static/assets/DateTimePicker.stories-B6gdzKq5.js +555 -0
- package/storybook-static/assets/DefaultPropsProvider-BGoQxtDa.js +16 -0
- package/storybook-static/assets/Delete-D2SMMmIA.js +1 -0
- package/storybook-static/assets/DialogContent-BeCDKgax.js +1 -0
- package/storybook-static/assets/Divider-BbCj9wT4.js +1 -0
- package/storybook-static/assets/DocsRenderer-PQXLIZUC-BebLK5Y_.js +1243 -0
- package/storybook-static/assets/Drawer-DcFwy73r.js +1 -0
- package/storybook-static/assets/Drawer.stories-C5AZkJBk.js +173 -0
- package/storybook-static/assets/EmptyTable-B-RKtgVs.png +0 -0
- package/storybook-static/assets/ErrorOutline-D9gM7ART.js +1 -0
- package/storybook-static/assets/Fade-Ll96CvH8.js +1 -0
- package/storybook-static/assets/Flyout.stories-Cf7z6MNw.js +163 -0
- package/storybook-static/assets/Gallery.stories-DdpWVTF6.js +127 -0
- package/storybook-static/assets/Grow-8y4FglGK.js +1 -0
- package/storybook-static/assets/Home-BRvJEp2L.js +1 -0
- package/storybook-static/assets/Icon.stories-D0mUiW_t.js +78 -0
- package/storybook-static/assets/IconButton-9OYSTH58.js +1 -0
- package/storybook-static/assets/Input-CjX0t4h-.js +1 -0
- package/storybook-static/assets/Input.stories-BRxekliy.js +650 -0
- package/storybook-static/assets/InputGroup.stories-DH6gUfmn.js +306 -0
- package/storybook-static/assets/KeyboardArrowRight-WO_attK2.js +1 -0
- package/storybook-static/assets/KeyboardArrowUp-DsyVef-i.js +1 -0
- package/storybook-static/assets/ListItem-D3O0103N.js +1 -0
- package/storybook-static/assets/ListItemIcon-hca6xN79.js +1 -0
- package/storybook-static/assets/ListItemText-BFLAwLdl.js +1 -0
- package/storybook-static/assets/Logout-gj-P3AfU.js +1 -0
- package/storybook-static/assets/Menu-ClzfjLc3.js +1 -0
- package/storybook-static/assets/MenuButton.stories-B-W_QVDt.js +162 -0
- package/storybook-static/assets/MenuItem-iU6tAqJI.js +1 -0
- package/storybook-static/assets/Modal-3okp9H2i.js +1 -0
- package/storybook-static/assets/Modal.stories-DIWzm4qR.js +468 -0
- package/storybook-static/assets/MoreVert-BoIVG4gh.js +1 -0
- package/storybook-static/assets/Notifications-DY_A-Sho.js +1 -0
- package/storybook-static/assets/PageLoader.stories-DmtO1mlm.js +158 -0
- package/storybook-static/assets/Paper-SwQBhqI7.js +1 -0
- package/storybook-static/assets/Person-CkQl-mpq.js +1 -0
- package/storybook-static/assets/PickersModalDialog-Tjnr_cu5.js +10 -0
- package/storybook-static/assets/PickersToolbarButton-Tt185-si.js +1 -0
- package/storybook-static/assets/Popper-CnCTYXxy.js +1 -0
- package/storybook-static/assets/Portal-Cj8XF9Lf.js +1 -0
- package/storybook-static/assets/ScrollTopButton.stories-BflQCwNP.js +90 -0
- package/storybook-static/assets/Select-CjcuMAY0.js +4 -0
- package/storybook-static/assets/Select-DJh2biEb.js +3 -0
- package/storybook-static/assets/Select.stories-DU1Gb3I2.js +1103 -0
- package/storybook-static/assets/Settings-BLKc1CnO.js +1 -0
- package/storybook-static/assets/Snackbar-BtVeKTw6.js +1 -0
- package/storybook-static/assets/Stack-D01OUIXi.js +1 -0
- package/storybook-static/assets/Stat.stories-Bn9-iuPT.js +60 -0
- package/storybook-static/assets/StatusMessage.stories-hnfX8FeU.js +73 -0
- package/storybook-static/assets/Stepper-BtKB5ykn.js +2 -0
- package/storybook-static/assets/Stepper.stories-CTEZbgPc.js +165 -0
- package/storybook-static/assets/Table.stories-CTn2Ktmn.js +1260 -0
- package/storybook-static/assets/TableContainer-CzLNaEU-.js +1 -0
- package/storybook-static/assets/TableRow-CS88-1HF.js +2 -0
- package/storybook-static/assets/Tabs-DLpDOu_n.js +1 -0
- package/storybook-static/assets/Tabs.stories-BFVuFy_5.js +159 -0
- package/storybook-static/assets/TextField-22T-xHBm.js +1 -0
- package/storybook-static/assets/Timeline.stories-DJU_U2Hv.js +97 -0
- package/storybook-static/assets/Tooltip-DbnHUxNj.js +1 -0
- package/storybook-static/assets/Tooltip.stories-B7tA3dnV.js +66 -0
- package/storybook-static/assets/Typography-BgntX2Ep.js +1 -0
- package/storybook-static/assets/Wizard.stories-CVrJLK_D.js +23 -0
- package/storybook-static/assets/createSimplePaletteValueFilter-bm0fmN_7.js +1 -0
- package/storybook-static/assets/createSvgIcon-D_Gca4vA.js +1 -0
- package/storybook-static/assets/debounce-Be36O1Ab.js +1 -0
- package/storybook-static/assets/emotion-react.browser.esm--g-C9cX9.js +8 -0
- package/storybook-static/assets/extendSxProp-CEpa30hT.js +1 -0
- package/storybook-static/assets/formField.sx-DMCmZIAa.js +1 -0
- package/storybook-static/assets/getReactElementRef-BQ3ANZdy.js +1 -0
- package/storybook-static/assets/iframe-BAJnc_4n.js +1079 -0
- package/storybook-static/assets/index-B1tlhOpe.js +240 -0
- package/storybook-static/assets/index-BF3FAXTk.js +9 -0
- package/storybook-static/assets/index-CIeucmOB.js +2 -0
- package/storybook-static/assets/index-CY7j4a7o.js +1 -0
- package/storybook-static/assets/index-CxkKctw5.js +1 -0
- package/storybook-static/assets/isFocusVisible-B8k4qzLc.js +1 -0
- package/storybook-static/assets/isMuiElement-CTZSFcY5.js +1 -0
- package/storybook-static/assets/jsx-runtime-D_zvdyIk.js +9 -0
- package/storybook-static/assets/listItemTextClasses-CC_rwJam.js +1 -0
- package/storybook-static/assets/mergeSlotProps-B0UBKBMe.js +1 -0
- package/storybook-static/assets/ownerDocument-DW-IO8s5.js +1 -0
- package/storybook-static/assets/ownerWindow-HkKU3E4x.js +1 -0
- package/storybook-static/assets/preload-helper-PPVm8Dsz.js +1 -0
- package/storybook-static/assets/react-18-BUJ64QCV.js +25 -0
- package/storybook-static/assets/resolvePreset-CN2aOJJr.js +1 -0
- package/storybook-static/assets/useControlled-DsVh1a5j.js +1 -0
- package/storybook-static/assets/useForkRef-0ANIrxcF.js +1 -0
- package/storybook-static/assets/useId-b4fZxjOL.js +1 -0
- package/storybook-static/assets/useMobilePicker-DK-c8xbD.js +1 -0
- package/storybook-static/assets/usePreviousProps-WR0rG4aR.js +1 -0
- package/storybook-static/assets/useSlot-b6pXgp5_.js +1 -0
- package/storybook-static/assets/useSlotProps-C0uMfuBt.js +1 -0
- package/storybook-static/assets/useTheme-BmOJK7ra.js +1 -0
- package/storybook-static/assets/useThemeProps-DYtxXiUU.js +1 -0
- package/storybook-static/assets/useThemeProps-U4yXiZ_5.js +1 -0
- package/storybook-static/assets/useTimeout-DNjRaOWc.js +1 -0
- package/storybook-static/assets/visuallyHidden-Dan1xhjv.js +1 -0
- package/storybook-static/favicon-wrapper.svg +46 -0
- package/storybook-static/favicon.svg +1 -0
- package/storybook-static/iframe.html +686 -0
- package/storybook-static/index.html +160 -0
- package/storybook-static/index.json +1 -0
- package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/project.json +1 -0
- package/storybook-static/sb-addons/chromatic-com-storybook-2/manager-bundle.js +356 -0
- package/storybook-static/sb-addons/chromatic-com-storybook-2/manager-bundle.js.LEGAL.txt +40 -0
- package/storybook-static/sb-addons/docs-4/manager-bundle.js +151 -0
- package/storybook-static/sb-addons/onboarding-1/manager-bundle.js +127 -0
- package/storybook-static/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js +971 -0
- package/storybook-static/sb-addons/vitest-3/manager-bundle.js +3 -0
- package/storybook-static/sb-common-assets/favicon-wrapper.svg +46 -0
- package/storybook-static/sb-common-assets/favicon.svg +1 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/sb-manager/globals-module-info.js +797 -0
- package/storybook-static/sb-manager/globals-runtime.js +69679 -0
- package/storybook-static/sb-manager/globals.js +34 -0
- package/storybook-static/sb-manager/runtime.js +13195 -0
- package/storybook-static/vite-inject-mocker-entry.js +18 -0
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import React, { ReactElement } from "react";
|
|
2
|
-
import {
|
|
1
|
+
import React, { ReactElement, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Stepper as MuiStepper,
|
|
4
|
+
Step as MuiStep,
|
|
5
|
+
StepLabel as MuiStepLabel,
|
|
6
|
+
Box,
|
|
7
|
+
IconButton,
|
|
8
|
+
} from "@mui/material";
|
|
9
|
+
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
|
|
10
|
+
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
|
3
11
|
import { StepProps } from "./Step";
|
|
4
12
|
import { useWizard } from "../../hooks/Wizard";
|
|
5
13
|
|
|
@@ -35,9 +43,120 @@ export const Stepper: React.FC<MyStepperProps> = ({
|
|
|
35
43
|
? wizard.currentStep
|
|
36
44
|
: 0;
|
|
37
45
|
|
|
46
|
+
const isHorizontal = orientation === "horizontal";
|
|
47
|
+
|
|
48
|
+
// Scroll horizontal con flechitas cuando los steps no caben.
|
|
49
|
+
const scrollRef = useRef<HTMLDivElement | null>(null);
|
|
50
|
+
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
|
51
|
+
const [canScrollRight, setCanScrollRight] = useState(false);
|
|
52
|
+
|
|
53
|
+
const updateScrollState = () => {
|
|
54
|
+
const el = scrollRef.current;
|
|
55
|
+
if (!el) return;
|
|
56
|
+
const { scrollLeft, scrollWidth, clientWidth } = el;
|
|
57
|
+
// Tolerancia de 1px por redondeos de subpixel.
|
|
58
|
+
setCanScrollLeft(scrollLeft > 1);
|
|
59
|
+
setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
useLayoutEffect(() => {
|
|
63
|
+
if (!isHorizontal) return;
|
|
64
|
+
updateScrollState();
|
|
65
|
+
}, [isHorizontal, children.length, currentStep]);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!isHorizontal) return;
|
|
69
|
+
const el = scrollRef.current;
|
|
70
|
+
if (!el) return;
|
|
71
|
+
const onScroll = () => updateScrollState();
|
|
72
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
73
|
+
const ro = new ResizeObserver(() => updateScrollState());
|
|
74
|
+
ro.observe(el);
|
|
75
|
+
return () => {
|
|
76
|
+
el.removeEventListener("scroll", onScroll);
|
|
77
|
+
ro.disconnect();
|
|
78
|
+
};
|
|
79
|
+
}, [isHorizontal]);
|
|
80
|
+
|
|
81
|
+
const scrollBy = (delta: number) => {
|
|
82
|
+
const el = scrollRef.current;
|
|
83
|
+
if (!el) return;
|
|
84
|
+
el.scrollBy({ left: delta, behavior: "smooth" });
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const showArrows = isHorizontal && (canScrollLeft || canScrollRight);
|
|
88
|
+
|
|
38
89
|
return (
|
|
39
90
|
<Box sx={sx}>
|
|
40
|
-
|
|
91
|
+
{/*
|
|
92
|
+
En horizontal envolvemos el MuiStepper en un Box con `overflowX: auto`
|
|
93
|
+
y flechitas `<` `>` a los costados para navegar cuando los steps no
|
|
94
|
+
caben en el contenedor. Mantenemos el scroll nativo (rueda / swipe)
|
|
95
|
+
y ocultamos la scrollbar visual.
|
|
96
|
+
*/}
|
|
97
|
+
<Box
|
|
98
|
+
sx={
|
|
99
|
+
isHorizontal
|
|
100
|
+
? {
|
|
101
|
+
display: "flex",
|
|
102
|
+
alignItems: "center",
|
|
103
|
+
width: "100%",
|
|
104
|
+
gap: 0.5,
|
|
105
|
+
}
|
|
106
|
+
: undefined
|
|
107
|
+
}
|
|
108
|
+
>
|
|
109
|
+
{isHorizontal && (
|
|
110
|
+
<IconButton
|
|
111
|
+
aria-label="Anterior"
|
|
112
|
+
size="small"
|
|
113
|
+
onClick={() => scrollBy(-160)}
|
|
114
|
+
disabled={!canScrollLeft}
|
|
115
|
+
sx={{
|
|
116
|
+
flex: "0 0 auto",
|
|
117
|
+
// Reservamos el slot siempre que haya overflow en algún lado,
|
|
118
|
+
// así el área scroll tiene un ancho estable (no saltan los
|
|
119
|
+
// steps cuando aparece/desaparece la flecha).
|
|
120
|
+
visibility: showArrows ? "visible" : "hidden",
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
<ChevronLeftIcon fontSize="small" />
|
|
124
|
+
</IconButton>
|
|
125
|
+
)}
|
|
126
|
+
<Box
|
|
127
|
+
ref={scrollRef}
|
|
128
|
+
sx={
|
|
129
|
+
isHorizontal
|
|
130
|
+
? {
|
|
131
|
+
flex: "1 1 auto",
|
|
132
|
+
minWidth: 0,
|
|
133
|
+
overflowX: "auto",
|
|
134
|
+
scrollBehavior: "smooth",
|
|
135
|
+
WebkitOverflowScrolling: "touch",
|
|
136
|
+
// Ocultamos la scrollbar (la navegación es con flechas)
|
|
137
|
+
scrollbarWidth: "none",
|
|
138
|
+
"&::-webkit-scrollbar": { display: "none" },
|
|
139
|
+
}
|
|
140
|
+
: undefined
|
|
141
|
+
}
|
|
142
|
+
>
|
|
143
|
+
<MuiStepper
|
|
144
|
+
activeStep={currentStep}
|
|
145
|
+
orientation={orientation}
|
|
146
|
+
alternativeLabel={alternateLabel}
|
|
147
|
+
sx={
|
|
148
|
+
isHorizontal
|
|
149
|
+
? {
|
|
150
|
+
display: "inline-flex",
|
|
151
|
+
flexWrap: "nowrap",
|
|
152
|
+
minWidth: "100%",
|
|
153
|
+
width: "max-content",
|
|
154
|
+
// Evita que los steps se compriman al punto de solaparse.
|
|
155
|
+
"& .MuiStep-root": { flex: "0 0 auto", minWidth: 120 },
|
|
156
|
+
}
|
|
157
|
+
: undefined
|
|
158
|
+
}
|
|
159
|
+
>
|
|
41
160
|
{children.map((child, idx) => {
|
|
42
161
|
const { label, completed, disabled, className, sx: stepSx, dotColor, activeDotColor, completedDotColor, iconTextColor } = child.props;
|
|
43
162
|
return (
|
|
@@ -62,7 +181,23 @@ export const Stepper: React.FC<MyStepperProps> = ({
|
|
|
62
181
|
</MuiStep>
|
|
63
182
|
);
|
|
64
183
|
})}
|
|
65
|
-
|
|
184
|
+
</MuiStepper>
|
|
185
|
+
</Box>
|
|
186
|
+
{isHorizontal && (
|
|
187
|
+
<IconButton
|
|
188
|
+
aria-label="Siguiente"
|
|
189
|
+
size="small"
|
|
190
|
+
onClick={() => scrollBy(160)}
|
|
191
|
+
disabled={!canScrollRight}
|
|
192
|
+
sx={{
|
|
193
|
+
flex: "0 0 auto",
|
|
194
|
+
visibility: showArrows ? "visible" : "hidden",
|
|
195
|
+
}}
|
|
196
|
+
>
|
|
197
|
+
<ChevronRightIcon fontSize="small" />
|
|
198
|
+
</IconButton>
|
|
199
|
+
)}
|
|
200
|
+
</Box>
|
|
66
201
|
<Box mt={2}>
|
|
67
202
|
{children.map((child, idx) =>
|
|
68
203
|
idx === currentStep ? <Box key={idx}>{child.props.children}</Box> : null
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Switch.definitions.ts — código fuente para la pestaña Docs de Storybook.
|
|
2
|
+
|
|
3
|
+
export const BasicSwitchDefinition = `
|
|
4
|
+
import React, { useState } from 'react';
|
|
5
|
+
import { Switch } from './Switch';
|
|
6
|
+
import { Box, Typography } from '@mui/material';
|
|
7
|
+
|
|
8
|
+
export const BasicSwitchExample = () => {
|
|
9
|
+
const [checked, setChecked] = useState(false);
|
|
10
|
+
return (
|
|
11
|
+
<Box sx={{ width: 280 }}>
|
|
12
|
+
<Switch checked={checked} onChange={setChecked} />
|
|
13
|
+
<Typography sx={{ mt: 2 }}>Estado: {checked ? 'ON' : 'OFF'}</Typography>
|
|
14
|
+
</Box>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
export const SwitchWithLabelDefinition = `
|
|
20
|
+
import React, { useState } from 'react';
|
|
21
|
+
import { Switch } from './Switch';
|
|
22
|
+
import { Box } from '@mui/material';
|
|
23
|
+
|
|
24
|
+
export const SwitchWithLabelExample = () => {
|
|
25
|
+
const [checked, setChecked] = useState(true);
|
|
26
|
+
return (
|
|
27
|
+
<Box sx={{ width: 280 }}>
|
|
28
|
+
<Switch
|
|
29
|
+
label="Notificaciones por email"
|
|
30
|
+
checked={checked}
|
|
31
|
+
onChange={setChecked}
|
|
32
|
+
/>
|
|
33
|
+
</Box>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
export const BorderedSwitchDefinition = `
|
|
39
|
+
import React, { useState } from 'react';
|
|
40
|
+
import { Switch } from './Switch';
|
|
41
|
+
import { Box } from '@mui/material';
|
|
42
|
+
|
|
43
|
+
export const BorderedSwitchExample = () => {
|
|
44
|
+
const [checked, setChecked] = useState(true);
|
|
45
|
+
return (
|
|
46
|
+
<Box sx={{ width: 360 }}>
|
|
47
|
+
<Switch
|
|
48
|
+
label="Recibir reportes diarios"
|
|
49
|
+
checked={checked}
|
|
50
|
+
onChange={setChecked}
|
|
51
|
+
bordered
|
|
52
|
+
/>
|
|
53
|
+
</Box>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
export const SwitchSizesDefinition = `
|
|
59
|
+
import React, { useState } from 'react';
|
|
60
|
+
import { Switch } from './Switch';
|
|
61
|
+
import { Box } from '@mui/material';
|
|
62
|
+
|
|
63
|
+
export const SwitchSizesExample = () => {
|
|
64
|
+
const [a, setA] = useState(false);
|
|
65
|
+
const [b, setB] = useState(true);
|
|
66
|
+
return (
|
|
67
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: 280 }}>
|
|
68
|
+
<Switch label="Pequeño" size="small" checked={a} onChange={setA} />
|
|
69
|
+
<Switch label="Mediano (default)" size="medium" checked={b} onChange={setB} />
|
|
70
|
+
</Box>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
export const SwitchWithErrorDefinition = `
|
|
76
|
+
import React, { useState } from 'react';
|
|
77
|
+
import { Switch } from './Switch';
|
|
78
|
+
import { Box } from '@mui/material';
|
|
79
|
+
|
|
80
|
+
export const SwitchWithErrorExample = () => {
|
|
81
|
+
const [checked, setChecked] = useState(false);
|
|
82
|
+
const hasError = !checked;
|
|
83
|
+
return (
|
|
84
|
+
<Box sx={{ width: 320 }}>
|
|
85
|
+
<Switch
|
|
86
|
+
label="Aceptar términos y condiciones"
|
|
87
|
+
checked={checked}
|
|
88
|
+
onChange={setChecked}
|
|
89
|
+
error={hasError}
|
|
90
|
+
helperText={hasError ? 'Debes aceptar los términos para continuar' : ''}
|
|
91
|
+
/>
|
|
92
|
+
</Box>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
export const DisabledSwitchDefinition = `
|
|
98
|
+
import React from 'react';
|
|
99
|
+
import { Switch } from './Switch';
|
|
100
|
+
import { Box } from '@mui/material';
|
|
101
|
+
|
|
102
|
+
export const DisabledSwitchExample = () => (
|
|
103
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: 280 }}>
|
|
104
|
+
<Switch label="Off bloqueado" checked={false} onChange={() => {}} disabled />
|
|
105
|
+
<Switch label="On bloqueado" checked={true} onChange={() => {}} disabled />
|
|
106
|
+
</Box>
|
|
107
|
+
);
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
export const RHFSwitchDefinition = `
|
|
111
|
+
import React from 'react';
|
|
112
|
+
import { useForm } from 'react-hook-form';
|
|
113
|
+
import { Switch } from './Switch';
|
|
114
|
+
import { Box, Button, Typography } from '@mui/material';
|
|
115
|
+
|
|
116
|
+
export const RHFSwitchExample = () => {
|
|
117
|
+
const { control, handleSubmit, watch } = useForm({
|
|
118
|
+
defaultValues: { darkMode: false },
|
|
119
|
+
});
|
|
120
|
+
const value = watch('darkMode');
|
|
121
|
+
return (
|
|
122
|
+
<Box sx={{ width: 320 }} component="form" onSubmit={handleSubmit(console.log)}>
|
|
123
|
+
<Switch
|
|
124
|
+
label="Modo oscuro"
|
|
125
|
+
name="darkMode"
|
|
126
|
+
control={control}
|
|
127
|
+
bordered
|
|
128
|
+
/>
|
|
129
|
+
<Typography sx={{ mt: 2 }}>Valor en el form: {String(value)}</Typography>
|
|
130
|
+
<Button type="submit" sx={{ mt: 1 }}>Enviar</Button>
|
|
131
|
+
</Box>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
`;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Box, Button, Typography } from '@mui/material';
|
|
4
|
+
import { useForm } from 'react-hook-form';
|
|
5
|
+
|
|
6
|
+
import { Switch } from './Switch';
|
|
7
|
+
import {
|
|
8
|
+
BasicSwitchDefinition,
|
|
9
|
+
SwitchWithLabelDefinition,
|
|
10
|
+
BorderedSwitchDefinition,
|
|
11
|
+
SwitchSizesDefinition,
|
|
12
|
+
SwitchWithErrorDefinition,
|
|
13
|
+
DisabledSwitchDefinition,
|
|
14
|
+
RHFSwitchDefinition,
|
|
15
|
+
} from './Switch.definitions';
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Meta
|
|
19
|
+
// =============================================================================
|
|
20
|
+
const meta: Meta<typeof Switch> = {
|
|
21
|
+
title: 'Components/Switch',
|
|
22
|
+
component: Switch,
|
|
23
|
+
tags: ['autodocs'],
|
|
24
|
+
parameters: {
|
|
25
|
+
layout: 'centered',
|
|
26
|
+
docs: {
|
|
27
|
+
description: {
|
|
28
|
+
component:
|
|
29
|
+
'Un toggle/switch basado en MUI `Switch`. Soporta modo controlado (`checked` + `onChange`) ' +
|
|
30
|
+
'y modo react-hook-form (`name` + `control`). Mantiene compatibilidad visual con el legacy ' +
|
|
31
|
+
'`FormToggleInput` mediante la prop `bordered` (label a la izquierda, switch a la derecha).',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
argTypes: {
|
|
36
|
+
label: { control: 'text', description: 'Texto al lado del switch. Si está ausente, se renderiza solo el switch.' },
|
|
37
|
+
labelPlacement: {
|
|
38
|
+
control: 'radio',
|
|
39
|
+
options: ['start', 'end', 'top', 'bottom'],
|
|
40
|
+
description: 'Posición del label respecto al switch. Default: `end`.',
|
|
41
|
+
},
|
|
42
|
+
size: { control: 'radio', options: ['small', 'medium'], description: 'Tamaño del switch. Default: `medium`.' },
|
|
43
|
+
bordered: { control: 'boolean', description: 'Renderizar contenedor con borde estilo "form field". Default: false.' },
|
|
44
|
+
borderRadius: { control: { type: 'number' }, description: 'Border radius del contenedor cuando `bordered`. Default: 10.' },
|
|
45
|
+
disabled: { control: 'boolean', description: 'Deshabilitar el switch.' },
|
|
46
|
+
error: { control: 'boolean', description: 'Estado de error visual.' },
|
|
47
|
+
helperText: { control: 'text', description: 'Texto auxiliar debajo del switch.' },
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default meta;
|
|
52
|
+
type Story = StoryObj<typeof Switch>;
|
|
53
|
+
|
|
54
|
+
// =============================================================================
|
|
55
|
+
// Stories
|
|
56
|
+
// =============================================================================
|
|
57
|
+
|
|
58
|
+
export const BasicSwitch: Story = {
|
|
59
|
+
render: () => {
|
|
60
|
+
const [checked, setChecked] = useState(false);
|
|
61
|
+
return (
|
|
62
|
+
<Box sx={{ width: 280 }}>
|
|
63
|
+
<Switch checked={checked} onChange={setChecked} />
|
|
64
|
+
<Typography sx={{ mt: 2 }}>Estado: {checked ? 'ON' : 'OFF'}</Typography>
|
|
65
|
+
</Box>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
parameters: {
|
|
69
|
+
docs: {
|
|
70
|
+
description: { story: 'Uso mínimo, sin label. Equivalente al legacy `ToggleInputReport`.' },
|
|
71
|
+
source: { code: BasicSwitchDefinition.trim() },
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const SwitchWithLabel: Story = {
|
|
77
|
+
render: () => {
|
|
78
|
+
const [checked, setChecked] = useState(true);
|
|
79
|
+
return (
|
|
80
|
+
<Box sx={{ width: 280 }}>
|
|
81
|
+
<Switch
|
|
82
|
+
label="Notificaciones por email"
|
|
83
|
+
checked={checked}
|
|
84
|
+
onChange={setChecked}
|
|
85
|
+
/>
|
|
86
|
+
</Box>
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
parameters: {
|
|
90
|
+
docs: {
|
|
91
|
+
description: { story: 'Switch con label a la derecha (default `labelPlacement="end"`).' },
|
|
92
|
+
source: { code: SwitchWithLabelDefinition.trim() },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const BorderedSwitch: Story = {
|
|
98
|
+
render: () => {
|
|
99
|
+
const [checked, setChecked] = useState(true);
|
|
100
|
+
return (
|
|
101
|
+
<Box sx={{ width: 360 }}>
|
|
102
|
+
<Switch
|
|
103
|
+
label="Recibir reportes diarios"
|
|
104
|
+
checked={checked}
|
|
105
|
+
onChange={setChecked}
|
|
106
|
+
bordered
|
|
107
|
+
/>
|
|
108
|
+
</Box>
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
parameters: {
|
|
112
|
+
docs: {
|
|
113
|
+
description: {
|
|
114
|
+
story:
|
|
115
|
+
'Variante con `bordered`: contenedor con borde, label a la izquierda y switch al borde opuesto. ' +
|
|
116
|
+
'Replica el estilo del legacy `FormToggleInput`.',
|
|
117
|
+
},
|
|
118
|
+
source: { code: BorderedSwitchDefinition.trim() },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const SwitchSizes: Story = {
|
|
124
|
+
render: () => {
|
|
125
|
+
const [a, setA] = useState(false);
|
|
126
|
+
const [b, setB] = useState(true);
|
|
127
|
+
return (
|
|
128
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: 280 }}>
|
|
129
|
+
<Switch label="Pequeño" size="small" checked={a} onChange={setA} />
|
|
130
|
+
<Switch label="Mediano (default)" size="medium" checked={b} onChange={setB} />
|
|
131
|
+
</Box>
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
parameters: {
|
|
135
|
+
docs: {
|
|
136
|
+
description: { story: 'Tamaños disponibles: `small` y `medium` (default).' },
|
|
137
|
+
source: { code: SwitchSizesDefinition.trim() },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const SwitchWithError: Story = {
|
|
143
|
+
render: () => {
|
|
144
|
+
const [checked, setChecked] = useState(false);
|
|
145
|
+
const hasError = !checked;
|
|
146
|
+
return (
|
|
147
|
+
<Box sx={{ width: 320 }}>
|
|
148
|
+
<Switch
|
|
149
|
+
label="Aceptar términos y condiciones"
|
|
150
|
+
checked={checked}
|
|
151
|
+
onChange={setChecked}
|
|
152
|
+
error={hasError}
|
|
153
|
+
helperText={hasError ? 'Debes aceptar los términos para continuar' : ''}
|
|
154
|
+
/>
|
|
155
|
+
</Box>
|
|
156
|
+
);
|
|
157
|
+
},
|
|
158
|
+
parameters: {
|
|
159
|
+
docs: {
|
|
160
|
+
description: { story: 'Estado de error con `error` + `helperText`.' },
|
|
161
|
+
source: { code: SwitchWithErrorDefinition.trim() },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const DisabledSwitch: Story = {
|
|
167
|
+
render: () => (
|
|
168
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: 280 }}>
|
|
169
|
+
<Switch label="Off bloqueado" checked={false} onChange={() => {}} disabled />
|
|
170
|
+
<Switch label="On bloqueado" checked={true} onChange={() => {}} disabled />
|
|
171
|
+
</Box>
|
|
172
|
+
),
|
|
173
|
+
parameters: {
|
|
174
|
+
docs: {
|
|
175
|
+
description: { story: 'Switch deshabilitado en ambos estados.' },
|
|
176
|
+
source: { code: DisabledSwitchDefinition.trim() },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const RHFSwitch: Story = {
|
|
182
|
+
render: () => {
|
|
183
|
+
const { control, handleSubmit, watch } = useForm({
|
|
184
|
+
defaultValues: { darkMode: false },
|
|
185
|
+
});
|
|
186
|
+
const value = watch('darkMode');
|
|
187
|
+
return (
|
|
188
|
+
<Box
|
|
189
|
+
sx={{ width: 320 }}
|
|
190
|
+
component="form"
|
|
191
|
+
onSubmit={handleSubmit((data) => console.log(data))}
|
|
192
|
+
>
|
|
193
|
+
<Switch
|
|
194
|
+
label="Modo oscuro"
|
|
195
|
+
name="darkMode"
|
|
196
|
+
control={control}
|
|
197
|
+
bordered
|
|
198
|
+
/>
|
|
199
|
+
<Typography sx={{ mt: 2 }}>Valor en el form: {String(value)}</Typography>
|
|
200
|
+
<Button type="submit" sx={{ mt: 1 }}>Enviar</Button>
|
|
201
|
+
</Box>
|
|
202
|
+
);
|
|
203
|
+
},
|
|
204
|
+
parameters: {
|
|
205
|
+
docs: {
|
|
206
|
+
description: {
|
|
207
|
+
story:
|
|
208
|
+
'Modo react-hook-form con `name` + `control`. El valor `true`/`false` viaja directo al form state.',
|
|
209
|
+
},
|
|
210
|
+
source: { code: RHFSwitchDefinition.trim() },
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
export interface BuildSwitchSxOptions {
|
|
4
|
+
bordered: boolean;
|
|
5
|
+
borderRadius: number | string;
|
|
6
|
+
hasLabel: boolean;
|
|
7
|
+
error: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const toRadius = (borderRadius: number | string) =>
|
|
11
|
+
typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Transición compartida (matchea el InputGroup y el RadioGroup).
|
|
15
|
+
*/
|
|
16
|
+
const FOCUS_TRANSITION =
|
|
17
|
+
'border-color 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms, ' +
|
|
18
|
+
'box-shadow 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builder de sx para Switch. Reproduce el estilo del antiguo
|
|
22
|
+
* `FormToggleInput` legacy cuando `bordered=true`: contenedor con borde,
|
|
23
|
+
* label a la izquierda y switch a la derecha (vía justify-content: space-between).
|
|
24
|
+
*
|
|
25
|
+
* Cuando `bordered=true`, el contenedor gana:
|
|
26
|
+
* - `:focus-within` → borde + shadow primario (mismo patrón que `InputGroup`).
|
|
27
|
+
* - `error=true` → borde + shadow de error (palette.error.main).
|
|
28
|
+
*
|
|
29
|
+
* El shadow es de 1px, así que no hay shift de layout — sólo se intensifica
|
|
30
|
+
* visualmente el borde sin empujar nada alrededor.
|
|
31
|
+
*
|
|
32
|
+
* Si `bordered=false`, sólo aplica un reset de margin del helper text.
|
|
33
|
+
*/
|
|
34
|
+
export const buildSwitchSx = ({
|
|
35
|
+
bordered,
|
|
36
|
+
borderRadius,
|
|
37
|
+
hasLabel,
|
|
38
|
+
error,
|
|
39
|
+
}: BuildSwitchSxOptions): SxProps<Theme> => {
|
|
40
|
+
const radius = toRadius(borderRadius);
|
|
41
|
+
|
|
42
|
+
if (!bordered) {
|
|
43
|
+
return {
|
|
44
|
+
'& .MuiFormHelperText-root': { marginLeft: 0 },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (theme) => ({
|
|
49
|
+
'& .MuiFormControlLabel-root': {
|
|
50
|
+
marginLeft: 0,
|
|
51
|
+
marginRight: 0,
|
|
52
|
+
paddingX: 1.5,
|
|
53
|
+
paddingY: 1.25,
|
|
54
|
+
border: `1px solid ${
|
|
55
|
+
error ? theme.palette.error.main : theme.palette.divider
|
|
56
|
+
}`,
|
|
57
|
+
borderRadius: radius,
|
|
58
|
+
transition: FOCUS_TRANSITION,
|
|
59
|
+
...(error && {
|
|
60
|
+
boxShadow: `0 0 0 1px ${theme.palette.error.main}`,
|
|
61
|
+
}),
|
|
62
|
+
// Focus del usuario sobre el switch interno.
|
|
63
|
+
'&:focus-within': {
|
|
64
|
+
borderColor: error
|
|
65
|
+
? theme.palette.error.main
|
|
66
|
+
: theme.palette.primary.main,
|
|
67
|
+
boxShadow: `0 0 0 1px ${
|
|
68
|
+
error ? theme.palette.error.main : theme.palette.primary.main
|
|
69
|
+
}`,
|
|
70
|
+
},
|
|
71
|
+
// Si hay label, separar el switch al borde opuesto.
|
|
72
|
+
...(hasLabel && {
|
|
73
|
+
width: '100%',
|
|
74
|
+
justifyContent: 'space-between',
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
'& .MuiFormHelperText-root': {
|
|
78
|
+
marginLeft: 0,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
};
|