next-recomponents 2.0.27 → 2.0.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.
package/dist/index.d.mts CHANGED
@@ -66,8 +66,11 @@ type FooterAggregation = "sum" | "avg" | "count";
66
66
  interface FooterType {
67
67
  [key: string]: FooterAggregation;
68
68
  }
69
+ type GridDensity = "compact" | "standard" | "comfortable";
69
70
  interface TableProps {
70
71
  data: any;
72
+ /** Muestra un input de búsqueda general que filtra filas por palabras clave */
73
+ searchable?: boolean;
71
74
  flex?: number;
72
75
  editableFields?: string[];
73
76
  onSelect?: (data: GridValidRowModel[]) => void;
@@ -86,6 +89,28 @@ interface TableProps {
86
89
  className?: string;
87
90
  fontSize?: string;
88
91
  colSize?: Record<string, number>;
92
+ /**
93
+ * Alto fijo de fila en px.
94
+ * Si se omite usa el default de MUI según `density`
95
+ * (compact ≈ 36, standard ≈ 52, comfortable ≈ 68).
96
+ * Se ignora automáticamente cuando `wrapText` es true.
97
+ */
98
+ rowHeight?: number;
99
+ /**
100
+ * Cuando es true el texto largo hace wrap en lugar de truncarse,
101
+ * y el alto de cada fila crece para mostrar todo el contenido.
102
+ * Internamente activa `getRowHeight={() => "auto"}` en el DataGrid.
103
+ */
104
+ wrapText?: boolean;
105
+ /**
106
+ * Densidad visual de la tabla.
107
+ * Afecta el padding de celdas y encabezados independientemente
108
+ * del tamaño de fuente.
109
+ * - "compact" → padding mínimo, tabla muy densa
110
+ * - "standard" → (default) comportamiento por defecto de MUI
111
+ * - "comfortable" → padding generoso, fácil de leer
112
+ */
113
+ density?: GridDensity;
89
114
  [key: string]: any;
90
115
  }
91
116
  declare function Table(props: TableProps): react_jsx_runtime.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -66,8 +66,11 @@ type FooterAggregation = "sum" | "avg" | "count";
66
66
  interface FooterType {
67
67
  [key: string]: FooterAggregation;
68
68
  }
69
+ type GridDensity = "compact" | "standard" | "comfortable";
69
70
  interface TableProps {
70
71
  data: any;
72
+ /** Muestra un input de búsqueda general que filtra filas por palabras clave */
73
+ searchable?: boolean;
71
74
  flex?: number;
72
75
  editableFields?: string[];
73
76
  onSelect?: (data: GridValidRowModel[]) => void;
@@ -86,6 +89,28 @@ interface TableProps {
86
89
  className?: string;
87
90
  fontSize?: string;
88
91
  colSize?: Record<string, number>;
92
+ /**
93
+ * Alto fijo de fila en px.
94
+ * Si se omite usa el default de MUI según `density`
95
+ * (compact ≈ 36, standard ≈ 52, comfortable ≈ 68).
96
+ * Se ignora automáticamente cuando `wrapText` es true.
97
+ */
98
+ rowHeight?: number;
99
+ /**
100
+ * Cuando es true el texto largo hace wrap en lugar de truncarse,
101
+ * y el alto de cada fila crece para mostrar todo el contenido.
102
+ * Internamente activa `getRowHeight={() => "auto"}` en el DataGrid.
103
+ */
104
+ wrapText?: boolean;
105
+ /**
106
+ * Densidad visual de la tabla.
107
+ * Afecta el padding de celdas y encabezados independientemente
108
+ * del tamaño de fuente.
109
+ * - "compact" → padding mínimo, tabla muy densa
110
+ * - "standard" → (default) comportamiento por defecto de MUI
111
+ * - "comfortable" → padding generoso, fácil de leer
112
+ */
113
+ density?: GridDensity;
89
114
  [key: string]: any;
90
115
  }
91
116
  declare function Table(props: TableProps): react_jsx_runtime.JSX.Element;
package/dist/index.js CHANGED
@@ -35828,6 +35828,7 @@ function useColumns(rows, currentCoin, options, colSize) {
35828
35828
  buttons,
35829
35829
  hideColumns,
35830
35830
  modal,
35831
+ wrapText,
35831
35832
  handleRowUpdate,
35832
35833
  onModalOpen
35833
35834
  } = options;
@@ -35866,6 +35867,20 @@ function useColumns(rows, currentCoin, options, colSize) {
35866
35867
  width: key == "id" ? 80 : (_a = colSize == null ? void 0 : colSize[key]) != null ? _a : void 0,
35867
35868
  editable: (_b = editableFields == null ? void 0 : editableFields.includes(key)) != null ? _b : false,
35868
35869
  type: typeof rows[0][key] === "number" ? "number" : "string",
35870
+ // When wrapText is enabled, allow cells to grow vertically
35871
+ ...wrapText && {
35872
+ renderHeader: (params) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35873
+ "span",
35874
+ {
35875
+ style: {
35876
+ whiteSpace: "normal",
35877
+ lineHeight: 1.3,
35878
+ wordBreak: "break-word"
35879
+ },
35880
+ children: params.colDef.headerName
35881
+ }
35882
+ )
35883
+ },
35869
35884
  renderCell: (buttons == null ? void 0 : buttons[key]) ? (params) => {
35870
35885
  var _a2, _b2, _c;
35871
35886
  const children = ((_a2 = buttons == null ? void 0 : buttons[key]) == null ? void 0 : _a2.type) == "input" ? null : (_b2 = params == null ? void 0 : params.row) == null ? void 0 : _b2[key];
@@ -35882,6 +35897,24 @@ function useColumns(rows, currentCoin, options, colSize) {
35882
35897
  }
35883
35898
  }
35884
35899
  });
35900
+ } : wrapText ? (params) => {
35901
+ var _a2;
35902
+ return (
35903
+ // Plain cell with wrap — no custom button
35904
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35905
+ "span",
35906
+ {
35907
+ style: {
35908
+ whiteSpace: "normal",
35909
+ wordBreak: "break-word",
35910
+ lineHeight: 1.4,
35911
+ padding: "6px 0",
35912
+ display: "block"
35913
+ },
35914
+ children: (_a2 = params.formattedValue) != null ? _a2 : params.value
35915
+ }
35916
+ )
35917
+ );
35885
35918
  } : null
35886
35919
  };
35887
35920
  });
@@ -35889,7 +35922,6 @@ function useColumns(rows, currentCoin, options, colSize) {
35889
35922
  cols.unshift({
35890
35923
  field: "Modal",
35891
35924
  headerName: "Modal",
35892
- // flex,
35893
35925
  editable: false,
35894
35926
  type: "string",
35895
35927
  width: 10,
@@ -35908,7 +35940,57 @@ function useColumns(rows, currentCoin, options, colSize) {
35908
35940
  });
35909
35941
  }
35910
35942
  return cols;
35911
- }, [rows]);
35943
+ }, [rows, wrapText]);
35944
+ }
35945
+ function SearchBar({
35946
+ value,
35947
+ onChange
35948
+ }) {
35949
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 px-2 pb-1", children: [
35950
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35951
+ "svg",
35952
+ {
35953
+ className: "text-gray-400 shrink-0",
35954
+ width: "14",
35955
+ height: "14",
35956
+ viewBox: "0 0 20 20",
35957
+ fill: "currentColor",
35958
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35959
+ "path",
35960
+ {
35961
+ fillRule: "evenodd",
35962
+ d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
35963
+ clipRule: "evenodd"
35964
+ }
35965
+ )
35966
+ }
35967
+ ),
35968
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35969
+ "input",
35970
+ {
35971
+ type: "text",
35972
+ value,
35973
+ onChange: (e) => onChange(e.target.value),
35974
+ placeholder: "Buscar\u2026",
35975
+ className: "w-full max-w-xs text-xs border border-gray-300 rounded px-2 py-1 outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-200 transition"
35976
+ }
35977
+ ),
35978
+ value && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35979
+ "button",
35980
+ {
35981
+ onClick: () => onChange(""),
35982
+ className: "text-gray-400 hover:text-gray-600 text-sm leading-none",
35983
+ title: "Limpiar b\xFAsqueda",
35984
+ children: "\xD7"
35985
+ }
35986
+ )
35987
+ ] });
35988
+ }
35989
+ function rowMatchesSearch(row, query) {
35990
+ if (!query.trim()) return true;
35991
+ const words = query.trim().toLowerCase().split(/\s+/);
35992
+ const rowText = Object.values(row).filter((v) => v != null && v !== "").join(" ").toLowerCase();
35993
+ return words.every((word) => rowText.includes(word));
35912
35994
  }
35913
35995
  function IHTable({
35914
35996
  data,
@@ -35928,7 +36010,11 @@ function IHTable({
35928
36010
  currentCoin = "$",
35929
36011
  fontSize = "1rem",
35930
36012
  className,
35931
- colSize
36013
+ colSize,
36014
+ rowHeight,
36015
+ wrapText = false,
36016
+ density = "standard",
36017
+ searchable = false
35932
36018
  }) {
35933
36019
  var _a;
35934
36020
  if (modal && onSelect)
@@ -35940,6 +36026,7 @@ function IHTable({
35940
36026
  ids: /* @__PURE__ */ new Set()
35941
36027
  });
35942
36028
  const [modalRow, setModalRow] = (0, import_react4.useState)();
36029
+ const [searchQuery, setSearchQuery] = (0, import_react4.useState)("");
35943
36030
  const resolvedButtons = modal ? { ...buttons, Modal: "" } : buttons;
35944
36031
  (0, import_react4.useEffect)(() => {
35945
36032
  setRows(data);
@@ -35971,8 +36058,12 @@ function IHTable({
35971
36058
  }
35972
36059
  return [];
35973
36060
  }, [selectedRows, rows]);
36061
+ const displayRows = (0, import_react4.useMemo)(
36062
+ () => searchable ? rows.filter((r) => rowMatchesSearch(r, searchQuery)) : rows,
36063
+ [rows, searchQuery, searchable]
36064
+ );
35974
36065
  const columns = useColumns(
35975
- rows,
36066
+ displayRows,
35976
36067
  currentCoin,
35977
36068
  {
35978
36069
  flex,
@@ -35980,31 +36071,36 @@ function IHTable({
35980
36071
  buttons: resolvedButtons,
35981
36072
  hideColumns,
35982
36073
  modal,
36074
+ wrapText,
35983
36075
  handleRowUpdate,
35984
36076
  onModalOpen: handleModalOpen
35985
36077
  },
35986
36078
  colSize
35987
36079
  );
35988
36080
  if (!rows.length) return null;
36081
+ const rowHeightProps = wrapText ? { getRowHeight: () => "auto" } : rowHeight != null ? { rowHeight } : {};
35989
36082
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "m-2", children: [
35990
36083
  header && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "font-bold p-2 ", children: header }),
35991
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
35992
- Toolbar,
35993
- {
35994
- exportName,
35995
- onSave,
35996
- onSelect,
35997
- rows,
35998
- filteredRows
35999
- }
36000
- ),
36084
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between", children: [
36085
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
36086
+ Toolbar,
36087
+ {
36088
+ exportName,
36089
+ onSave,
36090
+ onSelect,
36091
+ rows,
36092
+ filteredRows
36093
+ }
36094
+ ),
36095
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SearchBar, { value: searchQuery, onChange: setSearchQuery })
36096
+ ] }),
36001
36097
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
36002
36098
  import_material.Box,
36003
36099
  {
36004
36100
  sx: {
36005
36101
  display: "flex",
36006
36102
  flexDirection: "column",
36007
- height: (_a = HEIGHT_MAP[rows.length]) != null ? _a : height,
36103
+ height: (_a = HEIGHT_MAP[displayRows.length]) != null ? _a : height,
36008
36104
  width,
36009
36105
  zIndex: 999999999
36010
36106
  },
@@ -36022,25 +36118,42 @@ function IHTable({
36022
36118
  import_x_data_grid.DataGrid,
36023
36119
  {
36024
36120
  className,
36025
- rows,
36121
+ rows: displayRows,
36026
36122
  columns,
36123
+ density,
36027
36124
  checkboxSelection: Boolean(onSelect),
36028
36125
  rowSelectionModel: selectedRows,
36029
36126
  onRowSelectionModelChange: !modal ? setSelectedRows : void 0,
36127
+ ...rowHeightProps,
36030
36128
  sx: {
36031
36129
  fontSize,
36032
- // equivalente a text-xs
36033
36130
  "& .MuiDataGrid-cell": {
36034
- fontSize
36131
+ fontSize,
36132
+ // Allow cells to wrap text when wrapText is enabled
36133
+ ...wrapText && {
36134
+ alignItems: "flex-start",
36135
+ paddingTop: "8px",
36136
+ paddingBottom: "8px",
36137
+ whiteSpace: "normal",
36138
+ wordBreak: "break-word"
36139
+ }
36035
36140
  },
36036
36141
  "& .MuiDataGrid-columnHeader": {
36037
- fontSize
36142
+ fontSize,
36143
+ ...wrapText && {
36144
+ whiteSpace: "normal",
36145
+ "& .MuiDataGrid-columnHeaderTitle": {
36146
+ whiteSpace: "normal",
36147
+ lineHeight: 1.3,
36148
+ wordBreak: "break-word"
36149
+ }
36150
+ }
36038
36151
  },
36039
36152
  "& .MuiDataGrid-cell--editable": {
36040
36153
  backgroundColor: "#c6d8f0",
36041
36154
  fontWeight: 500
36042
36155
  },
36043
- ...rows.length <= Object.keys(HEIGHT_MAP).length && {
36156
+ ...displayRows.length <= Object.keys(HEIGHT_MAP).length && {
36044
36157
  "& .MuiDataGrid-filler": {
36045
36158
  display: "none"
36046
36159
  }
@@ -36049,13 +36162,13 @@ function IHTable({
36049
36162
  editMode: "row",
36050
36163
  processRowUpdate: handleRowUpdate,
36051
36164
  pageSizeOptions: [5, 10],
36052
- hideFooter: rows.length <= Object.keys(HEIGHT_MAP).length
36165
+ hideFooter: displayRows.length <= Object.keys(HEIGHT_MAP).length
36053
36166
  }
36054
36167
  )
36055
36168
  ]
36056
36169
  }
36057
36170
  ),
36058
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomFooter, { footer, rows })
36171
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomFooter, { footer, rows: displayRows })
36059
36172
  ] });
36060
36173
  }
36061
36174
  function Table(props) {
package/dist/index.mjs CHANGED
@@ -35808,6 +35808,7 @@ function useColumns(rows, currentCoin, options, colSize) {
35808
35808
  buttons,
35809
35809
  hideColumns,
35810
35810
  modal,
35811
+ wrapText,
35811
35812
  handleRowUpdate,
35812
35813
  onModalOpen
35813
35814
  } = options;
@@ -35846,6 +35847,20 @@ function useColumns(rows, currentCoin, options, colSize) {
35846
35847
  width: key == "id" ? 80 : (_a = colSize == null ? void 0 : colSize[key]) != null ? _a : void 0,
35847
35848
  editable: (_b = editableFields == null ? void 0 : editableFields.includes(key)) != null ? _b : false,
35848
35849
  type: typeof rows[0][key] === "number" ? "number" : "string",
35850
+ // When wrapText is enabled, allow cells to grow vertically
35851
+ ...wrapText && {
35852
+ renderHeader: (params) => /* @__PURE__ */ jsx6(
35853
+ "span",
35854
+ {
35855
+ style: {
35856
+ whiteSpace: "normal",
35857
+ lineHeight: 1.3,
35858
+ wordBreak: "break-word"
35859
+ },
35860
+ children: params.colDef.headerName
35861
+ }
35862
+ )
35863
+ },
35849
35864
  renderCell: (buttons == null ? void 0 : buttons[key]) ? (params) => {
35850
35865
  var _a2, _b2, _c;
35851
35866
  const children = ((_a2 = buttons == null ? void 0 : buttons[key]) == null ? void 0 : _a2.type) == "input" ? null : (_b2 = params == null ? void 0 : params.row) == null ? void 0 : _b2[key];
@@ -35862,6 +35877,24 @@ function useColumns(rows, currentCoin, options, colSize) {
35862
35877
  }
35863
35878
  }
35864
35879
  });
35880
+ } : wrapText ? (params) => {
35881
+ var _a2;
35882
+ return (
35883
+ // Plain cell with wrap — no custom button
35884
+ /* @__PURE__ */ jsx6(
35885
+ "span",
35886
+ {
35887
+ style: {
35888
+ whiteSpace: "normal",
35889
+ wordBreak: "break-word",
35890
+ lineHeight: 1.4,
35891
+ padding: "6px 0",
35892
+ display: "block"
35893
+ },
35894
+ children: (_a2 = params.formattedValue) != null ? _a2 : params.value
35895
+ }
35896
+ )
35897
+ );
35865
35898
  } : null
35866
35899
  };
35867
35900
  });
@@ -35869,7 +35902,6 @@ function useColumns(rows, currentCoin, options, colSize) {
35869
35902
  cols.unshift({
35870
35903
  field: "Modal",
35871
35904
  headerName: "Modal",
35872
- // flex,
35873
35905
  editable: false,
35874
35906
  type: "string",
35875
35907
  width: 10,
@@ -35888,7 +35920,57 @@ function useColumns(rows, currentCoin, options, colSize) {
35888
35920
  });
35889
35921
  }
35890
35922
  return cols;
35891
- }, [rows]);
35923
+ }, [rows, wrapText]);
35924
+ }
35925
+ function SearchBar({
35926
+ value,
35927
+ onChange
35928
+ }) {
35929
+ return /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 px-2 pb-1", children: [
35930
+ /* @__PURE__ */ jsx6(
35931
+ "svg",
35932
+ {
35933
+ className: "text-gray-400 shrink-0",
35934
+ width: "14",
35935
+ height: "14",
35936
+ viewBox: "0 0 20 20",
35937
+ fill: "currentColor",
35938
+ children: /* @__PURE__ */ jsx6(
35939
+ "path",
35940
+ {
35941
+ fillRule: "evenodd",
35942
+ d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
35943
+ clipRule: "evenodd"
35944
+ }
35945
+ )
35946
+ }
35947
+ ),
35948
+ /* @__PURE__ */ jsx6(
35949
+ "input",
35950
+ {
35951
+ type: "text",
35952
+ value,
35953
+ onChange: (e) => onChange(e.target.value),
35954
+ placeholder: "Buscar\u2026",
35955
+ className: "w-full max-w-xs text-xs border border-gray-300 rounded px-2 py-1 outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-200 transition"
35956
+ }
35957
+ ),
35958
+ value && /* @__PURE__ */ jsx6(
35959
+ "button",
35960
+ {
35961
+ onClick: () => onChange(""),
35962
+ className: "text-gray-400 hover:text-gray-600 text-sm leading-none",
35963
+ title: "Limpiar b\xFAsqueda",
35964
+ children: "\xD7"
35965
+ }
35966
+ )
35967
+ ] });
35968
+ }
35969
+ function rowMatchesSearch(row, query) {
35970
+ if (!query.trim()) return true;
35971
+ const words = query.trim().toLowerCase().split(/\s+/);
35972
+ const rowText = Object.values(row).filter((v) => v != null && v !== "").join(" ").toLowerCase();
35973
+ return words.every((word) => rowText.includes(word));
35892
35974
  }
35893
35975
  function IHTable({
35894
35976
  data,
@@ -35908,7 +35990,11 @@ function IHTable({
35908
35990
  currentCoin = "$",
35909
35991
  fontSize = "1rem",
35910
35992
  className,
35911
- colSize
35993
+ colSize,
35994
+ rowHeight,
35995
+ wrapText = false,
35996
+ density = "standard",
35997
+ searchable = false
35912
35998
  }) {
35913
35999
  var _a;
35914
36000
  if (modal && onSelect)
@@ -35920,6 +36006,7 @@ function IHTable({
35920
36006
  ids: /* @__PURE__ */ new Set()
35921
36007
  });
35922
36008
  const [modalRow, setModalRow] = useState4();
36009
+ const [searchQuery, setSearchQuery] = useState4("");
35923
36010
  const resolvedButtons = modal ? { ...buttons, Modal: "" } : buttons;
35924
36011
  useEffect3(() => {
35925
36012
  setRows(data);
@@ -35951,8 +36038,12 @@ function IHTable({
35951
36038
  }
35952
36039
  return [];
35953
36040
  }, [selectedRows, rows]);
36041
+ const displayRows = useMemo(
36042
+ () => searchable ? rows.filter((r) => rowMatchesSearch(r, searchQuery)) : rows,
36043
+ [rows, searchQuery, searchable]
36044
+ );
35954
36045
  const columns = useColumns(
35955
- rows,
36046
+ displayRows,
35956
36047
  currentCoin,
35957
36048
  {
35958
36049
  flex,
@@ -35960,31 +36051,36 @@ function IHTable({
35960
36051
  buttons: resolvedButtons,
35961
36052
  hideColumns,
35962
36053
  modal,
36054
+ wrapText,
35963
36055
  handleRowUpdate,
35964
36056
  onModalOpen: handleModalOpen
35965
36057
  },
35966
36058
  colSize
35967
36059
  );
35968
36060
  if (!rows.length) return null;
36061
+ const rowHeightProps = wrapText ? { getRowHeight: () => "auto" } : rowHeight != null ? { rowHeight } : {};
35969
36062
  return /* @__PURE__ */ jsxs5("div", { className: "m-2", children: [
35970
36063
  header && /* @__PURE__ */ jsx6("div", { className: "font-bold p-2 ", children: header }),
35971
- /* @__PURE__ */ jsx6(
35972
- Toolbar,
35973
- {
35974
- exportName,
35975
- onSave,
35976
- onSelect,
35977
- rows,
35978
- filteredRows
35979
- }
35980
- ),
36064
+ /* @__PURE__ */ jsxs5("div", { className: "flex justify-between", children: [
36065
+ /* @__PURE__ */ jsx6(
36066
+ Toolbar,
36067
+ {
36068
+ exportName,
36069
+ onSave,
36070
+ onSelect,
36071
+ rows,
36072
+ filteredRows
36073
+ }
36074
+ ),
36075
+ searchable && /* @__PURE__ */ jsx6(SearchBar, { value: searchQuery, onChange: setSearchQuery })
36076
+ ] }),
35981
36077
  /* @__PURE__ */ jsxs5(
35982
36078
  Box,
35983
36079
  {
35984
36080
  sx: {
35985
36081
  display: "flex",
35986
36082
  flexDirection: "column",
35987
- height: (_a = HEIGHT_MAP[rows.length]) != null ? _a : height,
36083
+ height: (_a = HEIGHT_MAP[displayRows.length]) != null ? _a : height,
35988
36084
  width,
35989
36085
  zIndex: 999999999
35990
36086
  },
@@ -36002,25 +36098,42 @@ function IHTable({
36002
36098
  DataGrid,
36003
36099
  {
36004
36100
  className,
36005
- rows,
36101
+ rows: displayRows,
36006
36102
  columns,
36103
+ density,
36007
36104
  checkboxSelection: Boolean(onSelect),
36008
36105
  rowSelectionModel: selectedRows,
36009
36106
  onRowSelectionModelChange: !modal ? setSelectedRows : void 0,
36107
+ ...rowHeightProps,
36010
36108
  sx: {
36011
36109
  fontSize,
36012
- // equivalente a text-xs
36013
36110
  "& .MuiDataGrid-cell": {
36014
- fontSize
36111
+ fontSize,
36112
+ // Allow cells to wrap text when wrapText is enabled
36113
+ ...wrapText && {
36114
+ alignItems: "flex-start",
36115
+ paddingTop: "8px",
36116
+ paddingBottom: "8px",
36117
+ whiteSpace: "normal",
36118
+ wordBreak: "break-word"
36119
+ }
36015
36120
  },
36016
36121
  "& .MuiDataGrid-columnHeader": {
36017
- fontSize
36122
+ fontSize,
36123
+ ...wrapText && {
36124
+ whiteSpace: "normal",
36125
+ "& .MuiDataGrid-columnHeaderTitle": {
36126
+ whiteSpace: "normal",
36127
+ lineHeight: 1.3,
36128
+ wordBreak: "break-word"
36129
+ }
36130
+ }
36018
36131
  },
36019
36132
  "& .MuiDataGrid-cell--editable": {
36020
36133
  backgroundColor: "#c6d8f0",
36021
36134
  fontWeight: 500
36022
36135
  },
36023
- ...rows.length <= Object.keys(HEIGHT_MAP).length && {
36136
+ ...displayRows.length <= Object.keys(HEIGHT_MAP).length && {
36024
36137
  "& .MuiDataGrid-filler": {
36025
36138
  display: "none"
36026
36139
  }
@@ -36029,13 +36142,13 @@ function IHTable({
36029
36142
  editMode: "row",
36030
36143
  processRowUpdate: handleRowUpdate,
36031
36144
  pageSizeOptions: [5, 10],
36032
- hideFooter: rows.length <= Object.keys(HEIGHT_MAP).length
36145
+ hideFooter: displayRows.length <= Object.keys(HEIGHT_MAP).length
36033
36146
  }
36034
36147
  )
36035
36148
  ]
36036
36149
  }
36037
36150
  ),
36038
- /* @__PURE__ */ jsx6(CustomFooter, { footer, rows })
36151
+ /* @__PURE__ */ jsx6(CustomFooter, { footer, rows: displayRows })
36039
36152
  ] });
36040
36153
  }
36041
36154
  function Table(props) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-recomponents",
3
- "version": "2.0.27",
3
+ "version": "2.0.28",
4
4
  "description": "description nueva",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,8 +22,12 @@ interface FooterType {
22
22
  [key: string]: FooterAggregation;
23
23
  }
24
24
 
25
+ type GridDensity = "compact" | "standard" | "comfortable";
26
+
25
27
  interface TableProps {
26
28
  data: any;
29
+ /** Muestra un input de búsqueda general que filtra filas por palabras clave */
30
+ searchable?: boolean;
27
31
  flex?: number;
28
32
  editableFields?: string[];
29
33
  onSelect?: (data: GridValidRowModel[]) => void;
@@ -44,6 +48,28 @@ interface TableProps {
44
48
  className?: string;
45
49
  fontSize?: string;
46
50
  colSize?: Record<string, number>;
51
+ /**
52
+ * Alto fijo de fila en px.
53
+ * Si se omite usa el default de MUI según `density`
54
+ * (compact ≈ 36, standard ≈ 52, comfortable ≈ 68).
55
+ * Se ignora automáticamente cuando `wrapText` es true.
56
+ */
57
+ rowHeight?: number;
58
+ /**
59
+ * Cuando es true el texto largo hace wrap en lugar de truncarse,
60
+ * y el alto de cada fila crece para mostrar todo el contenido.
61
+ * Internamente activa `getRowHeight={() => "auto"}` en el DataGrid.
62
+ */
63
+ wrapText?: boolean;
64
+ /**
65
+ * Densidad visual de la tabla.
66
+ * Afecta el padding de celdas y encabezados independientemente
67
+ * del tamaño de fuente.
68
+ * - "compact" → padding mínimo, tabla muy densa
69
+ * - "standard" → (default) comportamiento por defecto de MUI
70
+ * - "comfortable" → padding generoso, fácil de leer
71
+ */
72
+ density?: GridDensity;
47
73
  [key: string]: any;
48
74
  }
49
75
 
@@ -256,6 +282,7 @@ function useColumns(
256
282
  buttons?: Record<string, any>;
257
283
  hideColumns: string[];
258
284
  modal?: React.ReactNode;
285
+ wrapText?: boolean;
259
286
  handleRowUpdate: (row: GridRowModel) => GridRowModel;
260
287
  onModalOpen: (row: GridValidRowModel) => void;
261
288
  },
@@ -267,9 +294,11 @@ function useColumns(
267
294
  buttons,
268
295
  hideColumns,
269
296
  modal,
297
+ wrapText,
270
298
  handleRowUpdate,
271
299
  onModalOpen,
272
300
  } = options;
301
+
273
302
  return useMemo(() => {
274
303
  if (!rows.length) return [];
275
304
 
@@ -309,7 +338,6 @@ function useColumns(
309
338
 
310
339
  if (isNumber) {
311
340
  if (isNaN(value)) return value;
312
-
313
341
  return value;
314
342
  }
315
343
 
@@ -319,6 +347,20 @@ function useColumns(
319
347
  width: key == "id" ? 80 : (colSize?.[key] ?? undefined),
320
348
  editable: editableFields?.includes(key) ?? false,
321
349
  type: typeof rows[0][key] === "number" ? "number" : "string",
350
+ // When wrapText is enabled, allow cells to grow vertically
351
+ ...(wrapText && {
352
+ renderHeader: (params: any) => (
353
+ <span
354
+ style={{
355
+ whiteSpace: "normal",
356
+ lineHeight: 1.3,
357
+ wordBreak: "break-word",
358
+ }}
359
+ >
360
+ {params.colDef.headerName}
361
+ </span>
362
+ ),
363
+ }),
322
364
  renderCell: buttons?.[key]
323
365
  ? (params: any) => {
324
366
  const children =
@@ -338,14 +380,28 @@ function useColumns(
338
380
  },
339
381
  });
340
382
  }
341
- : null,
383
+ : wrapText
384
+ ? (params: any) => (
385
+ // Plain cell with wrap — no custom button
386
+ <span
387
+ style={{
388
+ whiteSpace: "normal",
389
+ wordBreak: "break-word",
390
+ lineHeight: 1.4,
391
+ padding: "6px 0",
392
+ display: "block",
393
+ }}
394
+ >
395
+ {params.formattedValue ?? params.value}
396
+ </span>
397
+ )
398
+ : null,
342
399
  }));
343
400
 
344
401
  if (modal) {
345
402
  cols.unshift({
346
403
  field: "Modal",
347
404
  headerName: "Modal",
348
- // flex,
349
405
  editable: false,
350
406
  type: "string",
351
407
  width: 10,
@@ -363,10 +419,63 @@ function useColumns(
363
419
  }
364
420
 
365
421
  return cols;
366
- }, [rows]);
422
+ }, [rows, wrapText]);
367
423
  }
368
424
 
369
- // ─── IHTable (array data) ─────────────────────────────────────────────────────
425
+ // ─── SearchBar ────────────────────────────────────────────────────────────────
426
+
427
+ function SearchBar({
428
+ value,
429
+ onChange,
430
+ }: {
431
+ value: string;
432
+ onChange: (v: string) => void;
433
+ }) {
434
+ return (
435
+ <div className="flex items-center gap-2 px-2 pb-1">
436
+ <svg
437
+ className="text-gray-400 shrink-0"
438
+ width="14"
439
+ height="14"
440
+ viewBox="0 0 20 20"
441
+ fill="currentColor"
442
+ >
443
+ <path
444
+ fillRule="evenodd"
445
+ d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
446
+ clipRule="evenodd"
447
+ />
448
+ </svg>
449
+ <input
450
+ type="text"
451
+ value={value}
452
+ onChange={(e) => onChange(e.target.value)}
453
+ placeholder="Buscar…"
454
+ className="w-full max-w-xs text-xs border border-gray-300 rounded px-2 py-1 outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-200 transition"
455
+ />
456
+ {value && (
457
+ <button
458
+ onClick={() => onChange("")}
459
+ className="text-gray-400 hover:text-gray-600 text-sm leading-none"
460
+ title="Limpiar búsqueda"
461
+ >
462
+ &times;
463
+ </button>
464
+ )}
465
+ </div>
466
+ );
467
+ }
468
+
469
+ /** Devuelve true si la fila contiene TODAS las palabras del query */
470
+ function rowMatchesSearch(row: GridValidRowModel, query: string): boolean {
471
+ if (!query.trim()) return true;
472
+ const words = query.trim().toLowerCase().split(/\s+/);
473
+ const rowText = Object.values(row)
474
+ .filter((v) => v != null && v !== "")
475
+ .join(" ")
476
+ .toLowerCase();
477
+ return words.every((word) => rowText.includes(word));
478
+ }
370
479
 
371
480
  function IHTable({
372
481
  data,
@@ -387,6 +496,10 @@ function IHTable({
387
496
  fontSize = "1rem",
388
497
  className,
389
498
  colSize,
499
+ rowHeight,
500
+ wrapText = false,
501
+ density = "standard",
502
+ searchable = false,
390
503
  }: TableProps) {
391
504
  if (modal && onSelect)
392
505
  throw new Error("Solo se puede usar modal o onSelect por separado");
@@ -398,6 +511,7 @@ function IHTable({
398
511
  ids: new Set(),
399
512
  });
400
513
  const [modalRow, setModalRow] = useState<GridValidRowModel | undefined>();
514
+ const [searchQuery, setSearchQuery] = useState("");
401
515
 
402
516
  // Inject Modal button key if modal is provided
403
517
  const resolvedButtons = modal ? { ...buttons, Modal: "" } : buttons;
@@ -437,8 +551,15 @@ function IHTable({
437
551
  return [];
438
552
  }, [selectedRows, rows]);
439
553
 
554
+ // Rows after applying the search filter (only when searchable is enabled)
555
+ const displayRows = useMemo(
556
+ () =>
557
+ searchable ? rows.filter((r) => rowMatchesSearch(r, searchQuery)) : rows,
558
+ [rows, searchQuery, searchable],
559
+ );
560
+
440
561
  const columns = useColumns(
441
- rows,
562
+ displayRows,
442
563
  currentCoin,
443
564
  {
444
565
  flex,
@@ -446,6 +567,7 @@ function IHTable({
446
567
  buttons: resolvedButtons,
447
568
  hideColumns,
448
569
  modal,
570
+ wrapText,
449
571
  handleRowUpdate,
450
572
  onModalOpen: handleModalOpen,
451
573
  },
@@ -454,22 +576,34 @@ function IHTable({
454
576
 
455
577
  if (!rows.length) return null;
456
578
 
579
+ // When wrapText is active we must use getRowHeight; rowHeight prop is ignored.
580
+ const rowHeightProps = wrapText
581
+ ? { getRowHeight: () => "auto" as const }
582
+ : rowHeight != null
583
+ ? { rowHeight }
584
+ : {};
585
+
457
586
  return (
458
587
  <div className="m-2">
459
588
  {header && <div className="font-bold p-2 ">{header}</div>}
460
- <Toolbar
461
- exportName={exportName}
462
- onSave={onSave}
463
- onSelect={onSelect}
464
- rows={rows}
465
- filteredRows={filteredRows}
466
- />
589
+ <div className="flex justify-between">
590
+ <Toolbar
591
+ exportName={exportName}
592
+ onSave={onSave}
593
+ onSelect={onSelect}
594
+ rows={rows}
595
+ filteredRows={filteredRows}
596
+ />
597
+ {searchable && (
598
+ <SearchBar value={searchQuery} onChange={setSearchQuery} />
599
+ )}
600
+ </div>
467
601
 
468
602
  <Box
469
603
  sx={{
470
604
  display: "flex",
471
605
  flexDirection: "column",
472
- height: HEIGHT_MAP[rows.length] ?? height,
606
+ height: HEIGHT_MAP[displayRows.length] ?? height,
473
607
  width,
474
608
  zIndex: 999999999,
475
609
  }}
@@ -485,24 +619,42 @@ function IHTable({
485
619
 
486
620
  <DataGrid
487
621
  className={className}
488
- rows={rows}
622
+ rows={displayRows}
489
623
  columns={columns as any}
624
+ density={density}
490
625
  checkboxSelection={Boolean(onSelect)}
491
626
  rowSelectionModel={selectedRows}
492
627
  onRowSelectionModelChange={!modal ? setSelectedRows : undefined}
628
+ {...rowHeightProps}
493
629
  sx={{
494
- fontSize, // equivalente a text-xs
630
+ fontSize,
495
631
  "& .MuiDataGrid-cell": {
496
632
  fontSize,
633
+ // Allow cells to wrap text when wrapText is enabled
634
+ ...(wrapText && {
635
+ alignItems: "flex-start",
636
+ paddingTop: "8px",
637
+ paddingBottom: "8px",
638
+ whiteSpace: "normal",
639
+ wordBreak: "break-word",
640
+ }),
497
641
  },
498
642
  "& .MuiDataGrid-columnHeader": {
499
643
  fontSize,
644
+ ...(wrapText && {
645
+ whiteSpace: "normal",
646
+ "& .MuiDataGrid-columnHeaderTitle": {
647
+ whiteSpace: "normal",
648
+ lineHeight: 1.3,
649
+ wordBreak: "break-word",
650
+ },
651
+ }),
500
652
  },
501
653
  "& .MuiDataGrid-cell--editable": {
502
654
  backgroundColor: "#c6d8f0",
503
655
  fontWeight: 500,
504
656
  },
505
- ...(rows.length <= Object.keys(HEIGHT_MAP).length && {
657
+ ...(displayRows.length <= Object.keys(HEIGHT_MAP).length && {
506
658
  "& .MuiDataGrid-filler": {
507
659
  display: "none",
508
660
  },
@@ -511,10 +663,10 @@ function IHTable({
511
663
  editMode="row"
512
664
  processRowUpdate={handleRowUpdate}
513
665
  pageSizeOptions={[5, 10]}
514
- hideFooter={rows.length <= Object.keys(HEIGHT_MAP).length}
666
+ hideFooter={displayRows.length <= Object.keys(HEIGHT_MAP).length}
515
667
  />
516
668
  </Box>
517
- <CustomFooter footer={footer} rows={rows} />
669
+ <CustomFooter footer={footer} rows={displayRows} />
518
670
  </div>
519
671
  );
520
672
  }