Improve RecordTableCellperformances (#3659)
* Improve RecordTableCellperformances * Fixes
This commit is contained in:
@ -28,8 +28,9 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
||||
objectMetadataItem.nameSingular,
|
||||
cachedConnection,
|
||||
)
|
||||
)
|
||||
) {
|
||||
return cachedConnection;
|
||||
}
|
||||
|
||||
const { variables } =
|
||||
parseApolloStoreFieldName<CachedObjectRecordQueryVariables>(
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
|
||||
|
||||
export const useObjectMetadataItemOnly = ({
|
||||
objectNameSingular,
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
objectMetadataItemFamilySelector({
|
||||
objectName: objectNameSingular,
|
||||
objectNameType: 'singular',
|
||||
}),
|
||||
);
|
||||
|
||||
let objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
if (!currentWorkspace) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.nameSingular === objectNameSingular,
|
||||
) ?? null;
|
||||
objectMetadataItems = mockObjectMetadataItems;
|
||||
}
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
throw new ObjectMetadataItemNotFoundError(
|
||||
objectNameSingular,
|
||||
objectMetadataItems,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
objectMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
||||
|
||||
@ -15,10 +16,14 @@ export const RecordChip = ({
|
||||
record,
|
||||
maxWidth,
|
||||
}: RecordChipProps) => {
|
||||
const { mapToObjectRecordIdentifier } = useObjectMetadataItem({
|
||||
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const mapToObjectRecordIdentifier = useMapToObjectRecordIdentifier({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const objectRecordIdentifier = mapToObjectRecordIdentifier(record);
|
||||
|
||||
return (
|
||||
|
||||
@ -4,13 +4,13 @@ import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useObjectRecordTable } from '@/object-record/hooks/useObjectRecordTable';
|
||||
import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
|
||||
import { RecordTableScope } from '@/object-record/record-table/scopes/RecordTableScope';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
|
||||
const recordTableId = 'people';
|
||||
const objectNamePlural = 'people';
|
||||
const objectNameSingular = 'person';
|
||||
const onColumnsChange = jest.fn();
|
||||
|
||||
const ObjectNamePluralSetter = ({ children }: { children: ReactNode }) => {
|
||||
@ -37,7 +37,7 @@ const Wrapper = ({ children }: { children: ReactNode }) => {
|
||||
describe('useObjectRecordTable', () => {
|
||||
it('should skip fetch if currentWorkspace is undefined', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useObjectRecordTable(objectNamePlural),
|
||||
() => useLoadRecordIndexTable(objectNameSingular),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { RecordUpdateHookParams } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordTableActionBar } from '@/object-record/record-table/action-bar/components/RecordTableActionBar';
|
||||
@ -18,10 +17,6 @@ export const RecordIndexTableContainer = ({
|
||||
objectNameSingular,
|
||||
createRecord,
|
||||
}: RecordIndexTableContainerProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular,
|
||||
});
|
||||
@ -37,7 +32,7 @@ export const RecordIndexTableContainer = ({
|
||||
<>
|
||||
<RecordTableWithWrappers
|
||||
recordTableId={recordTableId}
|
||||
objectNamePlural={objectMetadataItem.namePlural}
|
||||
objectNameSingular={objectNameSingular}
|
||||
viewBarId={viewBarId}
|
||||
updateRecordMutation={updateEntity}
|
||||
createRecord={createRecord}
|
||||
|
||||
@ -18,17 +18,11 @@ export const RecordIndexTableContainerEffect = ({
|
||||
recordTableId,
|
||||
viewBarId,
|
||||
}: RecordIndexTableContainerEffectProps) => {
|
||||
const {
|
||||
setAvailableTableColumns,
|
||||
setOnEntityCountChange,
|
||||
setObjectMetadataConfig,
|
||||
} = useRecordTable({ recordTableId });
|
||||
const { setAvailableTableColumns, setOnEntityCountChange } = useRecordTable({
|
||||
recordTableId,
|
||||
});
|
||||
|
||||
const {
|
||||
objectMetadataItem,
|
||||
basePathToShowPage,
|
||||
labelIdentifierFieldMetadata,
|
||||
} = useObjectMetadataItem({
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
@ -39,20 +33,6 @@ export const RecordIndexTableContainerEffect = ({
|
||||
viewBarId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (basePathToShowPage && labelIdentifierFieldMetadata) {
|
||||
setObjectMetadataConfig?.({
|
||||
basePathToShowPage,
|
||||
labelIdentifierFieldMetadataId: labelIdentifierFieldMetadata.id,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
basePathToShowPage,
|
||||
objectMetadataItem,
|
||||
labelIdentifierFieldMetadata,
|
||||
setObjectMetadataConfig,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const availableTableColumns = columnDefinitions.filter(
|
||||
filterAvailableTableColumns,
|
||||
|
||||
@ -2,30 +2,23 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { signInBackgroundMockCompanies } from '@/sign-in-background-mock/constants/signInBackgroundMockCompanies';
|
||||
|
||||
import { useFindManyRecords } from './useFindManyRecords';
|
||||
import { useFindManyRecords } from '../../hooks/useFindManyRecords';
|
||||
|
||||
export const useObjectRecordTable = (objectNamePlural: string) => {
|
||||
export const useLoadRecordIndexTable = (objectNameSingular: string) => {
|
||||
const { setRecordTableData, setIsRecordTableInitialLoading } =
|
||||
useRecordTable();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
getTableFiltersState,
|
||||
getTableSortsState,
|
||||
@ -38,12 +31,12 @@ export const useObjectRecordTable = (objectNamePlural: string) => {
|
||||
|
||||
const requestFilters = turnObjectDropdownFilterIntoQueryFilter(
|
||||
tableFilters,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
objectMetadataItem?.fields ?? [],
|
||||
);
|
||||
|
||||
const orderBy = turnSortsIntoOrderBy(
|
||||
tableSorts,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
objectMetadataItem?.fields ?? [],
|
||||
);
|
||||
|
||||
const { records, loading, fetchMoreRecords, queryStateIdentifier } =
|
||||
@ -1,12 +1,13 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useSetCurrentRowSelected } from '@/object-record/record-table/record-table-row/hooks/useSetCurrentRowSelected';
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||
|
||||
import { useCurrentRowSelected } from '../record-table-row/hooks/useCurrentRowSelected';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
@ -18,8 +19,11 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const CheckboxCell = () => {
|
||||
const { recordId } = useContext(RecordTableRowContext);
|
||||
const { isRowSelectedFamilyState } = useRecordTableStates();
|
||||
const setActionBarOpenState = useSetRecoilState(actionBarOpenState);
|
||||
const { currentRowSelected, setCurrentRowSelected } = useCurrentRowSelected();
|
||||
const { setCurrentRowSelected } = useSetCurrentRowSelected();
|
||||
const currentRowSelected = useRecoilValue(isRowSelectedFamilyState(recordId));
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
setCurrentRowSelected(!currentRowSelected);
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { RecordTableBody } from '@/object-record/record-table/components/RecordTableBody';
|
||||
import { RecordTableBodyEffect } from '@/object-record/record-table/components/RecordTableBodyEffect';
|
||||
import { RecordTableFirstColumnScrollEffect } from '@/object-record/record-table/components/RecordTableFirstColumnScrollObserver';
|
||||
import { RecordTableHeader } from '@/object-record/record-table/components/RecordTableHeader';
|
||||
import { RecordTableRefContext } from '@/object-record/record-table/contexts/RecordTableRefContext';
|
||||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { RecordTableScope } from '@/object-record/record-table/scopes/RecordTableScope';
|
||||
import { rgba } from '@/ui/theme/constants/colors';
|
||||
@ -48,8 +50,7 @@ const StyledTable = styled.table`
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
th {
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border-right: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
}
|
||||
@ -98,31 +99,43 @@ const StyledTable = styled.table`
|
||||
|
||||
type RecordTableProps = {
|
||||
recordTableId: string;
|
||||
objectNamePlural: string;
|
||||
objectNameSingular: string;
|
||||
onColumnsChange: (columns: any) => void;
|
||||
createRecord: () => void;
|
||||
};
|
||||
|
||||
export const RecordTable = ({
|
||||
recordTableId,
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
onColumnsChange,
|
||||
createRecord,
|
||||
}: RecordTableProps) => {
|
||||
const recordTableRef = useContext(RecordTableRefContext);
|
||||
const recordTableRef = useRef<HTMLTableElement>(null);
|
||||
const { scopeId } = useRecordTableStates(recordTableId);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
return (
|
||||
<RecordTableScope
|
||||
recordTableScopeId={scopeId}
|
||||
onColumnsChange={onColumnsChange}
|
||||
>
|
||||
{!!objectNamePlural && (
|
||||
<StyledTable ref={recordTableRef} className="entity-table-cell">
|
||||
<RecordTableHeader createRecord={createRecord} />
|
||||
<RecordTableBodyEffect objectNamePlural={objectNamePlural} />
|
||||
<RecordTableBody objectNamePlural={objectNamePlural} />
|
||||
</StyledTable>
|
||||
{!!objectNameSingular && (
|
||||
<RecordTableContext.Provider
|
||||
value={{
|
||||
objectMetadataItem,
|
||||
recordTableRef,
|
||||
}}
|
||||
>
|
||||
<RecordTableFirstColumnScrollEffect />
|
||||
<StyledTable ref={recordTableRef} className="entity-table-cell">
|
||||
<RecordTableHeader createRecord={createRecord} />
|
||||
<RecordTableBodyEffect objectNameSingular={objectNameSingular} />
|
||||
<RecordTableBody objectNameSingular={objectNameSingular} />
|
||||
</StyledTable>
|
||||
</RecordTableContext.Provider>
|
||||
)}
|
||||
</RecordTableScope>
|
||||
);
|
||||
|
||||
@ -2,15 +2,15 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/components/RecordTableBodyFetchMoreLoader';
|
||||
import { RecordTableRow } from '@/object-record/record-table/components/RecordTableRow';
|
||||
import { RowIdContext } from '@/object-record/record-table/contexts/RowIdContext';
|
||||
import { RowIndexContext } from '@/object-record/record-table/contexts/RowIndexContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
|
||||
type RecordTableBodyProps = {
|
||||
objectNamePlural: string;
|
||||
objectNameSingular: string;
|
||||
};
|
||||
|
||||
export const RecordTableBody = ({ objectNamePlural }: RecordTableBodyProps) => {
|
||||
export const RecordTableBody = ({
|
||||
objectNameSingular,
|
||||
}: RecordTableBodyProps) => {
|
||||
const { getTableRowIdsState } = useRecordTableStates();
|
||||
|
||||
const tableRowIds = useRecoilValue(getTableRowIdsState());
|
||||
@ -18,15 +18,15 @@ export const RecordTableBody = ({ objectNamePlural }: RecordTableBodyProps) => {
|
||||
return (
|
||||
<>
|
||||
<tbody>
|
||||
{tableRowIds.map((rowId, rowIndex) => (
|
||||
<RowIdContext.Provider value={rowId} key={rowId}>
|
||||
<RowIndexContext.Provider value={rowIndex}>
|
||||
<RecordTableRow key={rowId} rowId={rowId} />
|
||||
</RowIndexContext.Provider>
|
||||
</RowIdContext.Provider>
|
||||
{tableRowIds.map((recordId, rowIndex) => (
|
||||
<RecordTableRow
|
||||
key={recordId}
|
||||
recordId={recordId}
|
||||
rowIndex={rowIndex}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
<RecordTableBodyFetchMoreLoader objectNamePlural={objectNamePlural} />
|
||||
<RecordTableBodyFetchMoreLoader objectNameSingular={objectNameSingular} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectRecordTable } from '@/object-record/hooks/useObjectRecordTable';
|
||||
import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
|
||||
type RecordTableBodyEffectProps = {
|
||||
objectNamePlural: string;
|
||||
objectNameSingular: string;
|
||||
};
|
||||
|
||||
export const RecordTableBodyEffect = ({
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
}: RecordTableBodyEffectProps) => {
|
||||
const {
|
||||
fetchMoreRecords: fetchMoreObjects,
|
||||
@ -18,7 +18,7 @@ export const RecordTableBodyEffect = ({
|
||||
setRecordTableData,
|
||||
queryStateIdentifier,
|
||||
loading,
|
||||
} = useObjectRecordTable(objectNamePlural);
|
||||
} = useLoadRecordIndexTable(objectNameSingular);
|
||||
|
||||
const { getTableLastRowVisibleState } = useRecordTableStates();
|
||||
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectRecordTable } from '@/object-record/hooks/useObjectRecordTable';
|
||||
import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
import { FetchMoreLoader } from '@/ui/utilities/loading-state/components/FetchMoreLoader';
|
||||
|
||||
type RecordTableBodyFetchMoreLoaderProps = {
|
||||
objectNamePlural: string;
|
||||
objectNameSingular: string;
|
||||
};
|
||||
|
||||
export const RecordTableBodyFetchMoreLoader = ({
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
}: RecordTableBodyFetchMoreLoaderProps) => {
|
||||
const { queryStateIdentifier } = useObjectRecordTable(objectNamePlural);
|
||||
const { queryStateIdentifier } = useLoadRecordIndexTable(objectNameSingular);
|
||||
const { setRecordTableLastRowVisible } = useRecordTable();
|
||||
|
||||
const isFetchingMoreObjects = useRecoilValue(
|
||||
|
||||
@ -1,32 +1,31 @@
|
||||
import { useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
|
||||
import { ColumnContext } from '@/object-record/record-table/contexts/ColumnContext';
|
||||
import { ColumnIndexContext } from '@/object-record/record-table/contexts/ColumnIndexContext';
|
||||
import { RecordUpdateContext } from '@/object-record/record-table/contexts/EntityUpdateMutationHookContext';
|
||||
import { RowIdContext } from '@/object-record/record-table/contexts/RowIdContext';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { RecordTableCell } from '@/object-record/record-table/record-table-cell/components/RecordTableCell';
|
||||
import { useCurrentRowSelected } from '@/object-record/record-table/record-table-row/hooks/useCurrentRowSelected';
|
||||
import { useSetCurrentRowSelected } from '@/object-record/record-table/record-table-row/hooks/useSetCurrentRowSelected';
|
||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
export const RecordTableCellContainer = ({
|
||||
cellIndex,
|
||||
}: {
|
||||
cellIndex: number;
|
||||
}) => {
|
||||
const StyledContainer = styled.td<{ isSelected: boolean }>`
|
||||
background: ${({ isSelected, theme }) =>
|
||||
isSelected ? theme.accent.quaternary : theme.background.primary};
|
||||
`;
|
||||
|
||||
export const RecordTableCellContainer = () => {
|
||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
const currentRowId = useContext(RowIdContext);
|
||||
|
||||
const { setCurrentRowSelected } = useCurrentRowSelected();
|
||||
const { setCurrentRowSelected } = useSetCurrentRowSelected();
|
||||
|
||||
const handleContextMenu = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
@ -38,16 +37,15 @@ export const RecordTableCellContainer = ({
|
||||
setContextMenuOpenState(true);
|
||||
};
|
||||
|
||||
const columnDefinition = useContext(ColumnContext);
|
||||
|
||||
const { basePathToShowPage, objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular:
|
||||
columnDefinition?.metadata.objectMetadataNameSingular || '',
|
||||
});
|
||||
const { objectMetadataItem } = useContext(RecordTableContext);
|
||||
const { columnDefinition } = useContext(RecordTableCellContext);
|
||||
const { recordId, pathToShowPage, isSelected } = useContext(
|
||||
RecordTableRowContext,
|
||||
);
|
||||
|
||||
const updateRecord = useContext(RecordUpdateContext);
|
||||
|
||||
if (!columnDefinition || !currentRowId) {
|
||||
if (!columnDefinition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -56,30 +54,29 @@ export const RecordTableCellContainer = ({
|
||||
: TableHotkeyScope.CellEditMode;
|
||||
|
||||
return (
|
||||
<RecoilScope>
|
||||
<ColumnIndexContext.Provider value={cellIndex}>
|
||||
<td onContextMenu={(event) => handleContextMenu(event)}>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recoilScopeId: currentRowId + columnDefinition.label,
|
||||
entityId: currentRowId,
|
||||
fieldDefinition: columnDefinition,
|
||||
useUpdateRecord: () => [updateRecord, {}],
|
||||
hotkeyScope: customHotkeyScope,
|
||||
basePathToShowPage,
|
||||
isLabelIdentifier: isLabelIdentifierField({
|
||||
fieldMetadataItem: {
|
||||
id: columnDefinition.fieldMetadataId,
|
||||
name: columnDefinition.metadata.fieldName,
|
||||
},
|
||||
objectMetadataItem,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordTableCell customHotkeyScope={{ scope: customHotkeyScope }} />
|
||||
</FieldContext.Provider>
|
||||
</td>
|
||||
</ColumnIndexContext.Provider>
|
||||
</RecoilScope>
|
||||
<StyledContainer
|
||||
isSelected={isSelected}
|
||||
onContextMenu={(event) => handleContextMenu(event)}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recoilScopeId: recordId + columnDefinition.label,
|
||||
entityId: recordId,
|
||||
fieldDefinition: columnDefinition,
|
||||
useUpdateRecord: () => [updateRecord, {}],
|
||||
hotkeyScope: customHotkeyScope,
|
||||
basePathToShowPage: pathToShowPage,
|
||||
isLabelIdentifier: isLabelIdentifierField({
|
||||
fieldMetadataItem: {
|
||||
id: columnDefinition.fieldMetadataId,
|
||||
name: columnDefinition.metadata.fieldName,
|
||||
},
|
||||
objectMetadataItem,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordTableCell customHotkeyScope={{ scope: customHotkeyScope }} />
|
||||
</FieldContext.Provider>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecordTableRefContext } from '@/object-record/record-table/contexts/RecordTableRefContext';
|
||||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
|
||||
|
||||
export const RecordTableFirstColumnScrollEffect = () => {
|
||||
const recordTableRef = useContext(RecordTableRefContext);
|
||||
const { recordTableRef } = useContext(RecordTableContext);
|
||||
|
||||
const scrollLeft = useRecoilValue(scrollLeftState);
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { RecordTableRefContext } from '@/object-record/record-table/contexts/RecordTableRefContext';
|
||||
|
||||
export type RecordTableRefContextWrapperProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const RecordTableRefContextWrapper = ({
|
||||
children,
|
||||
}: RecordTableRefContextWrapperProps) => {
|
||||
const tableRef = useRef<HTMLTableElement>(null);
|
||||
|
||||
return (
|
||||
<RecordTableRefContext.Provider value={tableRef}>
|
||||
{children}
|
||||
</RecordTableRefContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -3,35 +3,33 @@ import { useInView } from 'react-intersection-observer';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
||||
import { RecordTableCellContainer } from '@/object-record/record-table/components/RecordTableCellContainer';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
|
||||
import { ColumnContext } from '../contexts/ColumnContext';
|
||||
import { useCurrentRowSelected } from '../record-table-row/hooks/useCurrentRowSelected';
|
||||
|
||||
import { CheckboxCell } from './CheckboxCell';
|
||||
|
||||
export const StyledRow = styled.tr<{ selected: boolean }>`
|
||||
background: ${(props) =>
|
||||
props.selected ? props.theme.accent.quaternary : 'none'};
|
||||
`;
|
||||
|
||||
type RecordTableRowProps = {
|
||||
rowId: string;
|
||||
recordId: string;
|
||||
rowIndex: number;
|
||||
};
|
||||
|
||||
const StyledPlaceholder = styled.td`
|
||||
height: 30px;
|
||||
`;
|
||||
|
||||
export const RecordTableRow = ({ rowId }: RecordTableRowProps) => {
|
||||
const { getVisibleTableColumnsSelector } = useRecordTableStates();
|
||||
export const RecordTableRow = ({ recordId, rowIndex }: RecordTableRowProps) => {
|
||||
const { getVisibleTableColumnsSelector, isRowSelectedFamilyState } =
|
||||
useRecordTableStates();
|
||||
const currentRowSelected = useRecoilValue(isRowSelectedFamilyState(recordId));
|
||||
const { objectMetadataItem } = useContext(RecordTableContext);
|
||||
|
||||
const visibleTableColumns = useRecoilValue(getVisibleTableColumnsSelector());
|
||||
|
||||
const { currentRowSelected } = useCurrentRowSelected();
|
||||
|
||||
const scrollWrapperRef = useContext(ScrollWrapperContext);
|
||||
|
||||
const { ref: elementRef, inView } = useInView({
|
||||
@ -40,34 +38,46 @@ export const RecordTableRow = ({ rowId }: RecordTableRowProps) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledRow
|
||||
ref={elementRef}
|
||||
data-testid={`row-id-${rowId}`}
|
||||
selected={currentRowSelected}
|
||||
data-selectable-id={rowId}
|
||||
<RecordTableRowContext.Provider
|
||||
value={{
|
||||
recordId,
|
||||
rowIndex,
|
||||
pathToShowPage:
|
||||
getBasePathToShowPage({ objectMetadataItem }) + recordId,
|
||||
isSelected: currentRowSelected,
|
||||
}}
|
||||
>
|
||||
{inView ? (
|
||||
<>
|
||||
<td>
|
||||
<CheckboxCell />
|
||||
</td>
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position)
|
||||
.map((column, columnIndex) => {
|
||||
return (
|
||||
<ColumnContext.Provider
|
||||
value={column}
|
||||
key={column.fieldMetadataId}
|
||||
>
|
||||
<RecordTableCellContainer cellIndex={columnIndex} />
|
||||
</ColumnContext.Provider>
|
||||
);
|
||||
})}
|
||||
<td></td>
|
||||
</>
|
||||
) : (
|
||||
<StyledPlaceholder />
|
||||
)}
|
||||
</StyledRow>
|
||||
<tr
|
||||
ref={elementRef}
|
||||
data-testid={`row-id-${recordId}`}
|
||||
data-selectable-id={recordId}
|
||||
>
|
||||
{inView ? (
|
||||
<>
|
||||
<td>
|
||||
<CheckboxCell />
|
||||
</td>
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position)
|
||||
.map((column, columnIndex) => {
|
||||
return (
|
||||
<RecordTableCellContext.Provider
|
||||
value={{
|
||||
columnDefinition: column,
|
||||
columnIndex,
|
||||
}}
|
||||
key={column.fieldMetadataId}
|
||||
>
|
||||
<RecordTableCellContainer />
|
||||
</RecordTableCellContext.Provider>
|
||||
);
|
||||
})}
|
||||
<td></td>
|
||||
</>
|
||||
) : (
|
||||
<StyledPlaceholder />
|
||||
)}
|
||||
</tr>
|
||||
</RecordTableRowContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,11 +3,8 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { RecordTable } from '@/object-record/record-table/components/RecordTable';
|
||||
import { RecordTableFirstColumnScrollEffect } from '@/object-record/record-table/components/RecordTableFirstColumnScrollObserver';
|
||||
import { RecordTableRefContextWrapper } from '@/object-record/record-table/components/RecordTableRefContext';
|
||||
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
@ -66,7 +63,7 @@ const StyledTableContainer = styled.div`
|
||||
`;
|
||||
|
||||
type RecordTableWithWrappersProps = {
|
||||
objectNamePlural: string;
|
||||
objectNameSingular: string;
|
||||
recordTableId: string;
|
||||
viewBarId: string;
|
||||
updateRecordMutation: (params: any) => void;
|
||||
@ -76,7 +73,7 @@ type RecordTableWithWrappersProps = {
|
||||
export const RecordTableWithWrappers = ({
|
||||
updateRecordMutation,
|
||||
createRecord,
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
recordTableId,
|
||||
viewBarId,
|
||||
}: RecordTableWithWrappersProps) => {
|
||||
@ -95,10 +92,6 @@ export const RecordTableWithWrappers = ({
|
||||
recordTableId,
|
||||
});
|
||||
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
@ -112,52 +105,49 @@ export const RecordTableWithWrappers = ({
|
||||
return (
|
||||
<EntityDeleteContext.Provider value={deleteOneRecord}>
|
||||
<ScrollWrapper>
|
||||
<RecordTableRefContextWrapper>
|
||||
<RecordTableFirstColumnScrollEffect />
|
||||
<RecordUpdateContext.Provider value={updateRecordMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer>
|
||||
<div ref={tableBodyRef}>
|
||||
<RecordTable
|
||||
recordTableId={recordTableId}
|
||||
objectNamePlural={objectNamePlural}
|
||||
onColumnsChange={useRecoilCallback(() => (columns) => {
|
||||
persistViewFields(
|
||||
mapColumnDefinitionsToViewFields(columns),
|
||||
);
|
||||
})}
|
||||
createRecord={createRecord}
|
||||
/>
|
||||
<DragSelect
|
||||
dragSelectable={tableBodyRef}
|
||||
onDragSelectionStart={resetTableRowSelection}
|
||||
onDragSelectionChange={setRowSelectedState}
|
||||
/>
|
||||
</div>
|
||||
<RecordTableInternalEffect
|
||||
<RecordUpdateContext.Provider value={updateRecordMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer>
|
||||
<div ref={tableBodyRef}>
|
||||
<RecordTable
|
||||
recordTableId={recordTableId}
|
||||
tableBodyRef={tableBodyRef}
|
||||
objectNameSingular={objectNameSingular}
|
||||
onColumnsChange={useRecoilCallback(() => (columns) => {
|
||||
persistViewFields(
|
||||
mapColumnDefinitionsToViewFields(columns),
|
||||
);
|
||||
})}
|
||||
createRecord={createRecord}
|
||||
/>
|
||||
{!isRecordTableInitialLoading && numberOfTableRows === 0 && (
|
||||
<StyledObjectEmptyContainer>
|
||||
<StyledEmptyObjectTitle>
|
||||
No {foundObjectMetadataItem?.namePlural}
|
||||
</StyledEmptyObjectTitle>
|
||||
<StyledEmptyObjectSubTitle>
|
||||
Create one:
|
||||
</StyledEmptyObjectSubTitle>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={`Add a ${foundObjectMetadataItem?.nameSingular}`}
|
||||
variant={'secondary'}
|
||||
onClick={createRecord}
|
||||
/>
|
||||
</StyledObjectEmptyContainer>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
</StyledTableWithHeader>
|
||||
</RecordUpdateContext.Provider>
|
||||
</RecordTableRefContextWrapper>
|
||||
<DragSelect
|
||||
dragSelectable={tableBodyRef}
|
||||
onDragSelectionStart={resetTableRowSelection}
|
||||
onDragSelectionChange={setRowSelectedState}
|
||||
/>
|
||||
</div>
|
||||
<RecordTableInternalEffect
|
||||
recordTableId={recordTableId}
|
||||
tableBodyRef={tableBodyRef}
|
||||
/>
|
||||
{!isRecordTableInitialLoading && numberOfTableRows === 0 && (
|
||||
<StyledObjectEmptyContainer>
|
||||
<StyledEmptyObjectTitle>
|
||||
No {foundObjectMetadataItem?.namePlural}
|
||||
</StyledEmptyObjectTitle>
|
||||
<StyledEmptyObjectSubTitle>
|
||||
Create one:
|
||||
</StyledEmptyObjectSubTitle>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={`Add a ${foundObjectMetadataItem?.nameSingular}`}
|
||||
variant={'secondary'}
|
||||
onClick={createRecord}
|
||||
/>
|
||||
</StyledObjectEmptyContainer>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
</StyledTableWithHeader>
|
||||
</RecordUpdateContext.Provider>
|
||||
</ScrollWrapper>
|
||||
</EntityDeleteContext.Provider>
|
||||
);
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
export const ColumnContext =
|
||||
createContext<ColumnDefinition<FieldMetadata> | null>(null);
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const ColumnIndexContext = createContext<number>(0);
|
||||
@ -0,0 +1,13 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
|
||||
type RecordTableRowContextProps = {
|
||||
columnDefinition: ColumnDefinition<FieldMetadata>;
|
||||
columnIndex: number;
|
||||
};
|
||||
|
||||
export const RecordTableCellContext = createContext<RecordTableRowContextProps>(
|
||||
{} as RecordTableRowContextProps,
|
||||
);
|
||||
@ -0,0 +1,12 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
type RecordTableContextProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
recordTableRef: React.RefObject<HTMLDivElement>;
|
||||
};
|
||||
|
||||
export const RecordTableContext = createContext<RecordTableContextProps>(
|
||||
{} as RecordTableContextProps,
|
||||
);
|
||||
@ -1,7 +0,0 @@
|
||||
import { createContext, RefObject } from 'react';
|
||||
|
||||
export const RecordTableRefContext = createContext<RefObject<HTMLTableElement>>(
|
||||
{
|
||||
current: null,
|
||||
},
|
||||
);
|
||||
@ -0,0 +1,12 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
type RecordTableRowContextProps = {
|
||||
pathToShowPage: string;
|
||||
recordId: string;
|
||||
rowIndex: number;
|
||||
isSelected: boolean;
|
||||
};
|
||||
|
||||
export const RecordTableRowContext = createContext<RecordTableRowContextProps>(
|
||||
{} as RecordTableRowContextProps,
|
||||
);
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const RowIdContext = createContext<string | null>(null);
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const RowIndexContext = createContext<number>(0);
|
||||
@ -1,31 +0,0 @@
|
||||
import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { useScopedState } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useScopedState';
|
||||
|
||||
export const useRecordTableScopedStates = (recordTableId?: string) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordTableScopeInternalContext,
|
||||
recordTableId,
|
||||
);
|
||||
|
||||
const {
|
||||
getScopedState,
|
||||
getScopedSelector,
|
||||
getScopedFamilyState,
|
||||
getScopedSnapshotValue,
|
||||
getScopedSelectorSnapshotValue,
|
||||
getScopedFamilySnapshotValue,
|
||||
} = useScopedState(scopeId);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
injectStateWithRecordTableScopeId: getScopedState,
|
||||
injectSelectorWithRecordTableScopeId: getScopedSelector,
|
||||
injectFamilyStateWithRecordTableScopeId: getScopedFamilyState,
|
||||
injectSelectorSnapshotValueWithRecordTableScopeId:
|
||||
getScopedSelectorSnapshotValue,
|
||||
injectSnapshotValueWithRecordTableScopeId: getScopedSnapshotValue,
|
||||
injectFamilySnapshotValueWithRecordTableScopeId:
|
||||
getScopedFamilySnapshotValue,
|
||||
};
|
||||
};
|
||||
@ -7,7 +7,6 @@ import { isSoftFocusActiveStateScopeMap } from '@/object-record/record-table/sta
|
||||
import { isSoftFocusOnTableCellFamilyStateScopeMap } from '@/object-record/record-table/states/isSoftFocusOnTableCellFamilyStateScopeMap';
|
||||
import { isTableCellInEditModeFamilyStateScopeMap } from '@/object-record/record-table/states/isTableCellInEditModeFamilyStateScopeMap';
|
||||
import { numberOfTableRowsStateScopeMap } from '@/object-record/record-table/states/numberOfTableRowsStateScopeMap';
|
||||
import { objectMetadataConfigStateScopeMap } from '@/object-record/record-table/states/objectMetadataConfigStateScopeMap';
|
||||
import { onColumnsChangeStateScopeMap } from '@/object-record/record-table/states/onColumnsChangeStateScopeMap';
|
||||
import { onEntityCountChangeStateScopeMap } from '@/object-record/record-table/states/onEntityCountChangeStateScopeMap';
|
||||
import { resizeFieldOffsetStateScopeMap } from '@/object-record/record-table/states/resizeFieldOffsetStateScopeMap';
|
||||
@ -44,10 +43,7 @@ export const useRecordTableStates = (recordTableId?: string) => {
|
||||
getTableFiltersState: getState(tableFiltersStateScopeMap, scopeId),
|
||||
getTableSortsState: getState(tableSortsStateScopeMap, scopeId),
|
||||
getTableColumnsState: getState(tableColumnsStateScopeMap, scopeId),
|
||||
getObjectMetadataConfigState: getState(
|
||||
objectMetadataConfigStateScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
|
||||
getOnColumnsChangeState: getState(onColumnsChangeStateScopeMap, scopeId),
|
||||
getOnEntityCountChangeState: getState(
|
||||
onEntityCountChangeStateScopeMap,
|
||||
|
||||
@ -4,9 +4,11 @@ import { Key } from 'ts-key-enum';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { useGetIsSomeCellInEditModeState } from '@/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { useUpsertRecordFromState } from '../../hooks/useUpsertRecordFromState';
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
@ -33,19 +35,27 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
getTableFiltersState,
|
||||
getTableSortsState,
|
||||
getTableColumnsState,
|
||||
getObjectMetadataConfigState,
|
||||
getOnEntityCountChangeState,
|
||||
getSoftFocusPositionState,
|
||||
getNumberOfTableRowsState,
|
||||
getOnColumnsChangeState,
|
||||
getIsRecordTableInitialLoadingState,
|
||||
getTableLastRowVisibleState,
|
||||
getNumberOfTableColumnsSelector,
|
||||
getSelectedRowIdsSelector,
|
||||
} = useRecordTableStates(recordTableId);
|
||||
|
||||
const setAvailableTableColumns = useSetRecoilState(
|
||||
getAvailableTableColumnsState(),
|
||||
const setAvailableTableColumns = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(columns: ColumnDefinition<FieldMetadata>[]) => {
|
||||
const availableTableColumnsState = getSnapshotValue(
|
||||
snapshot,
|
||||
getAvailableTableColumnsState(),
|
||||
);
|
||||
|
||||
if (isDeeplyEqual(availableTableColumnsState, columns)) {
|
||||
return;
|
||||
}
|
||||
set(getAvailableTableColumnsState(), columns);
|
||||
},
|
||||
[getAvailableTableColumnsState],
|
||||
);
|
||||
|
||||
const setOnEntityCountChange = useSetRecoilState(
|
||||
@ -54,10 +64,6 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
|
||||
const setTableFilters = useSetRecoilState(getTableFiltersState());
|
||||
|
||||
const setObjectMetadataConfig = useSetRecoilState(
|
||||
getObjectMetadataConfigState(),
|
||||
);
|
||||
|
||||
const setTableSorts = useSetRecoilState(getTableSortsState());
|
||||
|
||||
const setTableColumns = useSetRecoilState(getTableColumnsState());
|
||||
@ -113,160 +119,8 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
|
||||
const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId);
|
||||
|
||||
const moveUp = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
let newRowNumber = softFocusPosition.row - 1;
|
||||
|
||||
if (newRowNumber < 0) {
|
||||
newRowNumber = 0;
|
||||
}
|
||||
|
||||
setSoftFocusPosition({
|
||||
...softFocusPosition,
|
||||
row: newRowNumber,
|
||||
});
|
||||
},
|
||||
[getSoftFocusPositionState, setSoftFocusPosition],
|
||||
);
|
||||
|
||||
const moveDown = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
const numberOfTableRows = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableRowsState(),
|
||||
);
|
||||
|
||||
let newRowNumber = softFocusPosition.row + 1;
|
||||
|
||||
if (newRowNumber >= numberOfTableRows) {
|
||||
newRowNumber = numberOfTableRows - 1;
|
||||
}
|
||||
|
||||
setSoftFocusPosition({
|
||||
...softFocusPosition,
|
||||
row: newRowNumber,
|
||||
});
|
||||
},
|
||||
[
|
||||
getNumberOfTableRowsState,
|
||||
setSoftFocusPosition,
|
||||
getSoftFocusPositionState,
|
||||
],
|
||||
);
|
||||
|
||||
const moveRight = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
const numberOfTableColumns = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableColumnsSelector(),
|
||||
);
|
||||
|
||||
const numberOfTableRows = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableRowsState(),
|
||||
);
|
||||
const currentColumnNumber = softFocusPosition.column;
|
||||
const currentRowNumber = softFocusPosition.row;
|
||||
|
||||
const isLastRowAndLastColumn =
|
||||
currentColumnNumber === numberOfTableColumns - 1 &&
|
||||
currentRowNumber === numberOfTableRows - 1;
|
||||
|
||||
const isLastColumnButNotLastRow =
|
||||
currentColumnNumber === numberOfTableColumns - 1 &&
|
||||
currentRowNumber !== numberOfTableRows - 1;
|
||||
|
||||
const isNotLastColumn =
|
||||
currentColumnNumber !== numberOfTableColumns - 1;
|
||||
|
||||
if (isLastRowAndLastColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNotLastColumn) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber,
|
||||
column: currentColumnNumber + 1,
|
||||
});
|
||||
} else if (isLastColumnButNotLastRow) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber + 1,
|
||||
column: 0,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
getSoftFocusPositionState,
|
||||
getNumberOfTableColumnsSelector,
|
||||
getNumberOfTableRowsState,
|
||||
setSoftFocusPosition,
|
||||
],
|
||||
);
|
||||
|
||||
const moveLeft = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
const numberOfTableColumns = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableColumnsSelector(),
|
||||
);
|
||||
|
||||
const currentColumnNumber = softFocusPosition.column;
|
||||
const currentRowNumber = softFocusPosition.row;
|
||||
|
||||
const isFirstRowAndFirstColumn =
|
||||
currentColumnNumber === 0 && currentRowNumber === 0;
|
||||
|
||||
const isFirstColumnButNotFirstRow =
|
||||
currentColumnNumber === 0 && currentRowNumber > 0;
|
||||
|
||||
const isNotFirstColumn = currentColumnNumber > 0;
|
||||
|
||||
if (isFirstRowAndFirstColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNotFirstColumn) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber,
|
||||
column: currentColumnNumber - 1,
|
||||
});
|
||||
} else if (isFirstColumnButNotFirstRow) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber - 1,
|
||||
column: numberOfTableColumns - 1,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
getNumberOfTableColumnsSelector,
|
||||
getSoftFocusPositionState,
|
||||
setSoftFocusPosition,
|
||||
],
|
||||
);
|
||||
const { moveDown, moveLeft, moveRight, moveUp } =
|
||||
useRecordTableMoveFocus(recordTableId);
|
||||
|
||||
const useMapKeyboardToSoftFocus = () => {
|
||||
const disableSoftFocus = useDisableSoftFocus(recordTableId);
|
||||
@ -333,7 +187,6 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
setAvailableTableColumns,
|
||||
setTableFilters,
|
||||
setTableSorts,
|
||||
setObjectMetadataConfig,
|
||||
setOnEntityCountChange,
|
||||
setRecordTableData,
|
||||
setTableColumns,
|
||||
|
||||
@ -0,0 +1,183 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition';
|
||||
|
||||
export const useRecordTableMoveFocus = (recordTableId?: string) => {
|
||||
const {
|
||||
scopeId,
|
||||
getSoftFocusPositionState,
|
||||
getNumberOfTableRowsState,
|
||||
getNumberOfTableColumnsSelector,
|
||||
getSelectedRowIdsSelector,
|
||||
} = useRecordTableStates(recordTableId);
|
||||
|
||||
const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId);
|
||||
|
||||
const moveUp = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
let newRowNumber = softFocusPosition.row - 1;
|
||||
|
||||
if (newRowNumber < 0) {
|
||||
newRowNumber = 0;
|
||||
}
|
||||
|
||||
setSoftFocusPosition({
|
||||
...softFocusPosition,
|
||||
row: newRowNumber,
|
||||
});
|
||||
},
|
||||
[getSoftFocusPositionState, setSoftFocusPosition],
|
||||
);
|
||||
|
||||
const moveDown = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
const numberOfTableRows = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableRowsState(),
|
||||
);
|
||||
|
||||
let newRowNumber = softFocusPosition.row + 1;
|
||||
|
||||
if (newRowNumber >= numberOfTableRows) {
|
||||
newRowNumber = numberOfTableRows - 1;
|
||||
}
|
||||
|
||||
setSoftFocusPosition({
|
||||
...softFocusPosition,
|
||||
row: newRowNumber,
|
||||
});
|
||||
},
|
||||
[
|
||||
getNumberOfTableRowsState,
|
||||
setSoftFocusPosition,
|
||||
getSoftFocusPositionState,
|
||||
],
|
||||
);
|
||||
|
||||
const moveRight = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
const numberOfTableColumns = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableColumnsSelector(),
|
||||
);
|
||||
|
||||
const numberOfTableRows = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableRowsState(),
|
||||
);
|
||||
const currentColumnNumber = softFocusPosition.column;
|
||||
const currentRowNumber = softFocusPosition.row;
|
||||
|
||||
const isLastRowAndLastColumn =
|
||||
currentColumnNumber === numberOfTableColumns - 1 &&
|
||||
currentRowNumber === numberOfTableRows - 1;
|
||||
|
||||
const isLastColumnButNotLastRow =
|
||||
currentColumnNumber === numberOfTableColumns - 1 &&
|
||||
currentRowNumber !== numberOfTableRows - 1;
|
||||
|
||||
const isNotLastColumn =
|
||||
currentColumnNumber !== numberOfTableColumns - 1;
|
||||
|
||||
if (isLastRowAndLastColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNotLastColumn) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber,
|
||||
column: currentColumnNumber + 1,
|
||||
});
|
||||
} else if (isLastColumnButNotLastRow) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber + 1,
|
||||
column: 0,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
getSoftFocusPositionState,
|
||||
getNumberOfTableColumnsSelector,
|
||||
getNumberOfTableRowsState,
|
||||
setSoftFocusPosition,
|
||||
],
|
||||
);
|
||||
|
||||
const moveLeft = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const softFocusPosition = getSnapshotValue(
|
||||
snapshot,
|
||||
getSoftFocusPositionState(),
|
||||
);
|
||||
|
||||
const numberOfTableColumns = getSnapshotValue(
|
||||
snapshot,
|
||||
getNumberOfTableColumnsSelector(),
|
||||
);
|
||||
|
||||
const currentColumnNumber = softFocusPosition.column;
|
||||
const currentRowNumber = softFocusPosition.row;
|
||||
|
||||
const isFirstRowAndFirstColumn =
|
||||
currentColumnNumber === 0 && currentRowNumber === 0;
|
||||
|
||||
const isFirstColumnButNotFirstRow =
|
||||
currentColumnNumber === 0 && currentRowNumber > 0;
|
||||
|
||||
const isNotFirstColumn = currentColumnNumber > 0;
|
||||
|
||||
if (isFirstRowAndFirstColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNotFirstColumn) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber,
|
||||
column: currentColumnNumber - 1,
|
||||
});
|
||||
} else if (isFirstColumnButNotFirstRow) {
|
||||
setSoftFocusPosition({
|
||||
row: currentRowNumber - 1,
|
||||
column: numberOfTableColumns - 1,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
getNumberOfTableColumnsSelector,
|
||||
getSoftFocusPositionState,
|
||||
setSoftFocusPosition,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
moveDown,
|
||||
moveLeft,
|
||||
moveRight,
|
||||
moveUp,
|
||||
setSoftFocusPosition,
|
||||
getSelectedRowIdsSelector,
|
||||
};
|
||||
};
|
||||
@ -4,22 +4,20 @@ import { FieldDisplay } from '@/object-record/record-field/components/FieldDispl
|
||||
import { FieldInput } from '@/object-record/record-field/components/FieldInput';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus';
|
||||
import { RecordTableCellContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellContainer';
|
||||
import { useCloseRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { useRecordTable } from '../../hooks/useRecordTable';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { TableCellContainer } from './RecordTableCellContainer';
|
||||
|
||||
export const RecordTableCell = ({
|
||||
customHotkeyScope,
|
||||
}: {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
}) => {
|
||||
const { closeTableCell } = useTableCell();
|
||||
const { closeTableCell } = useCloseRecordTableCell();
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const { moveLeft, moveRight, moveDown } = useRecordTable();
|
||||
const { moveLeft, moveRight, moveDown } = useRecordTableMoveFocus();
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
@ -65,7 +63,7 @@ export const RecordTableCell = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<TableCellContainer
|
||||
<RecordTableCellContainer
|
||||
editHotkeyScope={customHotkeyScope}
|
||||
editModeContent={
|
||||
<FieldInput
|
||||
|
||||
@ -5,18 +5,18 @@ import { useRecoilValue } from 'recoil';
|
||||
import { useGetButtonIcon } from '@/object-record/record-field/hooks/useGetButtonIcon';
|
||||
import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEmpty';
|
||||
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { useGetIsSomeCellInEditModeState } from '@/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode';
|
||||
import { useOpenRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
|
||||
import { IconArrowUpRight } from '@/ui/display/icon';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
import { useCurrentTableCellEditMode } from '../hooks/useCurrentTableCellEditMode';
|
||||
import { useIsSoftFocusOnCurrentTableCell } from '../hooks/useIsSoftFocusOnCurrentTableCell';
|
||||
import { useMoveSoftFocusToCurrentCellOnHover } from '../hooks/useMoveSoftFocusToCurrentCellOnHover';
|
||||
import { useSetSoftFocusOnCurrentTableCell } from '../hooks/useSetSoftFocusOnCurrentTableCell';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { RecordTableCellButton } from './RecordTableCellButton';
|
||||
import { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode';
|
||||
@ -33,7 +33,7 @@ const StyledCellBaseContainer = styled.div`
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export type TableCellContainerProps = {
|
||||
export type RecordTableCellContainerProps = {
|
||||
editModeContent: ReactElement;
|
||||
nonEditModeContent: ReactElement;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
@ -49,28 +49,26 @@ const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
||||
scope: TableHotkeyScope.CellEditMode,
|
||||
};
|
||||
|
||||
export const TableCellContainer = ({
|
||||
export const RecordTableCellContainer = ({
|
||||
editModeHorizontalAlign = 'left',
|
||||
editModeVerticalPosition = 'over',
|
||||
editModeContent,
|
||||
nonEditModeContent,
|
||||
editHotkeyScope,
|
||||
}: TableCellContainerProps) => {
|
||||
const { isCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
|
||||
const { isSomeCellInEditModeState } = useRecordTable();
|
||||
const isSomeCellInEditMode = useRecoilValue(isSomeCellInEditModeState());
|
||||
|
||||
}: RecordTableCellContainerProps) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const { isCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
const isSomeCellInEditModeState = useGetIsSomeCellInEditModeState();
|
||||
const isSomeCellInEditMode = useRecoilValue(isSomeCellInEditModeState());
|
||||
|
||||
const moveSoftFocusToCurrentCellOnHover =
|
||||
useMoveSoftFocusToCurrentCellOnHover();
|
||||
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentTableCell();
|
||||
|
||||
const setSoftFocusOnCurrentTableCell = useSetSoftFocusOnCurrentTableCell();
|
||||
|
||||
const { openTableCell } = useTableCell();
|
||||
const { openTableCell } = useOpenRecordTableCell();
|
||||
|
||||
const handleButtonClick = () => {
|
||||
setSoftFocusOnCurrentTableCell();
|
||||
@ -90,14 +88,11 @@ export const TableCellContainer = ({
|
||||
|
||||
const editModeContentOnly = useIsFieldInputOnly();
|
||||
|
||||
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
||||
|
||||
const isEmpty = useIsFieldEmpty();
|
||||
|
||||
const isFirstColumn = useContext(ColumnIndexContext) === 0;
|
||||
|
||||
const { columnIndex } = useContext(RecordTableCellContext);
|
||||
const isFirstColumn = columnIndex === 0;
|
||||
const customButtonIcon = useGetButtonIcon();
|
||||
|
||||
const buttonIcon = isFirstColumn ? IconArrowUpRight : customButtonIcon;
|
||||
|
||||
const showButton =
|
||||
@ -105,7 +100,7 @@ export const TableCellContainer = ({
|
||||
hasSoftFocus &&
|
||||
!isCurrentTableCellInEditMode &&
|
||||
!editModeContentOnly &&
|
||||
(!isFirstColumnCell || !isEmpty);
|
||||
(!isFirstColumn || !isEmpty);
|
||||
|
||||
return (
|
||||
<CellHotkeyScopeContext.Provider
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
|
||||
import { useOpenRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
|
||||
|
||||
import { useSetSoftFocusOnCurrentTableCell } from '../hooks/useSetSoftFocusOnCurrentTableCell';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer';
|
||||
|
||||
@ -12,7 +12,7 @@ export const RecordTableCellDisplayMode = ({
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
|
||||
const { openTableCell } = useTableCell();
|
||||
const { openTableCell } = useOpenRecordTableCell();
|
||||
|
||||
const handleClick = () => {
|
||||
setSoftFocusOnCurrentCell();
|
||||
|
||||
@ -3,11 +3,11 @@ import { Key } from 'ts-key-enum';
|
||||
|
||||
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
|
||||
import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput';
|
||||
import { useOpenRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
|
||||
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer';
|
||||
|
||||
@ -16,7 +16,7 @@ type RecordTableCellSoftFocusModeProps = PropsWithChildren<unknown>;
|
||||
export const RecordTableCellSoftFocusMode = ({
|
||||
children,
|
||||
}: RecordTableCellSoftFocusModeProps) => {
|
||||
const { openTableCell } = useTableCell();
|
||||
const { openTableCell } = useOpenRecordTableCell();
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
const toggleEditOnlyInput = useToggleEditOnlyInput();
|
||||
|
||||
@ -1,58 +1,30 @@
|
||||
import { useContext } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEmpty';
|
||||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
|
||||
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
|
||||
import { useCloseCurrentTableCellInEditMode } from '../../hooks/internal/useCloseCurrentTableCellInEditMode';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
import { useCurrentTableCellEditMode } from './useCurrentTableCellEditMode';
|
||||
export const useCloseRecordTableCell = () => {
|
||||
const { getTableRowIdsState } = useRecordTableStates();
|
||||
const { columnIndex } = useContext(RecordTableCellContext);
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
const deleteOneRecord = useContext(EntityDeleteContext);
|
||||
|
||||
export const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
||||
scope: TableHotkeyScope.CellEditMode,
|
||||
};
|
||||
|
||||
export const useTableCell = () => {
|
||||
const { getObjectMetadataConfigState, getTableRowIdsState } =
|
||||
useRecordTableStates();
|
||||
|
||||
const { leaveTableFocus } = useRecordTable();
|
||||
|
||||
const objectMetadataConfig = useRecoilValue(getObjectMetadataConfigState());
|
||||
|
||||
const basePathToShowPage = objectMetadataConfig?.basePathToShowPage;
|
||||
|
||||
const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { setDragSelectionStartEnabled } = useDragSelect();
|
||||
|
||||
const closeCurrentTableCellInEditMode = useCloseCurrentTableCellInEditMode();
|
||||
|
||||
const customCellHotkeyScope = useContext(CellHotkeyScopeContext);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
||||
|
||||
const isEmpty = useIsFieldEmpty();
|
||||
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const deleteOneRecord = useContext(EntityDeleteContext);
|
||||
const {
|
||||
initDraftValue: initFieldInputDraftValue,
|
||||
getDraftValueSelector: getFieldInputDraftValueSelector,
|
||||
isDraftValueEmpty: isCurrentFieldInputValueEmpty,
|
||||
} = useRecordFieldInput(
|
||||
@ -63,34 +35,14 @@ export const useTableCell = () => {
|
||||
getFieldInputDraftValueSelector(),
|
||||
);
|
||||
|
||||
const isFirstColumnCell = columnIndex === 0;
|
||||
|
||||
const deleteRow = useRecoilCallback(({ snapshot }) => async () => {
|
||||
const tableRowIds = getSnapshotValue(snapshot, getTableRowIdsState());
|
||||
|
||||
await deleteOneRecord(tableRowIds[0]);
|
||||
});
|
||||
|
||||
const openTableCell = (options?: { initialValue?: string }) => {
|
||||
if (isFirstColumnCell && !isEmpty && basePathToShowPage) {
|
||||
leaveTableFocus();
|
||||
navigate(`${basePathToShowPage}${entityId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
setDragSelectionStartEnabled(false);
|
||||
setCurrentTableCellInEditMode();
|
||||
|
||||
initFieldInputDraftValue(options?.initialValue);
|
||||
|
||||
if (customCellHotkeyScope) {
|
||||
setHotkeyScope(
|
||||
customCellHotkeyScope.scope,
|
||||
customCellHotkeyScope.customScopes,
|
||||
);
|
||||
} else {
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes);
|
||||
}
|
||||
};
|
||||
|
||||
const closeTableCell = async () => {
|
||||
setDragSelectionStartEnabled(true);
|
||||
closeCurrentTableCellInEditMode();
|
||||
@ -106,6 +58,5 @@ export const useTableCell = () => {
|
||||
|
||||
return {
|
||||
closeTableCell,
|
||||
openTableCell,
|
||||
};
|
||||
};
|
||||
@ -1,19 +1,20 @@
|
||||
import { useContext, useMemo } from 'react';
|
||||
|
||||
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
|
||||
import { RowIndexContext } from '../../contexts/RowIndexContext';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
|
||||
import { TableCellPosition } from '../../types/TableCellPosition';
|
||||
|
||||
export const useCurrentTableCellPosition = () => {
|
||||
const currentRowNumber = useContext(RowIndexContext);
|
||||
const currentColumnNumber = useContext(ColumnIndexContext);
|
||||
const { rowIndex } = useContext(RecordTableRowContext);
|
||||
const { columnIndex } = useContext(RecordTableCellContext);
|
||||
|
||||
const currentTableCellPosition: TableCellPosition = useMemo(
|
||||
() => ({
|
||||
column: currentColumnNumber,
|
||||
row: currentRowNumber,
|
||||
column: columnIndex,
|
||||
row: rowIndex,
|
||||
}),
|
||||
[currentColumnNumber, currentRowNumber],
|
||||
[columnIndex, rowIndex],
|
||||
);
|
||||
|
||||
return currentTableCellPosition;
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
import { useContext } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEmpty';
|
||||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus';
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
import { useCurrentTableCellEditMode } from './useCurrentTableCellEditMode';
|
||||
|
||||
export const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
||||
scope: TableHotkeyScope.CellEditMode,
|
||||
};
|
||||
|
||||
export const useOpenRecordTableCell = () => {
|
||||
const { pathToShowPage } = useContext(RecordTableRowContext);
|
||||
|
||||
const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { setDragSelectionStartEnabled } = useDragSelect();
|
||||
|
||||
const customCellHotkeyScope = useContext(CellHotkeyScopeContext);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const leaveTableFocus = useLeaveTableFocus();
|
||||
|
||||
const { columnIndex } = useContext(RecordTableCellContext);
|
||||
const isFirstColumnCell = columnIndex === 0;
|
||||
const isEmpty = useIsFieldEmpty();
|
||||
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const { initDraftValue: initFieldInputDraftValue } = useRecordFieldInput(
|
||||
`${entityId}-${fieldDefinition?.metadata?.fieldName}`,
|
||||
);
|
||||
|
||||
const openTableCell = useRecoilCallback(
|
||||
() => (options?: { initialValue?: string }) => {
|
||||
if (isFirstColumnCell && !isEmpty) {
|
||||
leaveTableFocus();
|
||||
navigate(pathToShowPage);
|
||||
return;
|
||||
}
|
||||
|
||||
setDragSelectionStartEnabled(false);
|
||||
setCurrentTableCellInEditMode();
|
||||
|
||||
initFieldInputDraftValue(options?.initialValue);
|
||||
|
||||
if (customCellHotkeyScope) {
|
||||
setHotkeyScope(
|
||||
customCellHotkeyScope.scope,
|
||||
customCellHotkeyScope.customScopes,
|
||||
);
|
||||
} else {
|
||||
setHotkeyScope(
|
||||
DEFAULT_CELL_SCOPE.scope,
|
||||
DEFAULT_CELL_SCOPE.customScopes,
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
isFirstColumnCell,
|
||||
isEmpty,
|
||||
leaveTableFocus,
|
||||
navigate,
|
||||
pathToShowPage,
|
||||
setDragSelectionStartEnabled,
|
||||
setCurrentTableCellInEditMode,
|
||||
initFieldInputDraftValue,
|
||||
customCellHotkeyScope,
|
||||
setHotkeyScope,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
openTableCell,
|
||||
};
|
||||
};
|
||||
@ -1,14 +1,14 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { useSetSoftFocusPosition } from '@/object-record/record-table/hooks/internal/useSetSoftFocusPosition';
|
||||
import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
export const useSetSoftFocus = () => {
|
||||
const { setSoftFocusPosition } = useRecordTable();
|
||||
const setSoftFocusPosition = useSetSoftFocusPosition();
|
||||
|
||||
const { getIsSoftFocusActiveState } = useRecordTableStates();
|
||||
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
import { RowIdContext } from '../../contexts/RowIdContext';
|
||||
|
||||
export const useCurrentRowSelected = () => {
|
||||
const currentRowId = useContext(RowIdContext);
|
||||
|
||||
const { isRowSelectedFamilyState } = useRecordTableStates();
|
||||
|
||||
const isRowSelected = useRecoilValue(
|
||||
isRowSelectedFamilyState(currentRowId ?? ''),
|
||||
);
|
||||
|
||||
const setCurrentRowSelected = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(newSelectedState: boolean) => {
|
||||
if (!currentRowId) return;
|
||||
|
||||
const isRowSelected = getSnapshotValue(
|
||||
snapshot,
|
||||
isRowSelectedFamilyState(currentRowId),
|
||||
);
|
||||
|
||||
if (newSelectedState && !isRowSelected) {
|
||||
set(isRowSelectedFamilyState(currentRowId), true);
|
||||
} else if (!newSelectedState && isRowSelected) {
|
||||
set(isRowSelectedFamilyState(currentRowId), false);
|
||||
}
|
||||
},
|
||||
[currentRowId, isRowSelectedFamilyState],
|
||||
);
|
||||
|
||||
return {
|
||||
currentRowSelected: isRowSelected,
|
||||
setCurrentRowSelected,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
export const useSetCurrentRowSelected = () => {
|
||||
const { recordId } = useContext(RecordTableRowContext);
|
||||
|
||||
const { isRowSelectedFamilyState } = useRecordTableStates();
|
||||
|
||||
const setCurrentRowSelected = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(newSelectedState: boolean) => {
|
||||
const isRowSelected = getSnapshotValue(
|
||||
snapshot,
|
||||
isRowSelectedFamilyState(recordId),
|
||||
);
|
||||
|
||||
if (isRowSelected !== newSelectedState) {
|
||||
set(isRowSelectedFamilyState(recordId), newSelectedState);
|
||||
}
|
||||
},
|
||||
[recordId, isRowSelectedFamilyState],
|
||||
);
|
||||
|
||||
return {
|
||||
setCurrentRowSelected,
|
||||
};
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
import { ObjectMetadataConfig } from '@/object-record/record-table/types/ObjectMetadataConfig';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const objectMetadataConfigStateScopeMap =
|
||||
createStateScopeMap<ObjectMetadataConfig | null>({
|
||||
key: 'objectMetadataConfigStateScopeMap',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
import { availableTableColumnsStateScopeMap } from '@/object-record/record-table/states/availableTableColumnsStateScopeMap';
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { availableTableColumnsStateScopeMap } from '../availableTableColumnsStateScopeMap';
|
||||
import { tableColumnsStateScopeMap } from '../tableColumnsStateScopeMap';
|
||||
|
||||
export const visibleTableColumnsSelectorScopeMap =
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
export type ObjectMetadataConfig = {
|
||||
labelIdentifierFieldMetadataId: string;
|
||||
basePathToShowPage: string;
|
||||
};
|
||||
@ -15,6 +15,7 @@ const StyledContainer = styled.div`
|
||||
|
||||
export const SignInBackgroundMockContainer = () => {
|
||||
const objectNamePlural = 'companies';
|
||||
const objectNameSingular = 'company';
|
||||
const recordTableId = 'sign-up-mock-record-table-id';
|
||||
const viewBarId = 'companies-mock';
|
||||
|
||||
@ -33,7 +34,7 @@ export const SignInBackgroundMockContainer = () => {
|
||||
viewId={viewBarId}
|
||||
/>
|
||||
<RecordTableWithWrappers
|
||||
objectNamePlural={objectNamePlural}
|
||||
objectNameSingular={objectNameSingular}
|
||||
recordTableId={recordTableId}
|
||||
viewBarId={viewBarId}
|
||||
createRecord={async () => {}}
|
||||
|
||||
@ -30,7 +30,6 @@ export const SignInBackgroundMockContainerEffect = ({
|
||||
setOnEntityCountChange,
|
||||
setRecordTableData,
|
||||
setTableColumns,
|
||||
setObjectMetadataConfig,
|
||||
} = useRecordTable({
|
||||
recordTableId,
|
||||
});
|
||||
@ -81,10 +80,6 @@ export const SignInBackgroundMockContainerEffect = ({
|
||||
setTableColumns,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
setObjectMetadataConfig?.(mockIdentifier);
|
||||
}, [setObjectMetadataConfig]);
|
||||
|
||||
const { setActionBarEntries, setContextMenuEntries } =
|
||||
useRecordTableContextMenuEntries({
|
||||
objectNamePlural,
|
||||
@ -104,8 +99,3 @@ export const SignInBackgroundMockContainerEffect = ({
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const mockIdentifier = {
|
||||
basePathToShowPage: '/object/company/',
|
||||
labelIdentifierFieldMetadataId: '20202020-6d30-4111-9f40-b4301906fd3c',
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { StyledRow } from '@/object-record/record-table/components/RecordTableRow';
|
||||
import { grayScale } from '@/ui/theme/constants/colors';
|
||||
|
||||
type FetchMoreLoaderProps = {
|
||||
@ -30,12 +29,12 @@ export const FetchMoreLoader = ({
|
||||
return (
|
||||
<tbody ref={tbodyRef}>
|
||||
{loading && (
|
||||
<StyledRow selected={false}>
|
||||
<tr>
|
||||
<td colSpan={7}>
|
||||
<StyledText>Loading more...</StyledText>
|
||||
</td>
|
||||
<td colSpan={7} />
|
||||
</StyledRow>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
);
|
||||
|
||||
@ -9,8 +9,8 @@ import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObj
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { RecordIndexContainer } from '@/object-record/record-index/components/RecordIndexContainer';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
|
||||
import { useSelectedTableCellEditMode } from '@/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useTableCell';
|
||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||
|
||||
Reference in New Issue
Block a user