mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Implement and Add SideDrawerField for Formula Field (#1225)
* Implement and Add SideDrawerField for Formula Field * Modify Formula SideDrawerField to use already calculated value * fix unneccessary db update on formula calculations --------- Co-authored-by: Han Tuerker <burhan.tuerker@gmail.com>
This commit is contained in:
@@ -386,7 +386,9 @@ export const updateFieldAtom = atom(
|
||||
);
|
||||
|
||||
if (!row) throw new Error("Could not find row");
|
||||
const isLocalRow = Boolean(find(tableRowsLocal, ["_rowy_ref.path", path]));
|
||||
const isLocalRow =
|
||||
fieldName.startsWith("_rowy_formulaValue_") ||
|
||||
Boolean(find(tableRowsLocal, ["_rowy_ref.path", path]));
|
||||
|
||||
const update: Partial<TableRow> = {};
|
||||
|
||||
@@ -467,6 +469,14 @@ export const updateFieldAtom = atom(
|
||||
deleteFields: deleteField ? [fieldName] : [],
|
||||
});
|
||||
|
||||
// TODO(han): Formula field persistence
|
||||
// const config = find(tableColumnsOrdered, (c) => {
|
||||
// const [, key] = fieldName.split("_rowy_formulaValue_");
|
||||
// return c.key === key;
|
||||
// });
|
||||
// if(!config.persist) return;
|
||||
if (fieldName.startsWith("_rowy_formulaValue")) return;
|
||||
|
||||
// If it has no missingRequiredFields, also write to db
|
||||
// And write entire row to handle the case where it doesn’t exist in db yet
|
||||
if (missingRequiredFields.length === 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import FieldWrapper from "./FieldWrapper";
|
||||
import { IFieldConfig } from "@src/components/fields/types";
|
||||
import { getFieldProp } from "@src/components/fields";
|
||||
import { ColumnConfig, TableRowRef } from "@src/types/table";
|
||||
import { TableRow } from "@src/types/table";
|
||||
|
||||
export interface IMemoizedFieldProps {
|
||||
field: ColumnConfig;
|
||||
@@ -16,6 +17,7 @@ export interface IMemoizedFieldProps {
|
||||
isDirty: boolean;
|
||||
onDirty: (fieldName: string) => void;
|
||||
onSubmit: (fieldName: string, value: any) => void;
|
||||
row: TableRow;
|
||||
}
|
||||
|
||||
export const MemoizedField = memo(
|
||||
@@ -28,6 +30,7 @@ export const MemoizedField = memo(
|
||||
isDirty,
|
||||
onDirty,
|
||||
onSubmit,
|
||||
row,
|
||||
...props
|
||||
}: IMemoizedFieldProps) {
|
||||
const [localValue, setLocalValue, localValueRef] = useStateRef(value);
|
||||
@@ -40,11 +43,7 @@ export const MemoizedField = memo(
|
||||
onSubmit(field.fieldName, localValueRef.current);
|
||||
}, [field.fieldName, localValueRef, onSubmit]);
|
||||
|
||||
// Derivative/aggregate field support
|
||||
let type = field.type;
|
||||
if (field.config && field.config.renderFieldType) {
|
||||
type = field.config.renderFieldType;
|
||||
}
|
||||
|
||||
const fieldComponent: IFieldConfig["SideDrawerField"] = getFieldProp(
|
||||
"SideDrawerField",
|
||||
@@ -78,6 +77,7 @@ export const MemoizedField = memo(
|
||||
},
|
||||
onSubmit: handleSubmit,
|
||||
disabled,
|
||||
row,
|
||||
})}
|
||||
</FieldWrapper>
|
||||
);
|
||||
|
||||
@@ -130,6 +130,7 @@ export default function SideDrawerFields({ row }: ISideDrawerFieldsProps) {
|
||||
onDirty={onDirty}
|
||||
onSubmit={onSubmit}
|
||||
isDirty={dirtyField === field.key}
|
||||
row={row}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import DragIndicatorOutlinedIcon from "@mui/icons-material/DragIndicatorOutlined
|
||||
import DeleteIcon from "@mui/icons-material/DeleteOutline";
|
||||
|
||||
import { FieldType, ISideDrawerFieldProps } from "@src/components/fields/types";
|
||||
import { TableRowRef } from "@src/types/table";
|
||||
import { TableRow, TableRowRef } from "@src/types/table";
|
||||
|
||||
import AddButton from "./AddButton";
|
||||
import { getPseudoColumn } from "./utils";
|
||||
@@ -29,6 +29,7 @@ function ArrayFieldInput({
|
||||
onRemove,
|
||||
onSubmit,
|
||||
id,
|
||||
row,
|
||||
}: {
|
||||
index: number;
|
||||
onRemove: (index: number) => void;
|
||||
@@ -37,6 +38,7 @@ function ArrayFieldInput({
|
||||
onSubmit: () => void;
|
||||
_rowy_ref: TableRowRef;
|
||||
id: string;
|
||||
row: TableRow;
|
||||
}) {
|
||||
const typeDetected = detectType(value);
|
||||
|
||||
@@ -80,6 +82,7 @@ function ArrayFieldInput({
|
||||
column={getPseudoColumn(typeDetected, index, value)}
|
||||
value={value}
|
||||
_rowy_ref={_rowy_ref}
|
||||
row={row}
|
||||
/>
|
||||
</Stack>
|
||||
<Box
|
||||
@@ -174,6 +177,7 @@ export default function ArraySideDrawerField({
|
||||
onRemove={handleRemove}
|
||||
index={index}
|
||||
onSubmit={onSubmit}
|
||||
row={props.row}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { defaultFn, getDisplayCell } from "./util";
|
||||
|
||||
export default function Formula(props: IDisplayCellProps) {
|
||||
const { result, error, loading } = useFormula({
|
||||
column: props.column,
|
||||
row: props.row,
|
||||
ref: props._rowy_ref,
|
||||
listenerFields: props.column.config?.listenerFields || [],
|
||||
|
||||
46
src/components/fields/Formula/SideDrawerField.tsx
Normal file
46
src/components/fields/Formula/SideDrawerField.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ISideDrawerFieldProps } from "@src/components/fields/types";
|
||||
import { IFieldConfig } from "@src/components/fields/types";
|
||||
import { getFieldProp } from "@src/components/fields";
|
||||
import { isEmpty } from "lodash-es";
|
||||
import { createElement } from "react";
|
||||
|
||||
export default function Formula({
|
||||
column,
|
||||
onChange,
|
||||
onSubmit,
|
||||
_rowy_ref,
|
||||
onDirty,
|
||||
row,
|
||||
}: ISideDrawerFieldProps) {
|
||||
const value = row[`_rowy_formulaValue_${column.key}`];
|
||||
|
||||
let type = column.type;
|
||||
if (column.config && column.config.renderFieldType) {
|
||||
type = column.config.renderFieldType;
|
||||
}
|
||||
const fieldComponent: IFieldConfig["SideDrawerField"] = getFieldProp(
|
||||
"SideDrawerField",
|
||||
type
|
||||
);
|
||||
|
||||
// Should not reach this state
|
||||
if (isEmpty(fieldComponent)) {
|
||||
console.error("Could not find SideDrawerField component", column);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{createElement(fieldComponent, {
|
||||
column,
|
||||
_rowy_ref,
|
||||
value,
|
||||
onDirty,
|
||||
onChange,
|
||||
onSubmit,
|
||||
disabled: true,
|
||||
row,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,16 @@
|
||||
import { lazy } from "react";
|
||||
import FormulaIcon from "@mui/icons-material/Functions";
|
||||
import { IFieldConfig, FieldType } from "@src/components/fields/types";
|
||||
import withRenderTableCell from "@src/components/Table/TableCell/withRenderTableCell";
|
||||
import DisplayCell from "./DisplayCell";
|
||||
|
||||
import Settings, { settingsValidator } from "./Settings";
|
||||
const SideDrawerField = lazy(
|
||||
() =>
|
||||
import(
|
||||
"./SideDrawerField" /* webpackChunkName: "SideDrawerField-Formula" */
|
||||
)
|
||||
);
|
||||
|
||||
export const config: IFieldConfig = {
|
||||
type: FieldType.formula,
|
||||
@@ -16,7 +23,7 @@ export const config: IFieldConfig = {
|
||||
TableCell: withRenderTableCell(DisplayCell as any, null, undefined, {
|
||||
usesRowData: true,
|
||||
}),
|
||||
SideDrawerField: () => null as any,
|
||||
SideDrawerField,
|
||||
settings: Settings,
|
||||
settingsValidator: settingsValidator,
|
||||
requireConfiguration: true,
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { pick, zipObject } from "lodash-es";
|
||||
import { useAtom } from "jotai";
|
||||
import { useAtom, useSetAtom } from "jotai";
|
||||
|
||||
import { TableRow, TableRowRef } from "@src/types/table";
|
||||
import { tableColumnsOrderedAtom, tableScope } from "@src/atoms/tableScope";
|
||||
import { TableRow, TableRowRef, ColumnConfig } from "@src/types/table";
|
||||
import {
|
||||
tableColumnsOrderedAtom,
|
||||
tableScope,
|
||||
updateFieldAtom,
|
||||
} from "@src/atoms/tableScope";
|
||||
|
||||
import {
|
||||
listenerFieldTypes,
|
||||
@@ -12,11 +16,13 @@ import {
|
||||
} from "./util";
|
||||
|
||||
export const useFormula = ({
|
||||
column,
|
||||
row,
|
||||
ref,
|
||||
listenerFields,
|
||||
formulaFn,
|
||||
}: {
|
||||
column: ColumnConfig;
|
||||
row: TableRow;
|
||||
ref: TableRowRef;
|
||||
listenerFields: string[];
|
||||
@@ -78,5 +84,14 @@ export const useFormula = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [useDeepCompareMemoize(listeners), formulaFn]);
|
||||
|
||||
const updateField = useSetAtom(updateFieldAtom, tableScope);
|
||||
useEffect(() => {
|
||||
updateField({
|
||||
path: row._rowy_ref.path,
|
||||
fieldName: `_rowy_formulaValue_${column.key}`,
|
||||
value: result,
|
||||
});
|
||||
}, [result, column.key, row._rowy_ref.path, updateField]);
|
||||
|
||||
return { result, error, loading };
|
||||
};
|
||||
|
||||
@@ -92,6 +92,8 @@ export interface ISideDrawerFieldProps<T = any> {
|
||||
|
||||
/** Field locked. Do NOT check `column.locked` */
|
||||
disabled: boolean;
|
||||
|
||||
row: TableRow
|
||||
}
|
||||
|
||||
export interface ISettingsProps {
|
||||
|
||||
@@ -52,6 +52,12 @@ export const omitRowyFields = <T = Record<string, any>>(row: T) => {
|
||||
delete shallowClonedRow["_rowy_missingRequiredFields"];
|
||||
delete shallowClonedRow["_rowy_new"];
|
||||
|
||||
Object.keys(shallowClonedRow).forEach((key) => {
|
||||
if (key.startsWith("_rowy_formulaValue_")) {
|
||||
delete shallowClonedRow[key];
|
||||
}
|
||||
});
|
||||
|
||||
return shallowClonedRow as T;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user