@m4l/components 9.3.27 → 9.3.28

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.
@@ -1,8 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEnvironment, useModuleSkeleton, useModuleDictionary } from "@m4l/core";
3
3
  import { useTheme } from "@mui/material";
4
+ import { useForkRef } from "@mui/material/utils";
4
5
  import clsx from "clsx";
5
- import { forwardRef, useMemo, useState, useCallback } from "react";
6
+ import { forwardRef, useMemo, useState, useRef, useCallback, useEffect } from "react";
6
7
  import { A as ArrowDownStyled, a as AdormentStyled, S as SelectRootStyled, b as SkeletonSelectStyled, M as MenuItemListStyled, c as MenuItemNoOptionStyled, L as LabelPlaceholerStyled, R as RenderValueContainerStyled, d as RenderValueTypography } from "./slots/SelectSlots.js";
7
8
  import { I as ICON_ARROW_DOWN, S as SELECT_CLASSES, a as SELECT_KEY_COMPONENT } from "./constants.js";
8
9
  import { S as SELECT_DICTIONARY } from "./dictionary.js";
@@ -23,6 +24,7 @@ const Select = forwardRef(
23
24
  placeholder,
24
25
  className,
25
26
  dataTestId,
27
+ readOnly = false,
26
28
  ...otherProps
27
29
  }, ref) {
28
30
  const { currentSize } = useComponentSize(size);
@@ -33,22 +35,27 @@ const Select = forwardRef(
33
35
  const arrowDropDownIcon = `${host_static_assets}/${environment_assets}/${ICON_ARROW_DOWN}`;
34
36
  const selectedValue = useMemo(() => value, [value]);
35
37
  const [openLocal, setOpenLocal] = useState(false);
38
+ const selectRef = useRef(null);
36
39
  const theme = useTheme();
37
40
  const ownerState = useMemo(() => ({
38
41
  size: adjustedSize,
39
42
  disabled,
40
43
  error,
41
44
  variant,
42
- multiple
43
- }), [adjustedSize, disabled, error, variant, multiple]);
45
+ multiple,
46
+ readOnly
47
+ }), [adjustedSize, disabled, error, variant, multiple, readOnly]);
44
48
  const handleLocalChange = useCallback((event) => {
49
+ if (readOnly || disabled) {
50
+ return;
51
+ }
45
52
  const newValue = event.target.value;
46
53
  if (!onChange) {
47
54
  return;
48
55
  }
49
56
  if (multiple) {
50
- const selectedOptions = options.filter((option) => newValue.includes(option.id));
51
- if (selectedOptions.length > 0) {
57
+ if (Array.isArray(newValue)) {
58
+ const selectedOptions = options.filter((option) => newValue.includes(option.id));
52
59
  onChange(selectedOptions);
53
60
  }
54
61
  } else {
@@ -57,28 +64,41 @@ const Select = forwardRef(
57
64
  onChange(selectedOption);
58
65
  }
59
66
  }
60
- }, [multiple, onChange, options]);
67
+ }, [multiple, onChange, options, readOnly, disabled]);
61
68
  const handleDelete = useCallback((optionToDelete) => {
69
+ if (readOnly || disabled) {
70
+ return;
71
+ }
62
72
  if (multiple && Array.isArray(selectedValue)) {
63
73
  const newSelectedOptions = options.filter((option) => option.id !== optionToDelete.id && selectedValue.includes(option.id));
64
74
  onChange?.(newSelectedOptions);
65
75
  }
66
- }, [multiple, selectedValue, onChange, options]);
76
+ }, [multiple, selectedValue, onChange, options, readOnly, disabled]);
77
+ const handleToggleClick = useCallback(() => {
78
+ if (disabled || readOnly) {
79
+ return;
80
+ }
81
+ if (multiple) {
82
+ if (!openLocal) {
83
+ setOpenLocal(true);
84
+ }
85
+ return;
86
+ }
87
+ setOpenLocal(!openLocal);
88
+ }, [disabled, readOnly, multiple, openLocal]);
67
89
  const ArrowIcon = useMemo(() => {
68
90
  return () => /* @__PURE__ */ jsx(
69
91
  ArrowDownStyled,
70
92
  {
71
93
  ownerState: { ...ownerState },
72
94
  icon: arrowDropDownIcon,
73
- disabled,
95
+ disabled: disabled || readOnly,
74
96
  size: adjustedSize,
75
- onClick: () => {
76
- !disabled && setOpenLocal(!openLocal);
77
- },
97
+ onClick: handleToggleClick,
78
98
  rotationAngle: openLocal ? 180 : 0
79
99
  }
80
100
  );
81
- }, [ownerState, arrowDropDownIcon, disabled, adjustedSize, openLocal]);
101
+ }, [ownerState, arrowDropDownIcon, disabled, readOnly, adjustedSize, openLocal, handleToggleClick]);
82
102
  const RenderIcon = useCallback((icon) => {
83
103
  if (!icon) {
84
104
  return null;
@@ -104,8 +124,8 @@ const Select = forwardRef(
104
124
  e.stopPropagation();
105
125
  }, []);
106
126
  const renderValue = () => {
107
- if (!selectedValue || multiple && Array.isArray(selectedValue) && selectedValue.length === 0) {
108
- return /* @__PURE__ */ jsx(LabelPlaceholerStyled, { variant: "body", color: "text.disabled", children: placeholder });
127
+ if (selectedValue === null || selectedValue === void 0 || multiple && Array.isArray(selectedValue) && selectedValue.length === 0) {
128
+ return /* @__PURE__ */ jsx(LabelPlaceholerStyled, { ownerState: { disabled, readOnly }, variant: "body", color: "text.disabled", children: placeholder });
109
129
  }
110
130
  if (multiple) {
111
131
  if (!Array.isArray(selectedValue)) {
@@ -124,7 +144,7 @@ const Select = forwardRef(
124
144
  label: option.label,
125
145
  startIcon: option.startAdornment,
126
146
  opacity: true,
127
- onDeleted: () => handleDelete(option),
147
+ onDeleted: readOnly || disabled ? void 0 : () => handleDelete(option),
128
148
  iconButtonProps: { onMouseDown: onMouseDownIconButtonChip, onKeyDown: onKeyDownIconButtonChip }
129
149
  },
130
150
  option.id
@@ -135,17 +155,26 @@ const Select = forwardRef(
135
155
  const selectedOption = options.find((option) => String(option.id) === String(selectedValue));
136
156
  return selectedOption ? /* @__PURE__ */ jsxs(RenderValueContainerStyled, { ownerState: { ...ownerState }, className: SELECT_CLASSES.renderValueContainer, children: [
137
157
  selectedOption.startAdornment && RenderIcon(selectedOption.startAdornment),
138
- /* @__PURE__ */ jsx(RenderValueTypography, { variant: "body", color: "text.primary", size: adjustedSize, className: SELECT_CLASSES.renderValueTypography, children: selectedOption.label })
158
+ /* @__PURE__ */ jsx(RenderValueTypography, { ownerState: { disabled, readOnly }, variant: "body", color: "text.primary", size: adjustedSize, className: SELECT_CLASSES.renderValueTypography, children: selectedOption.label })
139
159
  ] }) : "";
140
160
  }
141
161
  };
142
162
  const onOpen = useCallback(() => {
163
+ if (disabled || readOnly) {
164
+ return;
165
+ }
143
166
  setOpenLocal(true);
144
- }, []);
167
+ }, [disabled, readOnly]);
145
168
  const onClose = useCallback(() => {
146
169
  setOpenLocal(false);
147
170
  }, []);
171
+ useEffect(() => {
172
+ if (readOnly && openLocal) {
173
+ setOpenLocal(false);
174
+ }
175
+ }, [readOnly, openLocal]);
148
176
  const StyledSelect = useMemo(() => SelectRootStyled(), []);
177
+ const handleRef = useForkRef(selectRef, ref);
149
178
  if (isSkeleton) {
150
179
  return /* @__PURE__ */ jsx(SkeletonSelectStyled, { ownerState: { ...ownerState }, className: SELECT_CLASSES.skeletonSelect });
151
180
  }
@@ -154,7 +183,7 @@ const Select = forwardRef(
154
183
  {
155
184
  ...otherProps,
156
185
  ...getPropDataTestId(SELECT_KEY_COMPONENT, SelectSlots.root, dataTestId),
157
- ref,
186
+ ref: handleRef,
158
187
  className: clsx(SELECT_CLASSES.root, SELECT_CLASSES[variant], className),
159
188
  ownerState: { ...ownerState },
160
189
  IconComponent: ArrowIcon,
@@ -165,29 +194,42 @@ const Select = forwardRef(
165
194
  disabled,
166
195
  error,
167
196
  renderValue,
168
- open: openLocal,
197
+ open: readOnly ? false : openLocal,
169
198
  native: false,
170
199
  onOpen,
171
200
  onClose,
201
+ onClick: handleToggleClick,
172
202
  displayEmpty: true,
203
+ inputProps: {
204
+ ...otherProps.inputProps,
205
+ readOnly
206
+ },
173
207
  MenuProps: {
174
208
  disableScrollLock: true,
209
+ anchorOrigin: {
210
+ vertical: "bottom",
211
+ horizontal: "left"
212
+ },
213
+ transformOrigin: {
214
+ vertical: "top",
215
+ horizontal: "left"
216
+ },
175
217
  sx: {
176
218
  "& .MuiPaper-root": {
177
- paddingTop: theme.vars.size.baseSpacings.sp3,
178
- paddingBottom: theme.vars.size.baseSpacings.sp3,
179
- paddingLeft: 0,
180
- paddingRight: 0,
219
+ padding: theme.vars.size.baseSpacings.sp1,
181
220
  maxHeight: "200px",
182
221
  overflow: "auto",
222
+ minWidth: `${(selectRef?.current?.offsetWidth ?? 0) + 1}px!important`,
223
+ // +1 para incluir el border del contenedor root.
183
224
  "& .MuiList-root": {
184
225
  padding: 0,
185
- display: "flex",
186
- flexDirection: "column",
226
+ display: "grid",
227
+ gridTemplateColumns: "auto",
187
228
  gap: theme.vars.size.baseSpacings.sp1,
188
- width: "fit-content",
229
+ width: "fit-content!important",
230
+ minWidth: "100%",
189
231
  "& .MuiMenuItem-root": {
190
- width: "fit-content"
232
+ width: "100%"
191
233
  }
192
234
  }
193
235
  }
@@ -13,6 +13,18 @@ const selectStyles = {
13
13
  border: theme.vars.size.borderStroke.actionInput,
14
14
  borderColor: ownerState?.error ? theme.vars.palette[color].enabled : theme.vars.palette.border.default,
15
15
  padding: 0,
16
+ cursor: ownerState?.readOnly || ownerState?.disabled ? "default" : "pointer",
17
+ ...ownerState?.readOnly && {
18
+ "& .M4LSelect-renderValueTypography": {
19
+ userSelect: "text",
20
+ cursor: "text"
21
+ }
22
+ },
23
+ "& .MuiSelect-nativeInput": {
24
+ inset: 0,
25
+ border: "unset",
26
+ outline: "unset"
27
+ },
16
28
  "& .MuiOutlinedInput-notchedOutline": {
17
29
  all: "unset",
18
30
  display: "none"
@@ -41,7 +53,10 @@ const selectStyles = {
41
53
  boxSizing: "border-box",
42
54
  display: "flex",
43
55
  flexDirection: "column",
44
- flexWrap: "unset"
56
+ flexWrap: "unset",
57
+ ...ownerState?.readOnly && {
58
+ cursor: "default"
59
+ }
45
60
  },
46
61
  // Estilos para la variante text
47
62
  [`&&&.${SELECT_CLASSES.text}`]: {
@@ -106,8 +121,12 @@ const selectStyles = {
106
121
  }
107
122
  }),
108
123
  arrowDown: {},
109
- renderValueTypography: () => ({
110
- lineHeight: "inherit!important"
124
+ /**
125
+ * Styles for the render value typography
126
+ */
127
+ renderValueTypography: ({ ownerState }) => ({
128
+ lineHeight: "inherit!important",
129
+ cursor: ownerState?.readOnly || ownerState?.disabled ? "default" : "pointer"
111
130
  }),
112
131
  // chip: () => ({
113
132
  // }),
@@ -122,16 +141,32 @@ const selectStyles = {
122
141
  * Styles for the label placeholder
123
142
  * @returns {object} The styles for the label placeholder
124
143
  */
125
- labelPlaceholer: ({ theme }) => ({
144
+ labelPlaceholer: ({ theme, ownerState }) => ({
126
145
  paddingLeft: theme.vars.size.baseSpacings.sp1,
127
- paddingRight: theme.vars.size.baseSpacings.sp3
146
+ paddingRight: theme.vars.size.baseSpacings.sp3,
147
+ cursor: ownerState?.readOnly || ownerState?.disabled ? "default" : "pointer"
128
148
  }),
129
149
  /**
130
150
  * Styles for the menu item select
131
151
  * @param {object} theme - The theme object
132
152
  * @returns {object} The styles for the menu item select
133
153
  */
134
- menuItem: {},
154
+ menuItem: {
155
+ "& .M4LMenuItem-menuItemContainer": {
156
+ width: "fit-content",
157
+ overflow: "unset",
158
+ "& .M4LImage-root": {
159
+ flexGrow: 1,
160
+ overflow: "auto",
161
+ flexShrink: 0
162
+ },
163
+ "& .M4LTypography-root": {
164
+ textOverflow: "unset",
165
+ overflow: "unset",
166
+ whiteSpace: "unset"
167
+ }
168
+ }
169
+ },
135
170
  /**
136
171
  * Estilos del menuItem que no tiene opciones
137
172
  * @param {object} theme - The theme object
@@ -45,6 +45,10 @@ export interface BaseSelectProps {
45
45
  * Identificador único del componente, para pruebas automatizadas.
46
46
  */
47
47
  dataTestId?: string;
48
+ /**
49
+ * Define si el Select está en modo solo lectura.
50
+ */
51
+ readOnly?: boolean;
48
52
  }
49
53
  export type SelectOptionValue<T extends string | number, Multiple> = Multiple extends true ? Array<SelectOption<T>> : SelectOption<T> | null;
50
54
  /**
@@ -72,8 +76,9 @@ export interface SelectProps<T extends string | number, Multiple extends boolean
72
76
  * Propiedades internas que definen el estado del componente `Select`.
73
77
  * paletteColor - Color de la paleta aplicado al Select.
74
78
  */
75
- export interface SelectOwnerState<T extends string | number, Multiple extends boolean | undefined = false> extends Pick<SelectProps<T, Multiple>, 'classes' | 'size' | 'error' | 'variant' | 'disabled' | 'multiple'> {
79
+ export interface SelectOwnerState<T extends string | number, Multiple extends boolean | undefined = false> extends Pick<SelectProps<T, Multiple>, 'classes' | 'size' | 'error' | 'variant' | 'disabled' | 'multiple' | 'readOnly'> {
76
80
  disabled?: boolean;
81
+ readOnly?: boolean;
77
82
  }
78
83
  /**
79
84
  * Define los tipos de slots disponibles en el Select.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m4l/components",
3
- "version": "9.3.27",
3
+ "version": "9.3.28",
4
4
  "license": "UNLICENSED",
5
5
  "description": "M4L Components",
6
6
  "lint-staged": {
@@ -64,7 +64,8 @@
64
64
  "attr-accept": "2.2.2",
65
65
  "@vitejs/plugin-react": "4.3.4",
66
66
  "storybook": "8.3.4",
67
- "@storybook/react": "8.3.4"
67
+ "@storybook/react": "8.3.4",
68
+ "rollup": "4.52.5"
68
69
  },
69
70
  "overrides": {
70
71
  "glob": "^10.4.5",
@@ -73,7 +74,8 @@
73
74
  "@types/react": "18.3.23",
74
75
  "@types/react-dom": "18.3.7",
75
76
  "react-dom": "18.3.1",
76
- "attr-accept": "2.2.2"
77
+ "attr-accept": "2.2.2",
78
+ "rollup": "4.52.5"
77
79
  },
78
80
  "files": [
79
81
  "*"