Skip to content

Commit

Permalink
refactor: Table cell editing (#1399)
Browse files Browse the repository at this point in the history
  • Loading branch information
pan-kot authored Aug 2, 2023
1 parent b4ed143 commit 392b450
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 41 deletions.
55 changes: 14 additions & 41 deletions src/table/internal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import clsx from 'clsx';
import React, { useImperativeHandle, useRef, useState } from 'react';
import React, { useImperativeHandle, useRef } from 'react';
import { TableForwardRefType, TableProps } from './interfaces';
import { getVisualContextClassname } from '../internal/components/visual-context';
import InternalContainer from '../container/internal';
Expand All @@ -15,7 +15,7 @@ import SelectionControl from './selection-control';
import { checkSortingState, getColumnKey, getItemKey, getVisibleColumnDefinitions, toContainerVariant } from './utils';
import { useRowEvents } from './use-row-events';
import { focusMarkers, useFocusMove, useSelection } from './use-selection';
import { fireCancelableEvent, fireNonCancelableEvent } from '../internal/events';
import { fireNonCancelableEvent } from '../internal/events';
import { isDevelopment } from '../internal/is-development';
import { ColumnWidthDefinition, ColumnWidthsProvider, DEFAULT_COLUMN_WIDTH } from './use-column-widths';
import { useScrollSync } from '../internal/hooks/use-scroll-sync';
Expand All @@ -37,6 +37,7 @@ import { checkColumnWidths } from './column-widths-utils';
import { useMobile } from '../internal/hooks/use-mobile';
import { useContainerQuery } from '@cloudscape-design/component-toolkit';
import { getTableRoleProps, getTableRowRoleProps } from './table-role';
import { useCellEditing } from './use-cell-editing';

const SELECTION_COLUMN_WIDTH = 54;
const selectionColumnId = Symbol('selection-column-id');
Expand Down Expand Up @@ -104,17 +105,15 @@ const InternalTable = React.forwardRef(
const theadRef = useRef<HTMLTableRowElement>(null);
const stickyHeaderRef = React.useRef<StickyHeaderRef>(null);
const scrollbarRef = React.useRef<HTMLDivElement>(null);
const [currentEditCell, setCurrentEditCell] = useState<[number, number] | null>(null);
const [lastSuccessfulEditCell, setLastSuccessfulEditCell] = useState<[number, number] | null>(null);
const [currentEditLoading, setCurrentEditLoading] = useState(false);
const { cancelEdit, ...cellEditing } = useCellEditing({ onCancel: onEditCancel, onSubmit: submitEdit });

useImperativeHandle(
ref,
() => ({
scrollToTop: stickyHeaderRef.current?.scrollToTop || (() => undefined),
cancelEdit: () => setCurrentEditCell(null),
cancelEdit,
}),
[]
[cancelEdit]
);

const handleScroll = useScrollSync([wrapperRefObject, scrollbarRef, secondaryWrapperRef]);
Expand Down Expand Up @@ -219,19 +218,6 @@ const InternalTable = React.forwardRef(
: {};

const getMouseDownTarget = useMouseDownTarget();
const wrapWithInlineLoadingState = (submitEdit: TableProps['submitEdit']) => {
if (!submitEdit) {
return undefined;
}
return async (...args: Parameters<typeof submitEdit>) => {
setCurrentEditLoading(true);
try {
await submitEdit(...args);
} finally {
setCurrentEditLoading(false);
}
};
};

const hasDynamicHeight = computedVariant === 'full-page';
const overlapElement = useDynamicOverlap({ disabled: !hasDynamicHeight });
Expand Down Expand Up @@ -405,13 +391,9 @@ const InternalTable = React.forwardRef(
</TableTdElement>
)}
{visibleColumnDefinitions.map((column, colIndex) => {
const isEditing =
!!currentEditCell && currentEditCell[0] === rowIndex && currentEditCell[1] === colIndex;
const successfulEdit =
!!lastSuccessfulEditCell &&
lastSuccessfulEditCell[0] === rowIndex &&
lastSuccessfulEditCell[1] === colIndex;
const isEditable = !!column.editConfig && !currentEditLoading;
const isEditing = cellEditing.checkEditing({ rowIndex, colIndex });
const successfulEdit = cellEditing.checkLastSuccessfulEdit({ rowIndex, colIndex });
const isEditable = !!column.editConfig && !cellEditing.isLoading;
return (
<TableBodyCell
key={getColumnKey(column, colIndex)}
Expand All @@ -437,20 +419,11 @@ const InternalTable = React.forwardRef(
isNextSelected={isNextSelected}
isPrevSelected={isPrevSelected}
successfulEdit={successfulEdit}
onEditStart={() => {
setLastSuccessfulEditCell(null);
setCurrentEditCell([rowIndex, colIndex]);
}}
onEditEnd={editCancelled => {
const eventCancelled = fireCancelableEvent(onEditCancel, {});
if (!eventCancelled) {
setCurrentEditCell(null);
if (!editCancelled) {
setLastSuccessfulEditCell([rowIndex, colIndex]);
}
}
}}
submitEdit={wrapWithInlineLoadingState(submitEdit)}
onEditStart={() => cellEditing.startEdit({ rowIndex, colIndex })}
onEditEnd={editCancelled =>
cellEditing.completeEdit({ rowIndex, colIndex }, editCancelled)
}
submitEdit={cellEditing.submitEdit}
hasFooter={hasFooter}
stripedRows={stripedRows}
isEvenRow={isEven}
Expand Down
66 changes: 66 additions & 0 deletions src/table/use-cell-editing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { useCallback, useState } from 'react';
import { TableProps } from './interfaces';
import { CancelableEventHandler, fireCancelableEvent } from '../internal/events';

export interface CellId {
rowIndex: number;
colIndex: number;
}

interface CellEditingProps {
onCancel?: CancelableEventHandler;
onSubmit?: TableProps.SubmitEditFunction<any>;
}

export function useCellEditing({ onCancel, onSubmit }: CellEditingProps) {
const [currentEditCell, setCurrentEditCell] = useState<null | CellId>(null);
const [lastSuccessfulEditCell, setLastSuccessfulEditCell] = useState<null | CellId>(null);
const [currentEditLoading, setCurrentEditLoading] = useState(false);

const startEdit = (cellId: CellId) => {
setLastSuccessfulEditCell(null);
setCurrentEditCell(cellId);
};

const cancelEdit = useCallback(() => setCurrentEditCell(null), []);

const completeEdit = (cellId: CellId, editCancelled: boolean) => {
const eventCancelled = fireCancelableEvent(onCancel, {});
if (!eventCancelled) {
setCurrentEditCell(null);
if (!editCancelled) {
setLastSuccessfulEditCell(cellId);
}
}
};

const checkEditing = ({ rowIndex, colIndex }: CellId) =>
rowIndex === currentEditCell?.rowIndex && colIndex === currentEditCell.colIndex;

const checkLastSuccessfulEdit = ({ rowIndex, colIndex }: CellId) =>
rowIndex === lastSuccessfulEditCell?.rowIndex && colIndex === lastSuccessfulEditCell.colIndex;

const submitEdit = onSubmit
? async (...args: Parameters<typeof onSubmit>) => {
setCurrentEditLoading(true);
try {
await onSubmit(...args);
} finally {
setCurrentEditLoading(false);
}
}
: undefined;

return {
isLoading: currentEditLoading,
startEdit,
cancelEdit,
checkEditing,
checkLastSuccessfulEdit,
completeEdit,
submitEdit,
};
}

0 comments on commit 392b450

Please sign in to comment.