fix(table): ресайз завершается при потерянном mouseup; тест-характеристики table-core; комментарии
- ColumnResizer: handleMouseMove завершает перетаскивание, если левая кнопка не зажата - cellMemoReset.test: убран дубль теста columnSizing, добавлены характеристики закрепления второй колонки и скрытия закреплённой - комментарии к columnFilters (триггер React.memo(TableHead)) и ссылки на архивную спеку Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
651d71f7c0
commit
e0e460b6c2
@ -847,6 +847,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
columnPinning={columnPinning}
|
columnPinning={columnPinning}
|
||||||
columnSizing={columnSizing}
|
columnSizing={columnSizing}
|
||||||
columnVisibility={columnVisibility}
|
columnVisibility={columnVisibility}
|
||||||
|
// Проп нужен как триггер React.memo(TableHead): фильтры шапка читает из table.getState().
|
||||||
columnFilters={columnFilters}
|
columnFilters={columnFilters}
|
||||||
showColumnFilters={showColumnFilters}
|
showColumnFilters={showColumnFilters}
|
||||||
selectedColumnId={selectedColumnId}
|
selectedColumnId={selectedColumnId}
|
||||||
|
|||||||
@ -28,6 +28,11 @@ const ColumnResizer = ({ columnId, reportDragDeltaX, onDragStart, onDragEnd }) =
|
|||||||
const handleMouseMove = useCallback(
|
const handleMouseMove = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (!isDragging) return;
|
if (!isDragging) return;
|
||||||
|
// Потерянный mouseup (отпустили вне окна без blur): кнопка уже не зажата.
|
||||||
|
if ((e.buttons & 1) === 0) {
|
||||||
|
finishDrag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const deltaX = e.clientX - lastXRef.current;
|
const deltaX = e.clientX - lastXRef.current;
|
||||||
|
|
||||||
@ -36,7 +41,7 @@ const ColumnResizer = ({ columnId, reportDragDeltaX, onDragStart, onDragEnd }) =
|
|||||||
lastXRef.current = e.clientX;
|
lastXRef.current = e.clientX;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isDragging, reportDragDeltaX],
|
[isDragging, reportDragDeltaX, finishDrag],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// Ключ явного сброса мемоизации ячеек MRT (memoMode: 'cells') в таблице листа.
|
// Ключ явного сброса мемоизации ячеек MRT (memoMode: 'cells') в таблице листа.
|
||||||
// Обоснование и контракт: openspec/changes/aurora-1839-cell-memo/ (design.md D1, D2; specs/sheet-table).
|
// Обоснование и контракт: openspec/changes/aurora-1839-cell-memo/ (design.md D1, D2; specs/sheet-table; после архивации — openspec/specs/sheet-table/spec.md).
|
||||||
// Новое состояние, которое меняет определения колонок или стили закрепления, добавляется сюда и в cellMemoReset.test.js.
|
// Новое состояние, которое меняет определения колонок или стили закрепления, добавляется сюда и в cellMemoReset.test.js.
|
||||||
|
|
||||||
const getPinnedSide = (ids, columnVisibility, columnSizing) =>
|
const getPinnedSide = (ids, columnVisibility, columnSizing) =>
|
||||||
|
|||||||
@ -110,7 +110,9 @@ test('пока ширину тянут, ключ прежний; после от
|
|||||||
|
|
||||||
// Допущение @tanstack/table-core 8.20.5, на котором стоит явный сброс
|
// Допущение @tanstack/table-core 8.20.5, на котором стоит явный сброс
|
||||||
// (openspec/changes/aurora-1839-cell-memo/design.md, «Контекст», п. 3; D1).
|
// (openspec/changes/aurora-1839-cell-memo/design.md, «Контекст», п. 3; D1).
|
||||||
// Первые два теста держат допущение: ячейки пересоздаются при смене массива columns и не пересоздаются при смене ширин.
|
// Тесты ниже фиксируют характеристику table-core 8.20.5 (упадут при обновлении библиотеки):
|
||||||
|
// новый массив columns даёт новые ячейки; смена columnSizing ячейки не пересоздаёт (поэтому ключ нужен для ширины
|
||||||
|
// закреплённой колонки); смена columnPinning пересоздаёт ячейки закреплённых сторон, центральные те же.
|
||||||
const createFixture = () =>
|
const createFixture = () =>
|
||||||
createTable({
|
createTable({
|
||||||
data: [{ a: 1, b: 2, c: 3 }],
|
data: [{ a: 1, b: 2, c: 3 }],
|
||||||
@ -131,13 +133,6 @@ const cellsById = (table) =>
|
|||||||
.map((cell) => [cell.id, cell]),
|
.map((cell) => [cell.id, cell]),
|
||||||
);
|
);
|
||||||
|
|
||||||
test('table-core: при прежних columns и data объекты ячеек те же', () => {
|
|
||||||
const table = createFixture();
|
|
||||||
const before = cellsById(table);
|
|
||||||
updateState(table, { columnSizing: { a: 80 } });
|
|
||||||
for (const [id, cell] of cellsById(table)) assert.equal(cell, before.get(id));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('table-core: новый массив columns с теми же определениями даёт новые ячейки', () => {
|
test('table-core: новый массив columns с теми же определениями даёт новые ячейки', () => {
|
||||||
const table = createFixture();
|
const table = createFixture();
|
||||||
const before = cellsById(table);
|
const before = cellsById(table);
|
||||||
@ -147,12 +142,11 @@ test('table-core: новый массив columns с теми же опреде
|
|||||||
for (const [id, cell] of after) assert.notEqual(cell, before.get(id));
|
for (const [id, cell] of after) assert.notEqual(cell, before.get(id));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Допущение дизайна «ячейки пересоздаются только при смене columns/data» неверно для columnPinning:
|
// В table-core 8.20.5 getLeftVisibleCells/getRightVisibleCells (features/ColumnPinning.js) возвращают
|
||||||
// в table-core 8.20.5 getLeftVisibleCells/getRightVisibleCells (features/ColumnPinning.js) возвращают
|
// поверхностные копии `{...cell, position}`, поэтому при смене закрепления пересоздаются ячейки закреплённых
|
||||||
// поверхностные копии `{...cell, position}`, поэтому при смене закрепления объект ячейки закрепляемой колонки
|
// колонок при прежнем массиве columns; центральные остаются теми же объектами.
|
||||||
// новый при прежнем массиве columns. Ячейки остальных колонок (центр) остаются теми же объектами.
|
// Ключ сброса по закреплению нужен из-за ширины закреплённой колонки (смена columnSizing ячейки не пересоздаёт)
|
||||||
// Ключ сброса по закреплению остаётся как защита: он нужен для ячеек, которые table-core не пересоздаёт,
|
// и как страховка на случай смены поведения библиотеки.
|
||||||
// и сработает как страховка, если поведение сменится. Тест фиксирует факт и упадёт при обновлении table-core.
|
|
||||||
test('table-core: смена columnPinning при прежнем массиве columns пересоздаёт ячейку закреплённой колонки, остальные те же', () => {
|
test('table-core: смена columnPinning при прежнем массиве columns пересоздаёт ячейку закреплённой колонки, остальные те же', () => {
|
||||||
const table = createFixture();
|
const table = createFixture();
|
||||||
const before = cellsById(table);
|
const before = cellsById(table);
|
||||||
@ -163,8 +157,6 @@ test('table-core: смена columnPinning при прежнем массиве
|
|||||||
for (const id of ['0_a', '0_b']) assert.equal(after.get(id), before.get(id));
|
for (const id of ['0_a', '0_b']) assert.equal(after.get(id), before.get(id));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Характеристика, а не ожидание дизайна: фиксирует поведение table-core 8.20.5 при смене columnSizing,
|
|
||||||
// чтобы обновление библиотеки показало смену поведения.
|
|
||||||
test('table-core: смена columnSizing при прежнем массиве columns оставляет те же ячейки', () => {
|
test('table-core: смена columnSizing при прежнем массиве columns оставляет те же ячейки', () => {
|
||||||
const table = createFixture();
|
const table = createFixture();
|
||||||
const before = cellsById(table);
|
const before = cellsById(table);
|
||||||
@ -173,3 +165,24 @@ test('table-core: смена columnSizing при прежнем массиве c
|
|||||||
assert.equal(after.size, before.size);
|
assert.equal(after.size, before.size);
|
||||||
for (const [id, cell] of after) assert.equal(cell, before.get(id));
|
for (const [id, cell] of after) assert.equal(cell, before.get(id));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('table-core: закрепление второй колонки слева пересоздаёт все закреплённые ячейки, центральная та же', () => {
|
||||||
|
const table = createFixture();
|
||||||
|
updateState(table, { columnPinning: { left: ['a'], right: [] } });
|
||||||
|
const before = cellsById(table);
|
||||||
|
updateState(table, { columnPinning: { left: ['a', 'c'], right: [] } });
|
||||||
|
const after = cellsById(table);
|
||||||
|
assert.notEqual(after.get('0_a'), before.get('0_a'));
|
||||||
|
assert.notEqual(after.get('0_c'), before.get('0_c'));
|
||||||
|
assert.equal(after.get('0_b'), before.get('0_b'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('table-core: скрытие закреплённой колонки пересоздаёт ячейки оставшейся закреплённой стороны', () => {
|
||||||
|
const table = createFixture();
|
||||||
|
updateState(table, { columnPinning: { left: ['a', 'c'], right: [] } });
|
||||||
|
const before = cellsById(table);
|
||||||
|
updateState(table, { columnVisibility: { a: false } });
|
||||||
|
const after = cellsById(table);
|
||||||
|
assert.equal(after.has('0_a'), false);
|
||||||
|
assert.notEqual(after.get('0_c'), before.get('0_c'));
|
||||||
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// columnSizingInfo для MRT/TanStack при собственном перетаскивании ширины (TableHead/ColumnResizer).
|
// columnSizingInfo для MRT/TanStack при собственном перетаскивании ширины (TableHead/ColumnResizer).
|
||||||
// Пока isResizingColumn !== false, MRT 3.2.1 не пересоздаёт колонки и замораживает тело таблицы
|
// Пока isResizingColumn !== false, MRT 3.2.1 не пересоздаёт колонки и замораживает тело таблицы
|
||||||
// (openspec/changes/aurora-1839-cell-memo/design.md, «Контекст» п. 3–4, D3).
|
// (openspec/changes/aurora-1839-cell-memo/design.md, «Контекст» п. 3–4, D3; после архивации — openspec/specs/sheet-table/spec.md).
|
||||||
|
|
||||||
export const DEFAULT_COLUMN_SIZING_INFO = {
|
export const DEFAULT_COLUMN_SIZING_INFO = {
|
||||||
startOffset: null,
|
startOffset: null,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user