@zauru-sdk/components 6.14.1 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [6.15.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.14.2...@zauru-sdk/components@6.15.0) (2026-10-28)
7
+
8
+
9
+ ### Features
10
+
11
+ * permitir editar GenericDynamicTable sin agregar o borrar filas ([7fc9b41](https://github.com/intuitiva/zauru-typescript-sdk/commit/7fc9b41a7aebf2627a674bc8d9d70b772c378056))
12
+
13
+
14
+
15
+
16
+
17
+ ## [6.14.2](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.14.1...@zauru-sdk/components@6.14.2) (2026-10-26)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * alinear encabezados y celdas de ZauruTable ([46783d1](https://github.com/intuitiva/zauru-typescript-sdk/commit/46783d1f))
23
+
24
+
6
25
  ## [6.14.1](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.14.0...@zauru-sdk/components@6.14.1) (2026-10-26)
7
26
 
8
27
  **Note:** Version bump only for package @zauru-sdk/components
@@ -15,8 +15,13 @@ type Props = {
15
15
  footerRow?: FooterColumnConfig[];
16
16
  thCSSProperties?: React.CSSProperties;
17
17
  thElementsClassName?: string;
18
- /** Controla si se pueden o no editar los campos (oculta botones y/o deshabilita campos). */
18
+ /** Controla si se pueden editar los campos. */
19
19
  editable?: boolean;
20
+ /**
21
+ * Si es false, no muestra agregar/eliminar filas aunque `editable` esté activo.
22
+ * Útil para catálogos fijos cuyos valores sí se editan.
23
+ */
24
+ rowActions?: boolean;
20
25
  /** Opciones de búsqueda. */
21
26
  searcheables?: SelectFieldOption[];
22
27
  /** Activa o desactiva el “skeleton” de carga. */
@@ -21,5 +21,7 @@ type Props = TableProps<any> & {
21
21
  theme?: "solarized" | "subTable";
22
22
  className?: string;
23
23
  };
24
+ export declare const normalizeZauruTableColumns: (columns: any) => any;
25
+ export declare const zauruTableFlexBasisCss: (tableClassName: string, columns: any) => string;
24
26
  export declare const ZauruTable: (props: Props) => import("react/jsx-runtime").JSX.Element;
25
27
  export {};
@@ -97,12 +97,13 @@ const GenericDynamicTableErrorComponent = ({ name }) => {
97
97
  */
98
98
  export const GenericDynamicTable = (props) => {
99
99
  const { columns, onChange, className, footerRow, defaultValue = [], thCSSProperties, thElementsClassName = "", editable = true, readOnly = false, // Nuevo prop
100
- searcheables = [], loading = false, paginated = true, defaultItemsPerPage = 10, itemsPerPageOptions = [10, 50, 100], name, withoutBg = false, orientation = "horizontal", maxRows, confirmDelete = true, addRowButtonHandler, rowSummary, } = props;
100
+ rowActions = true, searcheables = [], loading = false, paginated = true, defaultItemsPerPage = 10, itemsPerPageOptions = [10, 50, 100], name, withoutBg = false, orientation = "horizontal", maxRows, confirmDelete = true, addRowButtonHandler, rowSummary, } = props;
101
101
  /**
102
102
  * Definimos una variable interna para saber si los campos son
103
103
  * efectivamente editables: solo si `editable` es true y `readOnly` es false.
104
104
  */
105
105
  const isEditable = editable && !readOnly;
106
+ const showRowActions = isEditable && rowActions;
106
107
  try {
107
108
  const [tableData, setTableData] = useState(defaultValue);
108
109
  const [deletedData, setDeletedData] = useState([]);
@@ -174,9 +175,9 @@ export const GenericDynamicTable = (props) => {
174
175
  if (orientation === "horizontal") {
175
176
  return (_jsxs("tr", { style: { ...thCSSProperties }, children: [rendereableColumns.map((column, index) => {
176
177
  const ancho = column.width ??
177
- (isEditable ? 94 : 100) / (rendereableColumns.length ?? 1);
178
+ (showRowActions ? 94 : 100) / (rendereableColumns.length ?? 1);
178
179
  return (_jsx("th", { className: `text-left align-middle p-2 ${thElementsClassName} ${column.headerClassName || ""}`, style: { width: `${ancho}%` }, children: column.label }, index));
179
- }), isEditable && _jsx("th", { style: { width: "4%" } })] }));
180
+ }), showRowActions && _jsx("th", { style: { width: "4%" } })] }));
180
181
  }
181
182
  else {
182
183
  return null;
@@ -186,13 +187,13 @@ export const GenericDynamicTable = (props) => {
186
187
  const rendereableColumns = columns.filter((column) => column.type !== "hidden");
187
188
  if (orientation === "horizontal") {
188
189
  const rowClassName = index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "";
189
- const summaryColSpan = rendereableColumns.length + (isEditable ? 1 : 0);
190
+ const summaryColSpan = rendereableColumns.length + (showRowActions ? 1 : 0);
190
191
  const summaryContent = rowSummary?.(rowData);
191
- return (_jsxs(React.Fragment, { children: [_jsxs("tr", { className: rowClassName, children: [rendereableColumns.map((column) => renderCell(rowData, column)), isEditable && renderDeleteButton(rowData)] }), summaryContent ? (_jsx("tr", { className: rowClassName, children: _jsxs("td", { colSpan: summaryColSpan, className: "px-2 pb-3 pt-0 text-sm text-gray-700 border-b border-gray-300", children: [_jsx("span", { className: "font-medium text-gray-500", children: "Resumen: " }), summaryContent] }) })) : null] }, rowData.id));
192
+ return (_jsxs(React.Fragment, { children: [_jsxs("tr", { className: rowClassName, children: [rendereableColumns.map((column) => renderCell(rowData, column)), showRowActions && renderDeleteButton(rowData)] }), summaryContent ? (_jsx("tr", { className: rowClassName, children: _jsxs("td", { colSpan: summaryColSpan, className: "px-2 pb-3 pt-0 text-sm text-gray-700 border-b border-gray-300", children: [_jsx("span", { className: "font-medium text-gray-500", children: "Resumen: " }), summaryContent] }) })) : null] }, rowData.id));
192
193
  }
193
194
  else {
194
195
  // Orientación vertical
195
- return rendereableColumns.map((column) => (_jsxs("tr", { className: index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "", children: [_jsx("th", { className: `text-left align-middle p-2 ${thElementsClassName} ${column.headerClassName || ""}`, children: column.label }), renderCell(rowData, column), isEditable &&
196
+ return rendereableColumns.map((column) => (_jsxs("tr", { className: index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "", children: [_jsx("th", { className: `text-left align-middle p-2 ${thElementsClassName} ${column.headerClassName || ""}`, children: column.label }), renderCell(rowData, column), showRowActions &&
196
197
  column === rendereableColumns[rendereableColumns.length - 1] &&
197
198
  renderDeleteButton(rowData)] }, `${rowData.id}-${column.name}`)));
198
199
  }
@@ -303,7 +304,7 @@ export const GenericDynamicTable = (props) => {
303
304
  };
304
305
  return (_jsxs(_Fragment, { children: [name && (_jsxs(_Fragment, { children: [_jsx(GenericDynamicTableErrorComponent, { name: name }), _jsx("input", { name: name, type: "hidden", value: JSON.stringify(tableData), hidden: true }), _jsx("input", { name: `deleted_${name}`, type: "hidden", value: JSON.stringify(deletedData), hidden: true })] })), _jsxs("div", { className: `${className}`, children: [searcheables.length > 0 && (_jsx("div", { children: _jsx(TextField, { className: "mb-2", name: "search", title: `Buscar por: ${searcheables
305
306
  .map((x) => x.label)
306
- .join(", ")}`, onChange: handleChangeSearch, disabled: loading || readOnly }) })), _jsxs("table", { className: "w-full", children: [orientation === "horizontal" && _jsx("thead", { children: renderHeader() }), _jsx("tbody", { children: renderRows() }), isEditable && (_jsx("tfoot", { children: _jsx("tr", { children: _jsx("td", { colSpan: orientation === "horizontal" ? columns.length + 1 : 2, className: "align-middle text-right", children: (!maxRows || tableData.length < maxRows) && (_jsx("button", { className: "bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded", onClick: (event) => {
307
+ .join(", ")}`, onChange: handleChangeSearch, disabled: loading || readOnly }) })), _jsxs("table", { className: "w-full", children: [orientation === "horizontal" && _jsx("thead", { children: renderHeader() }), _jsx("tbody", { children: renderRows() }), showRowActions && (_jsx("tfoot", { children: _jsx("tr", { children: _jsx("td", { colSpan: orientation === "horizontal" ? columns.length + 1 : 2, className: "align-middle text-right", children: (!maxRows || tableData.length < maxRows) && (_jsx("button", { className: "bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded", onClick: (event) => {
307
308
  event.preventDefault();
308
309
  event.stopPropagation();
309
310
  if (addRowButtonHandler) {
@@ -317,7 +318,7 @@ export const GenericDynamicTable = (props) => {
317
318
  .map((column, index) => {
318
319
  const footerCell = footerRow.find((fc) => fc.name === column.name);
319
320
  return (_jsx("td", { colSpan: orientation === "vertical" ? 2 : 1, className: `align-middle ${footerCell?.className || ""}`, children: footerCell ? (footerCell.cell ? (footerCell.cell(tableData)) : (footerCell.content)) : (_jsx(_Fragment, {})) }, index));
320
- }), isEditable && _jsx("td", {})] }) }))] }), paginated && totalPages() > 1 && (_jsxs("div", { className: "flex justify-between items-center mt-4", children: [_jsxs("div", { className: "flex items-center", children: [_jsx(Button, { type: "button", disabled: currentPage === 1 || readOnly, onClickSave: () => setCurrentPage((old) => Math.max(old - 1, 1)), children: "Anterior" }), _jsx("span", { className: "mx-2", children: `Página ${currentPage} de ${totalPages()}` }), _jsx(Button, { type: "button", disabled: currentPage === totalPages() || readOnly, onClickSave: () => setCurrentPage((old) => Math.min(old + 1, totalPages())), children: "Siguiente" })] }), _jsx("div", { children: _jsx("select", { value: itemsPerPage, onChange: (e) => {
321
+ }), showRowActions && _jsx("td", {})] }) }))] }), paginated && totalPages() > 1 && (_jsxs("div", { className: "flex justify-between items-center mt-4", children: [_jsxs("div", { className: "flex items-center", children: [_jsx(Button, { type: "button", disabled: currentPage === 1 || readOnly, onClickSave: () => setCurrentPage((old) => Math.max(old - 1, 1)), children: "Anterior" }), _jsx("span", { className: "mx-2", children: `Página ${currentPage} de ${totalPages()}` }), _jsx(Button, { type: "button", disabled: currentPage === totalPages() || readOnly, onClickSave: () => setCurrentPage((old) => Math.min(old + 1, totalPages())), children: "Siguiente" })] }), _jsx("div", { children: _jsx("select", { value: itemsPerPage, onChange: (e) => {
321
322
  setItemsPerPage(Number(e.target.value));
322
323
  setCurrentPage(1); // resetear la página al cambiar los elementos por página
323
324
  }, children: itemsPerPageOptions.map((option) => (_jsxs("option", { value: option, children: [option, " elementos por p\u00E1gina"] }, option))) }) })] }))] })] }));
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useSearchParams } from "@remix-run/react";
3
- import { useEffect, useState } from "react";
3
+ import { useEffect, useId, useMemo, useState } from "react";
4
4
  import { createTheme } from "react-data-table-component";
5
5
  import { SearchSVG } from "@zauru-sdk/icons";
6
6
  import * as ReactDataTableComponent from "react-data-table-component";
@@ -160,6 +160,56 @@ createTheme("subTable", {
160
160
  background: "black",
161
161
  },
162
162
  });
163
+ export const normalizeZauruTableColumns = (columns) => {
164
+ if (!Array.isArray(columns)) {
165
+ return columns;
166
+ }
167
+ return columns.map((column, index) => {
168
+ if (!column || typeof column !== "object") {
169
+ return column;
170
+ }
171
+ const next = { wrap: true, ...column };
172
+ if (next.id == null) {
173
+ next.id = index + 1;
174
+ }
175
+ // RDT `width` sets both min-width and max-width, wiping a px floor.
176
+ // Keep % as flex-basis so minWidth/maxWidth/grow still apply.
177
+ if (typeof next.width === "string" && next.width.endsWith("%")) {
178
+ const pct = next.width;
179
+ delete next.width;
180
+ next.style = {
181
+ flexBasis: pct,
182
+ ...next.style,
183
+ };
184
+ }
185
+ return next;
186
+ });
187
+ };
188
+ export const zauruTableFlexBasisCss = (tableClassName, columns) => {
189
+ if (!tableClassName || !Array.isArray(columns)) {
190
+ return "";
191
+ }
192
+ const safeClass = tableClassName.replace(/[^a-zA-Z0-9_-]/g, "");
193
+ if (!safeClass) {
194
+ return "";
195
+ }
196
+ return columns
197
+ .flatMap((column) => {
198
+ const basis = column?.style?.flexBasis;
199
+ if (basis == null || column?.id == null) {
200
+ return [];
201
+ }
202
+ const id = String(column.id).replace(/[^a-zA-Z0-9_-]/g, "");
203
+ const safeBasis = String(basis).replace(/[^\d.%]/g, "");
204
+ if (!id || !safeBasis) {
205
+ return [];
206
+ }
207
+ return [
208
+ `.${safeClass} [data-column-id="${id}"] { flex-basis: ${safeBasis} !important; }`,
209
+ ];
210
+ })
211
+ .join("\n");
212
+ };
163
213
  //Documentación de la tabla https://react-data-table-component.netlify.app/?path=/docs/getting-started-intro--docs
164
214
  export const ZauruTable = (props) => {
165
215
  const { columns, conditionalRowStyles, data, loading = false, pagination, search, expandable, theme, className, offlineSearch = [], whitOutPagination = false, ...others } = props;
@@ -176,6 +226,9 @@ export const ZauruTable = (props) => {
176
226
  useEffect(() => {
177
227
  setFilteredData(data);
178
228
  }, [data]);
229
+ const tableInstanceClass = `zauru-table-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
230
+ const normalizedColumns = useMemo(() => normalizeZauruTableColumns(columns), [columns]);
231
+ const flexBasisCss = useMemo(() => zauruTableFlexBasisCss(tableInstanceClass, normalizedColumns), [tableInstanceClass, normalizedColumns]);
179
232
  const subHeaderComponentMemo = (_jsxs(_Fragment, { children: [_jsx("input", { name: "search", type: "text", placeholder: search?.placeholderSearch ?? "Filtrar", "aria-label": "Search Input", className: "bg-gray-50 border border-gray-300 text-gray-900 rounded-lg px-2", onChange: (event) => {
180
233
  const searchTerm = event.target.value;
181
234
  if (offlineSearch.length > 0) {
@@ -228,26 +281,7 @@ export const ZauruTable = (props) => {
228
281
  };
229
282
  const loadSubHeader = !!(search || offlineSearch.length > 0);
230
283
  const subHeaderComponent = loadSubHeader ? subHeaderComponentMemo : undefined;
231
- const normalizedColumns = Array.isArray(columns)
232
- ? columns.map((column) => {
233
- if (!column || typeof column !== "object") {
234
- return column;
235
- }
236
- const next = { wrap: true, ...column };
237
- // RDT `width` sets both min-width and max-width, wiping a px floor.
238
- // Keep % as flex-basis so minWidth/maxWidth/grow still apply.
239
- if (typeof next.width === "string" && next.width.endsWith("%")) {
240
- const pct = next.width;
241
- delete next.width;
242
- next.style = {
243
- flexBasis: pct,
244
- ...next.style,
245
- };
246
- }
247
- return next;
248
- })
249
- : columns;
250
- return (_jsx("div", { className: "w-full min-w-0 max-w-full overflow-x-auto", children: _jsx(DataTable, { className: className, subHeaderWrap: true, theme: theme ?? "solarized", columns: normalizedColumns, conditionalRowStyles: conditionalRowStyles, data: filteredData, customStyles: customStyles, progressPending: loading, highlightOnHover: true, pointerOnHover: true, dense: true, striped: true, pagination: !whitOutPagination, persistTableHead: true, responsive: true, noHeader: true, expandableRows: !!expandable, expandOnRowClicked: !!expandable, expandableRowExpanded: expandable ? expandable.expandableRowExpanded : undefined, expandableRowsComponent: expandable ? expandable.expandableRowsComponent : undefined, subHeader: loadSubHeader, subHeaderComponent: subHeaderComponent, paginationServer: !!pagination, paginationTotalRows: pagination?.totalRows ?? undefined, onChangeRowsPerPage: pagination ? handlePerRowsChange : undefined, onChangePage: pagination ? handlePageChange : undefined, paginationComponentOptions: paginationComponentOptions, paginationRowsPerPageOptions: pagination?.rowsPerPageOptions
251
- ? pagination.rowsPerPageOptions
252
- : [10, 50, 100], ...others }) }));
284
+ return (_jsxs("div", { className: `w-full min-w-0 max-w-full overflow-x-auto ${tableInstanceClass}`, children: [flexBasisCss ? _jsx("style", { children: flexBasisCss }) : null, _jsx(DataTable, { className: className, subHeaderWrap: true, theme: theme ?? "solarized", columns: normalizedColumns, conditionalRowStyles: conditionalRowStyles, data: filteredData, customStyles: customStyles, progressPending: loading, highlightOnHover: true, pointerOnHover: true, dense: true, striped: true, pagination: !whitOutPagination, persistTableHead: true, responsive: true, noHeader: true, expandableRows: !!expandable, expandOnRowClicked: !!expandable, expandableRowExpanded: expandable ? expandable.expandableRowExpanded : undefined, expandableRowsComponent: expandable ? expandable.expandableRowsComponent : undefined, subHeader: loadSubHeader, subHeaderComponent: subHeaderComponent, paginationServer: !!pagination, paginationTotalRows: pagination?.totalRows ?? undefined, onChangeRowsPerPage: pagination ? handlePerRowsChange : undefined, onChangePage: pagination ? handlePageChange : undefined, paginationComponentOptions: paginationComponentOptions, paginationRowsPerPageOptions: pagination?.rowsPerPageOptions
285
+ ? pagination.rowsPerPageOptions
286
+ : [10, 50, 100], ...others })] }));
253
287
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zauru-sdk/components",
3
- "version": "6.14.1",
3
+ "version": "6.15.0",
4
4
  "description": "Componentes reutilizables en las WebApps de Zauru.",
5
5
  "main": "./dist/esm/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -45,11 +45,11 @@
45
45
  "@remix-run/node": "^2.8.1",
46
46
  "@remix-run/react": "^2.8.1",
47
47
  "@zauru-sdk/common": "^6.12.0",
48
- "@zauru-sdk/hooks": "^6.3.23",
49
- "@zauru-sdk/icons": "^6.1.0",
50
- "@zauru-sdk/redux": "^6.7.2",
48
+ "@zauru-sdk/hooks": "^6.4.0",
49
+ "@zauru-sdk/icons": "^6.2.0",
50
+ "@zauru-sdk/redux": "^6.8.1",
51
51
  "@zauru-sdk/types": "^6.28.0",
52
- "@zauru-sdk/utils": "^6.26.0",
52
+ "@zauru-sdk/utils": "^6.27.0",
53
53
  "browser-image-compression": "^2.0.2",
54
54
  "framer-motion": "^12.0.0",
55
55
  "jsonwebtoken": "^9.0.2",
@@ -62,5 +62,5 @@
62
62
  "styled-components": "^5.3.5",
63
63
  "zod": "^3.23.8"
64
64
  },
65
- "gitHead": "2fda4a18e3f62d22c38fbb91052869db9ebc16f4"
65
+ "gitHead": "322f29f8eff1085aba8924ca290f3a04aa54d477"
66
66
  }
@@ -31,8 +31,13 @@ type Props = {
31
31
  footerRow?: FooterColumnConfig[];
32
32
  thCSSProperties?: React.CSSProperties;
33
33
  thElementsClassName?: string;
34
- /** Controla si se pueden o no editar los campos (oculta botones y/o deshabilita campos). */
34
+ /** Controla si se pueden editar los campos. */
35
35
  editable?: boolean;
36
+ /**
37
+ * Si es false, no muestra agregar/eliminar filas aunque `editable` esté activo.
38
+ * Útil para catálogos fijos cuyos valores sí se editan.
39
+ */
40
+ rowActions?: boolean;
36
41
  /** Opciones de búsqueda. */
37
42
  searcheables?: SelectFieldOption[];
38
43
  /** Activa o desactiva el “skeleton” de carga. */
@@ -169,6 +174,7 @@ export const GenericDynamicTable = (props: Props) => {
169
174
  thElementsClassName = "",
170
175
  editable = true,
171
176
  readOnly = false, // Nuevo prop
177
+ rowActions = true,
172
178
  searcheables = [],
173
179
  loading = false,
174
180
  paginated = true,
@@ -188,6 +194,7 @@ export const GenericDynamicTable = (props: Props) => {
188
194
  * efectivamente editables: solo si `editable` es true y `readOnly` es false.
189
195
  */
190
196
  const isEditable = editable && !readOnly;
197
+ const showRowActions = isEditable && rowActions;
191
198
 
192
199
  try {
193
200
  const [tableData, setTableData] = useState<RowDataType[]>(defaultValue);
@@ -276,7 +283,7 @@ export const GenericDynamicTable = (props: Props) => {
276
283
  {rendereableColumns.map((column, index) => {
277
284
  const ancho =
278
285
  column.width ??
279
- (isEditable ? 94 : 100) / (rendereableColumns.length ?? 1);
286
+ (showRowActions ? 94 : 100) / (rendereableColumns.length ?? 1);
280
287
  return (
281
288
  <th
282
289
  key={index}
@@ -289,7 +296,7 @@ export const GenericDynamicTable = (props: Props) => {
289
296
  </th>
290
297
  );
291
298
  })}
292
- {isEditable && <th style={{ width: "4%" }}></th>}
299
+ {showRowActions && <th style={{ width: "4%" }}></th>}
293
300
  </tr>
294
301
  );
295
302
  } else {
@@ -306,14 +313,14 @@ export const GenericDynamicTable = (props: Props) => {
306
313
  const rowClassName =
307
314
  index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "";
308
315
  const summaryColSpan =
309
- rendereableColumns.length + (isEditable ? 1 : 0);
316
+ rendereableColumns.length + (showRowActions ? 1 : 0);
310
317
  const summaryContent = rowSummary?.(rowData);
311
318
 
312
319
  return (
313
320
  <React.Fragment key={rowData.id}>
314
321
  <tr className={rowClassName}>
315
322
  {rendereableColumns.map((column) => renderCell(rowData, column))}
316
- {isEditable && renderDeleteButton(rowData)}
323
+ {showRowActions && renderDeleteButton(rowData)}
317
324
  </tr>
318
325
  {summaryContent ? (
319
326
  <tr className={rowClassName}>
@@ -345,7 +352,7 @@ export const GenericDynamicTable = (props: Props) => {
345
352
  {column.label}
346
353
  </th>
347
354
  {renderCell(rowData, column)}
348
- {isEditable &&
355
+ {showRowActions &&
349
356
  column === rendereableColumns[rendereableColumns.length - 1] &&
350
357
  renderDeleteButton(rowData)}
351
358
  </tr>
@@ -572,7 +579,7 @@ export const GenericDynamicTable = (props: Props) => {
572
579
  <table className="w-full">
573
580
  {orientation === "horizontal" && <thead>{renderHeader()}</thead>}
574
581
  <tbody>{renderRows()}</tbody>
575
- {isEditable && (
582
+ {showRowActions && (
576
583
  <tfoot>
577
584
  <tr>
578
585
  <td
@@ -633,7 +640,7 @@ export const GenericDynamicTable = (props: Props) => {
633
640
  </td>
634
641
  );
635
642
  })}
636
- {isEditable && <td></td>}
643
+ {showRowActions && <td></td>}
637
644
  </tr>
638
645
  </tfoot>
639
646
  )}
@@ -1,5 +1,5 @@
1
1
  import { useSearchParams } from "@remix-run/react";
2
- import { useEffect, useState } from "react";
2
+ import { useEffect, useId, useMemo, useState } from "react";
3
3
  import type {
4
4
  PaginationOptions,
5
5
  TableProps,
@@ -193,6 +193,61 @@ type Props = TableProps<any> & {
193
193
  className?: string;
194
194
  };
195
195
 
196
+ export const normalizeZauruTableColumns = (columns: any): any => {
197
+ if (!Array.isArray(columns)) {
198
+ return columns;
199
+ }
200
+ return columns.map((column: any, index: number) => {
201
+ if (!column || typeof column !== "object") {
202
+ return column;
203
+ }
204
+ const next = { wrap: true, ...column };
205
+ if (next.id == null) {
206
+ next.id = index + 1;
207
+ }
208
+ // RDT `width` sets both min-width and max-width, wiping a px floor.
209
+ // Keep % as flex-basis so minWidth/maxWidth/grow still apply.
210
+ if (typeof next.width === "string" && next.width.endsWith("%")) {
211
+ const pct = next.width;
212
+ delete next.width;
213
+ next.style = {
214
+ flexBasis: pct,
215
+ ...next.style,
216
+ };
217
+ }
218
+ return next;
219
+ });
220
+ };
221
+
222
+ export const zauruTableFlexBasisCss = (
223
+ tableClassName: string,
224
+ columns: any
225
+ ): string => {
226
+ if (!tableClassName || !Array.isArray(columns)) {
227
+ return "";
228
+ }
229
+ const safeClass = tableClassName.replace(/[^a-zA-Z0-9_-]/g, "");
230
+ if (!safeClass) {
231
+ return "";
232
+ }
233
+ return columns
234
+ .flatMap((column: any) => {
235
+ const basis = column?.style?.flexBasis;
236
+ if (basis == null || column?.id == null) {
237
+ return [];
238
+ }
239
+ const id = String(column.id).replace(/[^a-zA-Z0-9_-]/g, "");
240
+ const safeBasis = String(basis).replace(/[^\d.%]/g, "");
241
+ if (!id || !safeBasis) {
242
+ return [];
243
+ }
244
+ return [
245
+ `.${safeClass} [data-column-id="${id}"] { flex-basis: ${safeBasis} !important; }`,
246
+ ];
247
+ })
248
+ .join("\n");
249
+ };
250
+
196
251
  //Documentación de la tabla https://react-data-table-component.netlify.app/?path=/docs/getting-started-intro--docs
197
252
  export const ZauruTable = (props: Props) => {
198
253
  const {
@@ -227,6 +282,16 @@ export const ZauruTable = (props: Props) => {
227
282
  setFilteredData(data);
228
283
  }, [data]);
229
284
 
285
+ const tableInstanceClass = `zauru-table-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
286
+ const normalizedColumns = useMemo(
287
+ () => normalizeZauruTableColumns(columns),
288
+ [columns]
289
+ );
290
+ const flexBasisCss = useMemo(
291
+ () => zauruTableFlexBasisCss(tableInstanceClass, normalizedColumns),
292
+ [tableInstanceClass, normalizedColumns]
293
+ );
294
+
230
295
  const subHeaderComponentMemo = (
231
296
  <>
232
297
  <input
@@ -307,28 +372,10 @@ export const ZauruTable = (props: Props) => {
307
372
 
308
373
  const loadSubHeader = !!(search || offlineSearch.length > 0);
309
374
  const subHeaderComponent = loadSubHeader ? subHeaderComponentMemo : undefined;
310
- const normalizedColumns = Array.isArray(columns)
311
- ? columns.map((column: any) => {
312
- if (!column || typeof column !== "object") {
313
- return column;
314
- }
315
- const next = { wrap: true, ...column };
316
- // RDT `width` sets both min-width and max-width, wiping a px floor.
317
- // Keep % as flex-basis so minWidth/maxWidth/grow still apply.
318
- if (typeof next.width === "string" && next.width.endsWith("%")) {
319
- const pct = next.width;
320
- delete next.width;
321
- next.style = {
322
- flexBasis: pct,
323
- ...next.style,
324
- };
325
- }
326
- return next;
327
- })
328
- : columns;
329
375
 
330
376
  return (
331
- <div className="w-full min-w-0 max-w-full overflow-x-auto">
377
+ <div className={`w-full min-w-0 max-w-full overflow-x-auto ${tableInstanceClass}`}>
378
+ {flexBasisCss ? <style>{flexBasisCss}</style> : null}
332
379
  <DataTable
333
380
  className={className}
334
381
  subHeaderWrap