@stackcraftmx/calcita 0.57.0 → 0.58.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.
@@ -0,0 +1,230 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { useId, useState, useRef } from "react";
4
+ import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
5
+ import Box from "@mui/material/Box";
6
+ import FormHelperText from "@mui/material/FormHelperText";
7
+ import Paper from "@mui/material/Paper";
8
+ import Table from "@mui/material/Table";
9
+ import TableBody from "@mui/material/TableBody";
10
+ import TableCell from "@mui/material/TableCell";
11
+ import TableContainer from "@mui/material/TableContainer";
12
+ import TableHead from "@mui/material/TableHead";
13
+ import TableRow from "@mui/material/TableRow";
14
+ import DragIndicatorRoundedIcon from "@mui/icons-material/DragIndicatorRounded";
15
+ import { moveRow } from "./sortableRows.js";
16
+ import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
17
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
18
+ import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
19
+ const THEME_NAME = "CalcitaSortableTable";
20
+ const SortableRoot = piecePart(THEME_NAME, "Root", Box);
21
+ const SortableContainer = piecePart(THEME_NAME, "Container", TableContainer);
22
+ const SortableTableElement = piecePart(THEME_NAME, "Table", Table);
23
+ const SortableHead = piecePart(THEME_NAME, "Head", TableHead);
24
+ const SortableHeaderRow = piecePart(THEME_NAME, "HeaderRow", TableRow);
25
+ const SortableHeaderCell = piecePart(THEME_NAME, "HeaderCell", TableCell);
26
+ const SortableBody = piecePart(THEME_NAME, "Body", TableBody);
27
+ const SortableRow = piecePart(THEME_NAME, "Row", TableRow);
28
+ const SortableHandleCell = piecePart(THEME_NAME, "HandleCell", TableCell);
29
+ const SortableHandle = piecePart(THEME_NAME, "Handle", Box);
30
+ const SortablePositionCell = piecePart(THEME_NAME, "PositionCell", TableCell);
31
+ const SortableCell = piecePart(THEME_NAME, "Cell", TableCell);
32
+ const SortableEmpty = piecePart(THEME_NAME, "Empty", TableCell);
33
+ const SortableHelperText = piecePart(THEME_NAME, "HelperText", FormHelperText);
34
+ const SortableAnnouncer = piecePart(THEME_NAME, "Announcer", Box);
35
+ const HANDLE_COLUMN_WIDTH_PIXELS = 48;
36
+ const POSITION_COLUMN_WIDTH_PIXELS = 88;
37
+ const HOUSE_COLUMN_PADDING_X = 1.5;
38
+ const DEFAULT_COLUMN_MIN_WIDTH_PIXELS = 96;
39
+ const VISUALLY_HIDDEN_SX = { position: "absolute", width: "1px", height: "1px", padding: 0, margin: "-1px", overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap", border: 0 };
40
+ const JUSTIFY_BY_ALIGN = Object.freeze({ left: "flex-start", center: "center", right: "flex-end" });
41
+ function trackOf(column) {
42
+ if (column.width) {
43
+ return `${column.width}px`;
44
+ }
45
+ return `minmax(${column.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH_PIXELS}px, ${column.flex ?? 1}fr)`;
46
+ }
47
+ function isPromiseLike(value) {
48
+ return typeof value?.then === "function";
49
+ }
50
+ function SortableTableContent(props) {
51
+ const {
52
+ rows,
53
+ columns,
54
+ onReorder,
55
+ ariaLabel,
56
+ getRowId = (row) => row.id,
57
+ getRowLabel,
58
+ showPosition = true,
59
+ disabled = false,
60
+ error,
61
+ helperText,
62
+ sx,
63
+ texts,
64
+ ...unknownProps
65
+ } = usePieceProps(props, THEME_NAME);
66
+ warnUnknownProps(THEME_NAME, unknownProps);
67
+ const { t } = useToolkitTranslation("sortableTable", texts);
68
+ const helperId = useId();
69
+ const listId = useId();
70
+ const [pending, setPending] = useState(null);
71
+ const [notice, setNotice] = useState("");
72
+ const handlePropsByRow = useRef(/* @__PURE__ */ new Map());
73
+ const shownRows = pending && pending.basedOn === rows ? pending.rows : rows;
74
+ const isSaving = Boolean(pending && pending.basedOn === rows && pending.saving);
75
+ const canDrag = !disabled && !isSaving;
76
+ const total = shownRows.length;
77
+ const labelOf = (row) => String(getRowLabel ? getRowLabel(row) : row[columns[0]?.field] ?? getRowId(row));
78
+ const rowOfDraggable = (draggableId) => shownRows.find((row) => String(getRowId(row)) === draggableId);
79
+ const titleOf = (draggableId) => {
80
+ const row = rowOfDraggable(draggableId);
81
+ return row ? labelOf(row) : draggableId;
82
+ };
83
+ const handlePropsOf = (rowId, dragHandleProps) => {
84
+ if (dragHandleProps) {
85
+ handlePropsByRow.current.set(rowId, dragHandleProps);
86
+ return dragHandleProps;
87
+ }
88
+ return handlePropsByRow.current.get(rowId) ?? { tabIndex: 0, role: "button" };
89
+ };
90
+ const reorder = (row, fromIndex, toIndex) => {
91
+ const movedRows = moveRow(shownRows, fromIndex, toIndex);
92
+ const outcome = onReorder({ rows: movedRows, rowId: getRowId(row), fromIndex, toIndex });
93
+ if (!isPromiseLike(outcome)) {
94
+ return;
95
+ }
96
+ const attempt = { rows: movedRows, basedOn: rows, saving: true };
97
+ setPending(attempt);
98
+ outcome.then(
99
+ () => setPending((current) => current === attempt ? { ...attempt, saving: false } : current),
100
+ () => {
101
+ setPending((current) => current === attempt ? null : current);
102
+ setNotice(t("reverted", { title: labelOf(row), position: fromIndex + 1 }));
103
+ }
104
+ );
105
+ };
106
+ const announceStart = (start, provided) => {
107
+ setNotice("");
108
+ provided.announce(t("lifted", { title: titleOf(start.draggableId), position: start.source.index + 1, total }));
109
+ };
110
+ const announceUpdate = (update, provided) => {
111
+ if (update.destination) {
112
+ provided.announce(t("moving", { position: update.destination.index + 1, total }));
113
+ }
114
+ };
115
+ const finishMove = (result, provided) => {
116
+ const { draggableId, source, destination } = result;
117
+ const row = rowOfDraggable(draggableId);
118
+ if (!row || !destination || destination.index === source.index) {
119
+ provided.announce(t("cancelled", { title: titleOf(draggableId), position: source.index + 1 }));
120
+ return;
121
+ }
122
+ provided.announce(t("dropped", { title: labelOf(row), position: destination.index + 1, total }));
123
+ reorder(row, source.index, destination.index);
124
+ };
125
+ const gridTemplateColumns = [
126
+ `${HANDLE_COLUMN_WIDTH_PIXELS}px`,
127
+ ...showPosition ? [`${POSITION_COLUMN_WIDTH_PIXELS}px`] : [],
128
+ ...columns.map(trackOf)
129
+ ].join(" ");
130
+ const minimumWidth = HANDLE_COLUMN_WIDTH_PIXELS + (showPosition ? POSITION_COLUMN_WIDTH_PIXELS : 0) + columns.reduce((sum, column) => sum + (column.width ?? column.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH_PIXELS), 0);
131
+ const rowSx = { display: "grid", gridTemplateColumns };
132
+ const cellSx = (align = "left") => ({ display: "flex", alignItems: "center", minWidth: 0, justifyContent: JUSTIFY_BY_ALIGN[align], textAlign: align, overflowWrap: "break-word" });
133
+ const houseCellSx = (align) => ({ ...cellSx(align), px: HOUSE_COLUMN_PADDING_X });
134
+ const bottomText = error || helperText;
135
+ return /* @__PURE__ */ jsxs(SortableRoot, { sx, children: [
136
+ /* @__PURE__ */ jsx(SortableContainer, { component: Paper, variant: "outlined", ownSx: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs(
137
+ SortableTableElement,
138
+ {
139
+ component: "div",
140
+ "aria-label": ariaLabel,
141
+ "aria-busy": isSaving || void 0,
142
+ "aria-describedby": bottomText ? helperId : void 0,
143
+ ownSx: { display: "block", minWidth: minimumWidth },
144
+ children: [
145
+ /* @__PURE__ */ jsx(SortableHead, { component: "div", ownSx: { display: "block" }, children: /* @__PURE__ */ jsxs(SortableHeaderRow, { component: "div", ownSx: rowSx, children: [
146
+ /* @__PURE__ */ jsx(SortableHeaderCell, { component: "div", role: "columnheader", ownSx: houseCellSx("center"), children: /* @__PURE__ */ jsx(Box, { component: "span", sx: VISUALLY_HIDDEN_SX, children: t("handleHeader") }) }),
147
+ showPosition && /* @__PURE__ */ jsx(SortableHeaderCell, { component: "div", role: "columnheader", ownSx: houseCellSx("right"), children: t("position") }),
148
+ columns.map((column) => /* @__PURE__ */ jsx(SortableHeaderCell, { component: "div", role: "columnheader", ownSx: cellSx(column.align), children: column.headerName }, column.field))
149
+ ] }) }),
150
+ /* @__PURE__ */ jsx(
151
+ DragDropContext,
152
+ {
153
+ onDragStart: announceStart,
154
+ onDragUpdate: announceUpdate,
155
+ onDragEnd: finishMove,
156
+ dragHandleUsageInstructions: t("instructions"),
157
+ children: /* @__PURE__ */ jsx(Droppable, { droppableId: listId, children: (droppableProvided) => /* @__PURE__ */ jsxs(
158
+ SortableBody,
159
+ {
160
+ component: "div",
161
+ ref: droppableProvided.innerRef,
162
+ ...droppableProvided.droppableProps,
163
+ ownSx: { display: "block", "& > .MuiTableRow-root:last-of-type > .MuiTableCell-root": { borderBottom: 0 } },
164
+ children: [
165
+ shownRows.map((row, index) => {
166
+ const rowId = String(getRowId(row));
167
+ const label = labelOf(row);
168
+ return /* @__PURE__ */ jsx(Draggable, { draggableId: rowId, index, isDragDisabled: !canDrag, children: (draggableProvided, draggableSnapshot) => /* @__PURE__ */ jsxs(
169
+ SortableRow,
170
+ {
171
+ component: "div",
172
+ ref: draggableProvided.innerRef,
173
+ ...draggableProvided.draggableProps,
174
+ ownSx: {
175
+ ...rowSx,
176
+ // el que flota lleva fondo y sombra: sin eso se transparenta sobre los demás
177
+ ...draggableSnapshot.isDragging && { bgcolor: "background.paper", boxShadow: 8, borderRadius: 1 }
178
+ },
179
+ children: [
180
+ /* @__PURE__ */ jsx(SortableHandleCell, { component: "div", role: "cell", ownSx: houseCellSx("center"), children: /* @__PURE__ */ jsx(
181
+ SortableHandle,
182
+ {
183
+ component: "span",
184
+ ...handlePropsOf(rowId, draggableProvided.dragHandleProps),
185
+ "aria-label": t("handle", { title: label, position: index + 1 }),
186
+ "aria-disabled": !canDrag || void 0,
187
+ ownSx: {
188
+ display: "inline-flex",
189
+ p: 0.5,
190
+ borderRadius: 1,
191
+ color: canDrag ? "text.secondary" : "text.disabled",
192
+ cursor: canDrag ? draggableSnapshot.isDragging ? "grabbing" : "grab" : "default",
193
+ "&:focus-visible": { outline: 2, outlineColor: "primary.main", outlineOffset: 1 }
194
+ },
195
+ children: /* @__PURE__ */ jsx(DragIndicatorRoundedIcon, { fontSize: "small" })
196
+ }
197
+ ) }),
198
+ showPosition && /* @__PURE__ */ jsx(
199
+ SortablePositionCell,
200
+ {
201
+ component: "div",
202
+ role: "cell",
203
+ ownSx: { ...houseCellSx("right"), fontVariantNumeric: "tabular-nums", color: "text.secondary" },
204
+ children: index + 1
205
+ }
206
+ ),
207
+ columns.map((column) => /* @__PURE__ */ jsx(SortableCell, { component: "div", role: "cell", ownSx: cellSx(column.align), children: column.renderCell ? column.renderCell(row, { index, position: index + 1 }) : row[column.field] ?? t("noValue") }, column.field))
208
+ ]
209
+ }
210
+ ) }, rowId);
211
+ }),
212
+ droppableProvided.placeholder,
213
+ total === 0 && /* @__PURE__ */ jsx(TableRow, { component: "div", sx: rowSx, children: /* @__PURE__ */ jsx(SortableEmpty, { component: "div", role: "cell", ownSx: { gridColumn: "1 / -1", color: "text.secondary", textAlign: "center", py: 3 }, children: t("empty") }) })
214
+ ]
215
+ }
216
+ ) })
217
+ }
218
+ )
219
+ ]
220
+ }
221
+ ) }),
222
+ bottomText && /* @__PURE__ */ jsx(SortableHelperText, { id: helperId, error: Boolean(error), children: bottomText }),
223
+ /* @__PURE__ */ jsx(SortableAnnouncer, { role: "status", "aria-live": "polite", ownSx: VISUALLY_HIDDEN_SX, children: notice })
224
+ ] });
225
+ }
226
+ const SortableTable = withPieceSlots(SortableTableContent, THEME_NAME);
227
+ export {
228
+ SortableTable as default
229
+ };
230
+ //# sourceMappingURL=SortableTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SortableTable.js","sources":["../../src/sortable/SortableTable.jsx"],"sourcesContent":["// Una tabla chica donde el orden de los renglones es el dato (@stackcraftmx/calcita/sortable): qué se hace primero, en qué\n// orden pasa la ruta. Los renglones se arrastran con el ratón, con el dedo o con el teclado (espacio para levantar, flechas\n// para mover, espacio para soltar), y la columna «Orden» se renumera 1..n. Con @hello-pangea/dnd por dentro, la misma del\n// tablero kanban.\n//\n// <SortableTable rows={stops} columns={[{field: 'name', headerName: 'Sucursal', flex: 1}]} ariaLabel=\"Ruta de reparto\"\n// onReorder={({rows}) => setStops(rows)} />\n//\n// Dos formas de guardar, según lo que regrese onReorder:\n// - nada (en un formulario: se guarda al final con su botón): la pantalla pone los renglones nuevos (rows) y la tabla los\n// enseña. FormSortableField lo hace con formik y marca el formulario como cambiado;\n// - una promesa (se guarda al soltar): la tabla enseña el orden nuevo al instante y, si la promesa truena (el servidor dijo\n// que no), el renglón regresa a donde estaba y el lector de pantalla lo anuncia; mientras se guarda no se arrastra otro.\n// Al cumplirse, la pantalla pone los renglones nuevos o los vuelve a pedir.\n//\n// Para lo que no va en una tabla de servidor: la tabla de MUI X gratis no ordena renglones arrastrando (es de su versión de\n// paga), así que esta lista va completa en el navegador, sin páginas.\n//\n// OJO trampa: un renglón que se arrastra flota (position fixed) y en una tabla de HTML pierde el ancho de sus columnas. Aquí\n// la tabla de MUI se pinta con divisiones (role table, row y cell) y cada renglón es una cuadrícula con las mismas columnas:\n// el que flota conserva su ancho. El estilo del tema del proyecto para MuiTableCell se le aplica igual.\n//\n// Mover sin arrastrar (un botón «Subir» en las acciones de la pantalla): moveRow de sortableRows.js.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSortableTable con defaultProps y\n// styleOverrides de sus partes: root, container, table, head, headerRow, headerCell, body, row, handleCell, handle, positionCell,\n// cell, empty, helperText, announcer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useRef, useState} from 'react';\nimport {DragDropContext, Draggable, Droppable} from '@hello-pangea/dnd';\nimport Box from '@mui/material/Box';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Paper from '@mui/material/Paper';\nimport Table from '@mui/material/Table';\nimport TableBody from '@mui/material/TableBody';\nimport TableCell from '@mui/material/TableCell';\nimport TableContainer from '@mui/material/TableContainer';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport DragIndicatorRoundedIcon from '@mui/icons-material/DragIndicatorRounded';\n\nimport {moveRow} from '@/sortable/sortableRows.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\nconst THEME_NAME = 'CalcitaSortableTable';\nconst SortableRoot = piecePart(THEME_NAME, 'Root', Box);\nconst SortableContainer = piecePart(THEME_NAME, 'Container', TableContainer);\nconst SortableTableElement = piecePart(THEME_NAME, 'Table', Table);\nconst SortableHead = piecePart(THEME_NAME, 'Head', TableHead);\nconst SortableHeaderRow = piecePart(THEME_NAME, 'HeaderRow', TableRow);\nconst SortableHeaderCell = piecePart(THEME_NAME, 'HeaderCell', TableCell);\nconst SortableBody = piecePart(THEME_NAME, 'Body', TableBody);\nconst SortableRow = piecePart(THEME_NAME, 'Row', TableRow);\nconst SortableHandleCell = piecePart(THEME_NAME, 'HandleCell', TableCell);\nconst SortableHandle = piecePart(THEME_NAME, 'Handle', Box);\nconst SortablePositionCell = piecePart(THEME_NAME, 'PositionCell', TableCell);\nconst SortableCell = piecePart(THEME_NAME, 'Cell', TableCell);\nconst SortableEmpty = piecePart(THEME_NAME, 'Empty', TableCell);\nconst SortableHelperText = piecePart(THEME_NAME, 'HelperText', FormHelperText);\nconst SortableAnnouncer = piecePart(THEME_NAME, 'Announcer', Box);\n\n// las columnas de la casa, con menos relleno a los lados que las demás: la del asa (el ícono y su foco) y la del orden\n// (OJO: con 72 px y el relleno de siempre, «Orden» se partía en «Orde / n»; con 88 caben «Orden», «Order» o «Prioridad»)\nconst HANDLE_COLUMN_WIDTH_PIXELS = 48;\nconst POSITION_COLUMN_WIDTH_PIXELS = 88;\nconst HOUSE_COLUMN_PADDING_X = 1.5;\n// lo mínimo de una columna sin ancho fijo: abajo de eso la tabla se desliza de lado (en el teléfono)\nconst DEFAULT_COLUMN_MIN_WIDTH_PIXELS = 96;\n// lo que se ve, solo para el lector de pantalla\nconst VISUALLY_HIDDEN_SX = {position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap', border: 0};\nconst JUSTIFY_BY_ALIGN = Object.freeze({left: 'flex-start', center: 'center', right: 'flex-end'});\n\n/**\n * Una columna de la tabla. Row: el renglón de la pantalla (llega tal cual a renderCell).\n *\n * @template Row\n * @typedef {object} SortableTableColumn\n * @property {string} field la llave del renglón que se pinta (y la del encabezado)\n * @property {string} headerName el encabezado, ya traducido\n * @property {number} [width] ancho fijo en px\n * @property {number} [flex] cuánto del espacio libre toma, como en la tabla de servidor (por omisión 1)\n * @property {number} [minWidth] el mínimo en px de una columna sin ancho fijo (por omisión 96)\n * @property {'left'|'center'|'right'} [align] los números a la derecha\n * @property {(row: Row, place: {index: number, position: number}) => import('react').ReactNode} [renderCell] lo de la celda a la\n * medida; position es la que ve la persona (desde 1)\n */\n\n/** La pista de la cuadrícula de una columna: el ancho fijo, o lo que le toque del espacio libre sin bajar de su mínimo. */\nfunction trackOf(column) {\n if (column.width) {\n return `${column.width}px`;\n }\n return `minmax(${column.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH_PIXELS}px, ${column.flex ?? 1}fr)`;\n}\n\n/** Una función que regresa una promesa: se guarda al soltar. */\nfunction isPromiseLike(value) {\n return typeof value?.then === 'function';\n}\n\n/**\n * Los renglones conservan el tipo que usa la pantalla (Row): onReorder los regresa del mismo tipo, ya en su orden nuevo.\n *\n * @template {object} Row\n * @param {object} props\n * @param {Row[]} props.rows en su orden\n * @param {SortableTableColumn<Row>[]} props.columns\n * @param {(change: {rows: Row[], rowId: string|number, fromIndex: number, toIndex: number}) => Promise<unknown>|void} props.onReorder\n * al soltar un renglón en otro lugar: rows ya en el orden nuevo; fromIndex y toIndex desde 0. Si regresa una promesa, el\n * orden nuevo se enseña mientras se cumple y regresa si truena\n * @param {string} props.ariaLabel qué es la tabla, ya traducido\n * @param {(row: Row) => string|number} [props.getRowId] por omisión row.id\n * @param {(row: Row) => string} [props.getRowLabel] el nombre del renglón que oye el lector de pantalla («Polanco»); por omisión\n * lo de la primera columna\n * @param {boolean} [props.showPosition] la columna «Orden» con 1..n (por omisión sí)\n * @param {boolean} [props.disabled] nada se arrastra (mientras se envía el formulario, o de solo lectura)\n * @param {import('react').ReactNode} [props.error] el error de la lista (en rojo, abajo de la tabla)\n * @param {import('react').ReactNode} [props.helperText] una ayuda abajo de la tabla (el error gana)\n * @param {import('@mui/material').SxProps<import('@mui/material').Theme>} [props.sx] lo de afuera de la tabla\n * @param {object} [props.texts] los textos que cambian, con la forma de `sortableTable` en los textos del paquete\n */\nfunction SortableTableContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSortableTable.defaultProps) y encima lo que llega aquí\n const {\n rows, columns, onReorder, ariaLabel, getRowId = (row) => row.id, getRowLabel, showPosition = true, disabled = false, error,\n helperText, sx, texts, ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('sortableTable', texts);\n const helperId = useId();\n const listId = useId();\n // un orden que se está guardando (o ya se guardó y la pantalla todavía no da los renglones nuevos); vale mientras rows\n // sea el mismo arreglo sobre el que se movió\n const [pending, setPending] = useState(null);\n // lo último que se anunció fuera del arrastre (el renglón que regresó)\n const [notice, setNotice] = useState('');\n // el asa que la librería le dio a cada renglón la última vez que se podía arrastrar (ver la trampa en el asa)\n const handlePropsByRow = useRef(new Map());\n const shownRows = pending && pending.basedOn === rows ? pending.rows : rows;\n const isSaving = Boolean(pending && pending.basedOn === rows && pending.saving);\n const canDrag = !disabled && !isSaving;\n const total = shownRows.length;\n\n const labelOf = (row) => String(getRowLabel ? getRowLabel(row) : (row[columns[0]?.field] ?? getRowId(row)));\n const rowOfDraggable = (draggableId) => shownRows.find((row) => String(getRowId(row)) === draggableId);\n const titleOf = (draggableId) => {\n const row = rowOfDraggable(draggableId);\n return row ? labelOf(row) : draggableId;\n };\n\n // OJO trampa pagada: sin poder arrastrar (mientras se guarda) la librería no da el asa. Sin ella, quien soltó con el\n // teclado se quedaba sin foco, y la librería (que busca el asa para devolverle el foco al soltar) avisaba en la consola\n // que no la encontraba. Mientras tanto se queda con la que tuvo la última vez: con el renglón apagado no arranca otro\n // arrastre (la librería lo revisa en su registro, no en el asa). Sin una de antes (apagada desde el principio), solo\n // recibe el foco.\n const handlePropsOf = (rowId, dragHandleProps) => {\n if (dragHandleProps) {\n handlePropsByRow.current.set(rowId, dragHandleProps);\n return dragHandleProps;\n }\n return handlePropsByRow.current.get(rowId) ?? {tabIndex: 0, role: 'button'};\n };\n\n const reorder = (row, fromIndex, toIndex) => {\n const movedRows = moveRow(shownRows, fromIndex, toIndex);\n const outcome = onReorder({rows: movedRows, rowId: getRowId(row), fromIndex, toIndex});\n // sin promesa: la pantalla ya puso sus renglones (un formulario); nada que esperar\n if (!isPromiseLike(outcome)) {\n return;\n }\n const attempt = {rows: movedRows, basedOn: rows, saving: true};\n setPending(attempt);\n outcome.then(\n () => setPending((current) => (current === attempt ? {...attempt, saving: false} : current)),\n () => {\n // el servidor dijo que no: se enseñan otra vez los renglones de la pantalla (el orden de antes)\n setPending((current) => (current === attempt ? null : current));\n setNotice(t('reverted', {title: labelOf(row), position: fromIndex + 1}));\n },\n );\n };\n\n // lo que oye quien usa lector de pantalla (posiciones desde 1, como se cuentan)\n const announceStart = (start, provided) => {\n setNotice('');\n provided.announce(t('lifted', {title: titleOf(start.draggableId), position: start.source.index + 1, total}));\n };\n const announceUpdate = (update, provided) => {\n if (update.destination) {\n provided.announce(t('moving', {position: update.destination.index + 1, total}));\n }\n };\n const finishMove = (result, provided) => {\n const {draggableId, source, destination} = result;\n const row = rowOfDraggable(draggableId);\n // se soltó fuera de la tabla o en el mismo lugar: nada que mover\n if (!row || !destination || destination.index === source.index) {\n provided.announce(t('cancelled', {title: titleOf(draggableId), position: source.index + 1}));\n return;\n }\n provided.announce(t('dropped', {title: labelOf(row), position: destination.index + 1, total}));\n reorder(row, source.index, destination.index);\n };\n\n // todas las filas con las mismas columnas: la del asa, la del orden y las de la pantalla\n const gridTemplateColumns = [\n `${HANDLE_COLUMN_WIDTH_PIXELS}px`, ...(showPosition ? [`${POSITION_COLUMN_WIDTH_PIXELS}px`] : []), ...columns.map(trackOf),\n ].join(' ');\n // abajo de esto la tabla se desliza de lado en vez de apretar las columnas\n const minimumWidth = HANDLE_COLUMN_WIDTH_PIXELS + (showPosition ? POSITION_COLUMN_WIDTH_PIXELS : 0)\n + columns.reduce((sum, column) => sum + (column.width ?? column.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH_PIXELS), 0);\n const rowSx = {display: 'grid', gridTemplateColumns};\n // una palabra solo se parte si no cabe ni sola en su renglón (un correo largo); un encabezado de dos palabras baja entero\n const cellSx = (align = 'left') => ({display: 'flex', alignItems: 'center', minWidth: 0, justifyContent: JUSTIFY_BY_ALIGN[align], textAlign: align, overflowWrap: 'break-word'});\n const houseCellSx = (align) => ({...cellSx(align), px: HOUSE_COLUMN_PADDING_X});\n const bottomText = error || helperText;\n\n return (\n <SortableRoot sx={sx}>\n <SortableContainer component={Paper} variant=\"outlined\" ownSx={{overflowX: 'auto'}}>\n <SortableTableElement component=\"div\" aria-label={ariaLabel} aria-busy={isSaving || undefined}\n aria-describedby={bottomText ? helperId : undefined} ownSx={{display: 'block', minWidth: minimumWidth}}>\n <SortableHead component=\"div\" ownSx={{display: 'block'}}>\n <SortableHeaderRow component=\"div\" ownSx={rowSx}>\n <SortableHeaderCell component=\"div\" role=\"columnheader\" ownSx={houseCellSx('center')}>\n <Box component=\"span\" sx={VISUALLY_HIDDEN_SX}>{t('handleHeader')}</Box>\n </SortableHeaderCell>\n {showPosition && (\n <SortableHeaderCell component=\"div\" role=\"columnheader\" ownSx={houseCellSx('right')}>{t('position')}</SortableHeaderCell>\n )}\n {columns.map((column) => (\n <SortableHeaderCell key={column.field} component=\"div\" role=\"columnheader\" ownSx={cellSx(column.align)}>\n {column.headerName}\n </SortableHeaderCell>\n ))}\n </SortableHeaderRow>\n </SortableHead>\n <DragDropContext onDragStart={announceStart} onDragUpdate={announceUpdate} onDragEnd={finishMove}\n dragHandleUsageInstructions={t('instructions')}>\n <Droppable droppableId={listId}>\n {(droppableProvided) => (\n <SortableBody component=\"div\" ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}\n ownSx={{display: 'block', '& > .MuiTableRow-root:last-of-type > .MuiTableCell-root': {borderBottom: 0}}}>\n {shownRows.map((row, index) => {\n const rowId = String(getRowId(row));\n const label = labelOf(row);\n return (\n <Draggable key={rowId} draggableId={rowId} index={index} isDragDisabled={!canDrag}>\n {(draggableProvided, draggableSnapshot) => (\n <SortableRow component=\"div\" ref={draggableProvided.innerRef} {...draggableProvided.draggableProps}\n ownSx={{\n ...rowSx,\n // el que flota lleva fondo y sombra: sin eso se transparenta sobre los demás\n ...(draggableSnapshot.isDragging && {bgcolor: 'background.paper', boxShadow: 8, borderRadius: 1}),\n }}>\n <SortableHandleCell component=\"div\" role=\"cell\" ownSx={houseCellSx('center')}>\n <SortableHandle component=\"span\" {...handlePropsOf(rowId, draggableProvided.dragHandleProps)}\n aria-label={t('handle', {title: label, position: index + 1})}\n aria-disabled={!canDrag || undefined}\n ownSx={{\n display: 'inline-flex', p: 0.5, borderRadius: 1,\n color: canDrag ? 'text.secondary' : 'text.disabled',\n cursor: canDrag ? (draggableSnapshot.isDragging ? 'grabbing' : 'grab') : 'default',\n '&:focus-visible': {outline: 2, outlineColor: 'primary.main', outlineOffset: 1},\n }}>\n <DragIndicatorRoundedIcon fontSize=\"small\" />\n </SortableHandle>\n </SortableHandleCell>\n {showPosition && (\n <SortablePositionCell component=\"div\" role=\"cell\"\n ownSx={{...houseCellSx('right'), fontVariantNumeric: 'tabular-nums', color: 'text.secondary'}}>\n {index + 1}\n </SortablePositionCell>\n )}\n {columns.map((column) => (\n <SortableCell key={column.field} component=\"div\" role=\"cell\" ownSx={cellSx(column.align)}>\n {column.renderCell ? column.renderCell(row, {index, position: index + 1}) : (row[column.field] ?? t('noValue'))}\n </SortableCell>\n ))}\n </SortableRow>\n )}\n </Draggable>\n );\n })}\n {droppableProvided.placeholder}\n {total === 0 && (\n <TableRow component=\"div\" sx={rowSx}>\n <SortableEmpty component=\"div\" role=\"cell\" ownSx={{gridColumn: '1 / -1', color: 'text.secondary', textAlign: 'center', py: 3}}>\n {t('empty')}\n </SortableEmpty>\n </TableRow>\n )}\n </SortableBody>\n )}\n </Droppable>\n </DragDropContext>\n </SortableTableElement>\n </SortableContainer>\n {bottomText && <SortableHelperText id={helperId} error={Boolean(error)}>{bottomText}</SortableHelperText>}\n <SortableAnnouncer role=\"status\" aria-live=\"polite\" ownSx={VISUALLY_HIDDEN_SX}>{notice}</SortableAnnouncer>\n </SortableRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\n// OJO: con su tipo propio, el genérico (Row: el renglón de la pantalla) que la envoltura borraría; slots y slotProps van en su\n// documentación\n/** @type {typeof SortableTableContent} */\nconst SortableTable = withPieceSlots(SortableTableContent, THEME_NAME);\nexport default SortableTable;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ function moveRow(rows, fromIndex, toIndex) {
2
+ const next = [...rows];
3
+ const isInside = (index) => Number.isInteger(index) && index >= 0 && index < rows.length;
4
+ if (!isInside(fromIndex) || !isInside(toIndex) || fromIndex === toIndex) {
5
+ return next;
6
+ }
7
+ const [moved] = next.splice(fromIndex, 1);
8
+ next.splice(toIndex, 0, moved);
9
+ return next;
10
+ }
11
+ export {
12
+ moveRow
13
+ };
14
+ //# sourceMappingURL=sortableRows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sortableRows.js","sources":["../../src/sortable/sortableRows.js"],"sourcesContent":["// Mover un renglón de lugar en una lista donde el orden es el dato (qué se hace primero, en qué orden pasa la ruta). Sin\n// React ni MUI: lo usa SortableTable al soltar y la pantalla que quiera mover sin arrastrar (botones «Subir» y «Bajar»).\n//\n// Las posiciones van desde 0, como las del arreglo; lo que ve una persona («posición 3») es la de aquí más uno.\n//\n// Funciones puras, sin React: function (regla de la casa).\n\n/**\n * La lista con un renglón movido a otro lugar. No toca la lista que recibe: regresa una nueva.\n *\n * Una posición fuera de la lista (o la misma) regresa una copia igual: nada que mover.\n *\n * @template T\n * @param {ReadonlyArray<T>} rows en su orden de hoy\n * @param {number} fromIndex dónde está el renglón (desde 0)\n * @param {number} toIndex dónde queda (desde 0, contado en la lista ya sin él, como lo da el arrastre)\n * @returns {T[]}\n */\nexport function moveRow(rows, fromIndex, toIndex) {\n const next = [...rows];\n const isInside = (index) => Number.isInteger(index) && index >= 0 && index < rows.length;\n // guard clause: posiciones que no existen o el mismo lugar\n if (!isInside(fromIndex) || !isInside(toIndex) || fromIndex === toIndex) {\n return next;\n }\n const [moved] = next.splice(fromIndex, 1);\n next.splice(toIndex, 0, moved);\n return next;\n}\n"],"names":[],"mappings":"AAkBO,SAAS,QAAQ,MAAM,WAAW,SAAS;AAC9C,QAAM,OAAO,CAAC,GAAG,IAAI;AACrB,QAAM,WAAW,CAAC,UAAU,OAAO,UAAU,KAAK,KAAK,SAAS,KAAK,QAAQ,KAAK;AAElF,MAAI,CAAC,SAAS,SAAS,KAAK,CAAC,SAAS,OAAO,KAAK,cAAc,SAAS;AACrE,WAAO;AAAA,EACX;AACA,QAAM,CAAC,KAAK,IAAI,KAAK,OAAO,WAAW,CAAC;AACxC,OAAK,OAAO,SAAS,GAAG,KAAK;AAC7B,SAAO;AACX;"}
@@ -0,0 +1,9 @@
1
+ import { default as default2 } from "./sortable/FormSortableField.js";
2
+ import { default as default3 } from "./sortable/SortableTable.js";
3
+ import { moveRow } from "./sortable/sortableRows.js";
4
+ export {
5
+ default2 as FormSortableField,
6
+ default3 as SortableTable,
7
+ moveRow
8
+ };
9
+ //# sourceMappingURL=sortable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sortable.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -1,19 +1,19 @@
1
1
  /** Los tipos de la casa: su ícono y su color (de la paleta del tema, claro y oscuro). */
2
2
  export declare const DEFAULT_FILE_TYPES: Readonly<{
3
3
  xlsx: Readonly<{
4
- Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material/SvgIcon").SvgIconTypeMap<{}, "svg">> & {
4
+ Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
5
5
  muiName: string;
6
6
  };
7
7
  color: "success.main";
8
8
  }>;
9
9
  csv: Readonly<{
10
- Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material/SvgIcon").SvgIconTypeMap<{}, "svg">> & {
10
+ Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
11
11
  muiName: string;
12
12
  };
13
13
  color: "info.main";
14
14
  }>;
15
15
  pdf: Readonly<{
16
- Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material/SvgIcon").SvgIconTypeMap<{}, "svg">> & {
16
+ Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
17
17
  muiName: string;
18
18
  };
19
19
  color: "error.main";
@@ -21,7 +21,7 @@ export declare const DEFAULT_FILE_TYPES: Readonly<{
21
21
  }>;
22
22
  /** Un tipo que nadie declaró: el ícono de archivo y el gris del texto. */
23
23
  export declare const UNKNOWN_FILE_TYPE: Readonly<{
24
- Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material/SvgIcon").SvgIconTypeMap<{}, "svg">> & {
24
+ Icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
25
25
  muiName: string;
26
26
  };
27
27
  color: "text.secondary";
@@ -2,7 +2,7 @@ declare const SubmitButton: (props: {
2
2
  children: import('react').ReactNode;
3
3
  disabled?: boolean;
4
4
  isPending?: boolean;
5
- } & Omit<import("@mui/material/Button").ButtonProps<"button", {}>, "children" | "type"> & {
5
+ } & Omit<import("@mui/material").ButtonProps<"button", {}>, "children" | "type"> & {
6
6
  slots?: Record<string, import('react').ElementType>;
7
7
  slotProps?: Record<string, object | ((partProps: object) => object)>;
8
8
  }) => import("react").JSX.Element;
@@ -961,6 +961,19 @@ declare const _default: Readonly<{
961
961
  emptyFiltered: string;
962
962
  empty: string;
963
963
  };
964
+ sortableTable: {
965
+ instructions: string;
966
+ handleHeader: string;
967
+ handle: string;
968
+ position: string;
969
+ lifted: string;
970
+ moving: string;
971
+ dropped: string;
972
+ cancelled: string;
973
+ reverted: string;
974
+ empty: string;
975
+ noValue: string;
976
+ };
964
977
  inbox: {
965
978
  labels: string;
966
979
  select: string;
@@ -961,6 +961,19 @@ declare const _default: Readonly<{
961
961
  emptyFiltered: string;
962
962
  empty: string;
963
963
  };
964
+ sortableTable: {
965
+ instructions: string;
966
+ handleHeader: string;
967
+ handle: string;
968
+ position: string;
969
+ lifted: string;
970
+ moving: string;
971
+ dropped: string;
972
+ cancelled: string;
973
+ reverted: string;
974
+ empty: string;
975
+ noValue: string;
976
+ };
964
977
  inbox: {
965
978
  labels: string;
966
979
  select: string;
@@ -0,0 +1,15 @@
1
+ import SortableTable from './SortableTable.js';
2
+ /**
3
+ * @template {object} Row
4
+ * @param {{name: string, onReorder?: (change: {rows: Row[], rowId: string|number, fromIndex: number, toIndex: number}) => void} & Omit<Parameters<typeof SortableTable<Row>>[0], 'rows'|'onReorder'|'error'>} props
5
+ * las de SortableTable (columns, ariaLabel, getRowId…) sin rows ni error; su onReorder corre después del cambio del campo
6
+ */
7
+ export default function FormSortableField<Row extends object>({ name, onReorder, disabled, ...tableProps }: {
8
+ name: string;
9
+ onReorder?: (change: {
10
+ rows: Row[];
11
+ rowId: string | number;
12
+ fromIndex: number;
13
+ toIndex: number;
14
+ }) => void;
15
+ } & Omit<Parameters<typeof SortableTable<Row>>[0], 'rows' | 'onReorder' | 'error'>): import("react").JSX.Element;
@@ -0,0 +1,77 @@
1
+ export type SortableTableColumn<Row> = {
2
+ /**
3
+ * la llave del renglón que se pinta (y la del encabezado)
4
+ */
5
+ field: string;
6
+ /**
7
+ * el encabezado, ya traducido
8
+ */
9
+ headerName: string;
10
+ /**
11
+ * ancho fijo en px
12
+ */
13
+ width?: number;
14
+ /**
15
+ * cuánto del espacio libre toma, como en la tabla de servidor (por omisión 1)
16
+ */
17
+ flex?: number;
18
+ /**
19
+ * el mínimo en px de una columna sin ancho fijo (por omisión 96)
20
+ */
21
+ minWidth?: number;
22
+ /**
23
+ * los números a la derecha
24
+ */
25
+ align?: 'left' | 'center' | 'right';
26
+ /**
27
+ * lo de la celda a la
28
+ * medida; position es la que ve la persona (desde 1)
29
+ */
30
+ renderCell?: (row: Row, place: {
31
+ index: number;
32
+ position: number;
33
+ }) => import('react').ReactNode;
34
+ };
35
+ /**
36
+ * Los renglones conservan el tipo que usa la pantalla (Row): onReorder los regresa del mismo tipo, ya en su orden nuevo.
37
+ *
38
+ * @template {object} Row
39
+ * @param {object} props
40
+ * @param {Row[]} props.rows en su orden
41
+ * @param {SortableTableColumn<Row>[]} props.columns
42
+ * @param {(change: {rows: Row[], rowId: string|number, fromIndex: number, toIndex: number}) => Promise<unknown>|void} props.onReorder
43
+ * al soltar un renglón en otro lugar: rows ya en el orden nuevo; fromIndex y toIndex desde 0. Si regresa una promesa, el
44
+ * orden nuevo se enseña mientras se cumple y regresa si truena
45
+ * @param {string} props.ariaLabel qué es la tabla, ya traducido
46
+ * @param {(row: Row) => string|number} [props.getRowId] por omisión row.id
47
+ * @param {(row: Row) => string} [props.getRowLabel] el nombre del renglón que oye el lector de pantalla («Polanco»); por omisión
48
+ * lo de la primera columna
49
+ * @param {boolean} [props.showPosition] la columna «Orden» con 1..n (por omisión sí)
50
+ * @param {boolean} [props.disabled] nada se arrastra (mientras se envía el formulario, o de solo lectura)
51
+ * @param {import('react').ReactNode} [props.error] el error de la lista (en rojo, abajo de la tabla)
52
+ * @param {import('react').ReactNode} [props.helperText] una ayuda abajo de la tabla (el error gana)
53
+ * @param {import('@mui/material').SxProps<import('@mui/material').Theme>} [props.sx] lo de afuera de la tabla
54
+ * @param {object} [props.texts] los textos que cambian, con la forma de `sortableTable` en los textos del paquete
55
+ */
56
+ declare function SortableTableContent<Row extends object>(props: {
57
+ rows: Row[];
58
+ columns: SortableTableColumn<Row>[];
59
+ onReorder: (change: {
60
+ rows: Row[];
61
+ rowId: string | number;
62
+ fromIndex: number;
63
+ toIndex: number;
64
+ }) => Promise<unknown> | void;
65
+ ariaLabel: string;
66
+ getRowId?: (row: Row) => string | number;
67
+ getRowLabel?: (row: Row) => string;
68
+ showPosition?: boolean;
69
+ disabled?: boolean;
70
+ error?: import('react').ReactNode;
71
+ helperText?: import('react').ReactNode;
72
+ sx?: import('@mui/material').SxProps<import('@mui/material').Theme>;
73
+ texts?: object;
74
+ }): import("react").JSX.Element;
75
+ /** @type {typeof SortableTableContent} */
76
+ declare const SortableTable: typeof SortableTableContent;
77
+ export default SortableTable;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * La lista con un renglón movido a otro lugar. No toca la lista que recibe: regresa una nueva.
3
+ *
4
+ * Una posición fuera de la lista (o la misma) regresa una copia igual: nada que mover.
5
+ *
6
+ * @template T
7
+ * @param {ReadonlyArray<T>} rows en su orden de hoy
8
+ * @param {number} fromIndex dónde está el renglón (desde 0)
9
+ * @param {number} toIndex dónde queda (desde 0, contado en la lista ya sin él, como lo da el arrastre)
10
+ * @returns {T[]}
11
+ */
12
+ export declare function moveRow<T>(rows: ReadonlyArray<T>, fromIndex: number, toIndex: number): T[];
@@ -0,0 +1,3 @@
1
+ export { default as FormSortableField } from './sortable/FormSortableField.js';
2
+ export { default as SortableTable } from './sortable/SortableTable.js';
3
+ export { moveRow } from './sortable/sortableRows.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackcraftmx/calcita",
3
- "version": "0.57.0",
3
+ "version": "0.58.0",
4
4
  "description": "Componentes y utilidades de React con MUI para arrancar proyectos rápido: tabla de servidor, diálogos, avisos, descargas, formularios, formatos y reportes en Excel.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "calcifux",
@@ -139,6 +139,10 @@
139
139
  "types": "./dist/types/kanban.d.ts",
140
140
  "import": "./dist/kanban.js"
141
141
  },
142
+ "./sortable": {
143
+ "types": "./dist/types/sortable.d.ts",
144
+ "import": "./dist/sortable.js"
145
+ },
142
146
  "./package.json": "./package.json"
143
147
  },
144
148
  "scripts": {