Skip to content

Commit

Permalink
#845 Fix table invalidation for classes with required properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Polleps committed Apr 15, 2024
1 parent 4a43bdd commit cad74e8
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { Details } from '../Details';
import { useEffect, useState } from 'react';
import { getIconForClass } from '../../views/FolderPage/iconMap';
import styled from 'styled-components';
import { styled } from 'styled-components';

const shouldBeRendered = (resource: Resource) =>
resource.hasClasses(dataBrowser.classes.folder) ||
Expand Down
16 changes: 10 additions & 6 deletions browser/data-browser/src/components/TableEditor/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,26 @@ export function IndexCell({
onExpand,
...props
}: React.PropsWithChildren<IndexCellProps>): JSX.Element {
const { markings } = useTableEditorContext();

const marking = markings.get(props.rowIndex);

return (
<StyledIndexCell role='rowheader' {...props}>
<StyledIndexCell role='rowheader' {...props} hasMarking={!!marking}>
<IconButton
title='Open resource'
onClick={() => onExpand(props.rowIndex)}
>
<FaExpandAlt />
</IconButton>
<IndexNumber>{children}</IndexNumber>
{marking ? marking : <IndexNumber>{children}</IndexNumber>}
</StyledIndexCell>
);
}

const IndexNumber = styled.span``;

const StyledIndexCell = styled(Cell)`
const StyledIndexCell = styled(Cell)<{ hasMarking: boolean }>`
justify-content: flex-end !important;
color: ${p => p.theme.colors.textLight};
Expand All @@ -246,9 +250,9 @@ const StyledIndexCell = styled(Cell)`
display: none;
}
&:hover button,
&:focus-within button {
display: block;
&:not([data-hasmarking='true']):hover button,
&:not([data-hasmarking='true']):focus-within button {
display: ${p => (p.hasMarking ? 'none' : 'block')};
}
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useState,
createContext,
useContext,
ReactElement,
} from 'react';
import { FixedSizeList } from 'react-window';
import { EventManager } from '../../helpers/EventManager';
Expand Down Expand Up @@ -63,14 +64,16 @@ export interface TableEditorContext {
clearCell: () => void;
clearRow: (index: number) => void;
enterEditModeWithCharacter: (key: string) => void;
markings: Map<number, ReactElement>;
setMarkings: React.Dispatch<React.SetStateAction<Map<number, ReactElement>>>;
registerEventListener<T extends TableEvent>(
event: T,
cb: TableEventHandlers[T],
): () => void;
emitInteractionsFired(interactions: KeyboardInteraction[]): void;
}

const initial = {
const initial: TableEditorContext = {
mouseDown: false,
setMouseDown: emptySetState,
tableRef: { current: null },
Expand All @@ -94,6 +97,8 @@ const initial = {
clearCell: () => undefined,
clearRow: (_: number) => undefined,
enterEditModeWithCharacter: (_: string) => undefined,
markings: new Map<number, ReactElement>(),
setMarkings: emptySetState,
registerEventListener: () => () => undefined,
emitInteractionsFired: () => undefined,
};
Expand Down Expand Up @@ -125,6 +130,10 @@ export function TableEditorContextProvider({

const [indicatorHidden, setIndicatorHidden] = useState(false);

const [markings, setMarkings] = useState<Map<number, ReactElement>>(
new Map(),
);

const activeCellRef = useRef<HTMLDivElement | null>(null);
const multiSelectCornerCellRef = useRef<HTMLDivElement | null>(null);

Expand Down Expand Up @@ -196,6 +205,8 @@ export function TableEditorContextProvider({
clearRow,
enterEditModeWithCharacter,
emitInteractionsFired,
markings,
setMarkings,
}),
[
disabledKeyboardInteractions,
Expand All @@ -210,6 +221,7 @@ export function TableEditorContextProvider({
cursorMode,
emitInteractionsFired,
mouseDown,
markings,
],
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { core, useStore, server, dataBrowser, Resource } from '@tomic/react';
import { core, useStore, server, dataBrowser } from '@tomic/react';
import { useState, useCallback, FormEvent, FC, useEffect } from 'react';
import { styled } from 'styled-components';
import { stringToSlug } from '../../../../../helpers/stringToSlug';
Expand Down
2 changes: 2 additions & 0 deletions browser/data-browser/src/styling.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const buildTheme = (darkMode: boolean, mainIn: string): DefaultTheme => {
textLight2: darkMode ? darken(0.8)(text) : lighten(0.8)(text),
alert: '#cf5b5b',
alertLight: '#e66f6f',
warning: '#f5a623',
},
animation: {
duration: `${animationDuration}ms`,
Expand Down Expand Up @@ -159,6 +160,7 @@ declare module 'styled-components' {
/** Error / warning color */
alert: string;
alertLight: string;
warning: string;
};
animation: {
duration: string;
Expand Down
46 changes: 9 additions & 37 deletions browser/data-browser/src/views/TablePage/TableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,7 @@ import {
useDebouncedCallback,
useValue,
} from '@tomic/react';
import {
startTransition,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { useCallback, useContext, useMemo } from 'react';
import { Cell } from '../../components/TableEditor';
import { CellAlign } from '../../components/TableEditor/Cell';
import {
Expand All @@ -34,7 +27,7 @@ interface TableCell {
rowIndex: number;
resource: Resource;
property: Property;
invalidateTable?: () => void;
onEditNextRow?: () => void;
}

function useIsEditing(row: number, column: number) {
Expand All @@ -59,9 +52,8 @@ export function TableCell({
rowIndex,
resource,
property,
invalidateTable,
onEditNextRow,
}: TableCell): JSX.Element {
const [markForInvalidate, setMarkForInvalidate] = useState(false);
const { setActiveCell } = useTableEditorContext();
const { addItemsToHistoryStack } = useContext(TablePageContext);
const [save, savePending] = useDebouncedCallback(
Expand Down Expand Up @@ -93,7 +85,6 @@ export function TableCell({
async (v: JSONValue) => {
if (!createdAt) {
await setCreatedAt(Date.now());
setMarkForInvalidate(true);
}

addItemsToHistoryStack(
Expand All @@ -115,32 +106,13 @@ export function TableCell({
);

const handleEditNextRow = useCallback(() => {
if (markForInvalidate && !savePending) {
startTransition(() => {
setMarkForInvalidate(false);
invalidateTable?.();
setTimeout(() => {
setActiveCell(rowIndex + 1, columnIndex);
}, 0);
});
}
}, [
markForInvalidate,
savePending,
invalidateTable,
setActiveCell,
rowIndex,
columnIndex,
]);

useEffect(() => {
if (markForInvalidate && !isEditing && !savePending) {
startTransition(() => {
setMarkForInvalidate(false);
invalidateTable?.();
});
if (!savePending) {
onEditNextRow?.();
setTimeout(() => {
setActiveCell(rowIndex + 1, columnIndex);
}, 0);
}
}, [isEditing, markForInvalidate, savePending, invalidateTable]);
}, [savePending, setActiveCell, rowIndex, columnIndex]);

return (
<Cell
Expand Down
43 changes: 42 additions & 1 deletion browser/data-browser/src/views/TablePage/TableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,59 @@ import {
import { TableCell } from './TableCell';
import { randomSubject } from '../../helpers/randomString';
import { styled, keyframes } from 'styled-components';
import { useTableEditorContext } from '../../components/TableEditor/TableEditorContext';
import { FaTriangleExclamation } from 'react-icons/fa6';
import { useTableInvalidation } from './useTableInvalidation';

interface TableRowProps {
collection: Collection;
index: number;
columns: Property[];
}

const WarningIcon = styled(FaTriangleExclamation)`
color: ${p => p.theme.colors.warning};
`;

const saveWarning = (
<WarningIcon title='Row is incomplete or has invalid data' />
);

const TableCellMemo = memo(TableCell);

function useMarkings(row: Resource, index: number) {
const { setMarkings } = useTableEditorContext();

useEffect(() => {
if (row.commitError) {
setMarkings(markings => {
const newMap = new Map(markings);
newMap.set(index, saveWarning);

return newMap;
});
}

return () => {
setMarkings(markings => {
const newMap = new Map(markings);
newMap.delete(index);

return newMap;
});
};
}, [row, index]);
}

export function TableRow({
collection,
index,
columns,
}: TableRowProps): JSX.Element {
const resource = useMemberFromCollection(collection, index);

useMarkings(resource, index);

if (resource.subject === unknownSubject) {
return (
<>
Expand Down Expand Up @@ -76,6 +113,10 @@ export function TableNewRow({

const resource = useResource(subject, resourceOpts);

const onEditNextRow = useTableInvalidation(resource, invalidateTable);

useMarkings(resource, index);

useEffect(() => {
if (resource.subject === unknownSubject) {
return;
Expand Down Expand Up @@ -108,7 +149,7 @@ export function TableNewRow({
columnIndex={cIndex + 1}
resource={resource}
property={column}
invalidateTable={invalidateTable}
onEditNextRow={onEditNextRow}
/>
))}
</>
Expand Down
39 changes: 39 additions & 0 deletions browser/data-browser/src/views/TablePage/useTableInvalidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Resource, StoreEvents, useStore } from '@tomic/react';
import { useCallback, useEffect, useState } from 'react';
import {
CursorMode,
useTableEditorContext,
} from '../../components/TableEditor/TableEditorContext';

export function useTableInvalidation(
resource: Resource,
invalidateTable: () => void,
) {
const store = useStore();
const { cursorMode } = useTableEditorContext();

const [markedForInvalidation, setMarkedForInvalidation] = useState(false);

const onEnter = useCallback(() => {
if (markedForInvalidation) {
invalidateTable();
}
}, [invalidateTable, markedForInvalidation]);

useEffect(() => {
if (markedForInvalidation && cursorMode !== CursorMode.Edit) {
invalidateTable();
}
}, [markedForInvalidation, cursorMode]);

// The first time a resource is saved, mark it for invalidation
useEffect(() => {
return store.on(StoreEvents.ResourceSaved, r => {
if (!markedForInvalidation && r.subject === resource.subject) {
setMarkedForInvalidation(true);
}
});
}, [resource, store, markedForInvalidation]);

return onEnter;
}

0 comments on commit cad74e8

Please sign in to comment.