diff --git a/src/components/Table/EditorCellTextField.tsx b/src/components/Table/EditorCellTextField.tsx new file mode 100644 index 00000000..ee0fd9f3 --- /dev/null +++ b/src/components/Table/EditorCellTextField.tsx @@ -0,0 +1,77 @@ +import type { IEditorCellProps } from "@src/components/fields/types"; +import { useSaveOnUnmount } from "@src/hooks/useSaveOnUnmount"; + +import { InputBase, InputBaseProps } from "@mui/material"; + +import { spreadSx } from "@src/utils/ui"; + +export interface IEditorCellTextFieldProps extends IEditorCellProps { + InputProps?: Partial; +} + +export default function EditorCellTextField({ + column, + value, + onSubmit, + setFocusInsideCell, + InputProps = {}, +}: IEditorCellTextFieldProps) { + const [localValue, setLocalValue] = useSaveOnUnmount(value, onSubmit); + const maxLength = column.config?.maxLength; + + return ( + setLocalValue(e.target.value)} + fullWidth + autoFocus + onKeyDown={(e) => { + if ( + e.key === "ArrowLeft" || + e.key === "ArrowRight" || + e.key === "ArrowUp" || + e.key === "ArrowDown" + ) { + e.stopPropagation(); + } + if (e.key === "Escape") { + // Escape removes focus inside cell, this runs before save on unmount + setLocalValue(value); + } + if (e.key === "Enter" && !e.shiftKey) { + // Removes focus from inside cell, triggering save on unmount + setFocusInsideCell(false); + } + }} + onClick={(e) => e.stopPropagation()} + onDoubleClick={(e) => e.stopPropagation()} + {...InputProps} + inputProps={{ maxLength, ...InputProps.inputProps }} + sx={[ + { + width: "100%", + height: "calc(100% - 1px)", + marginTop: "1px", + paddingBottom: "1px", + + backgroundColor: "var(--cell-background-color)", + outline: "inherit", + outlineOffset: "inherit", + + font: "inherit", // Prevent text jumping + letterSpacing: "inherit", // Prevent text jumping + + "& .MuiInputBase-input": { p: "var(--cell-padding)" }, + + "& textarea.MuiInputBase-input": { + lineHeight: (theme) => theme.typography.body2.lineHeight, + maxHeight: "100%", + boxSizing: "border-box", + py: 3 / 8, + }, + }, + ...spreadSx(InputProps.sx), + ]} + /> + ); +} diff --git a/src/components/Table/Styled/StyledCell.tsx b/src/components/Table/Styled/StyledCell.tsx index 23ffb2ef..c11fee62 100644 --- a/src/components/Table/Styled/StyledCell.tsx +++ b/src/components/Table/Styled/StyledCell.tsx @@ -14,6 +14,9 @@ export const StyledCell = styled("div")(({ theme }) => ({ height: "100%", contain: "strict", overflow: "hidden", + + display: "flex", + alignItems: "center", }, backgroundColor: "var(--cell-background-color)", diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index f7d72c76..f8ca9486 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -446,7 +446,11 @@ export default function Table({ cell.column.columnDef.meta?.config?.required )} aria-selected={isSelectedCell} - aria-describedby="rowy-table-cell-description" + aria-describedby={ + canEditCell + ? "rowy-table-editable-cell-description" + : undefined + } style={{ width: cell.column.getSize(), height: tableSchema.rowHeight, @@ -537,7 +541,10 @@ export default function Table({ -
+
Press Enter to edit.
diff --git a/src/components/Table/editors/TextEditor.tsx b/src/components/Table/editors/TextEditor.tsx deleted file mode 100644 index 58229457..00000000 --- a/src/components/Table/editors/TextEditor.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { useRef, useLayoutEffect } from "react"; -import { EditorProps } from "react-data-grid"; -import { useSetAtom } from "jotai"; -import { get } from "lodash-es"; - -import { TextField } from "@mui/material"; - -import { tableScope, updateFieldAtom } from "@src/atoms/tableScope"; -import { FieldType } from "@src/constants/fields"; -import { getFieldType } from "@src/components/fields"; - -/** WARNING: THIS DOES NOT WORK IN REACT 18 STRICT MODE */ -export default function TextEditor({ row, column }: EditorProps) { - const updateField = useSetAtom(updateFieldAtom, tableScope); - - const type = getFieldType(column as any); - - const cellValue = get(row, column.key); - const defaultValue = - type === FieldType.percentage && typeof cellValue === "number" - ? cellValue * 100 - : cellValue; - - const inputRef = useRef(null); - - // WARNING: THIS DOES NOT WORK IN REACT 18 STRICT MODE - useLayoutEffect(() => { - const inputElement = inputRef.current; - return () => { - const newValue = inputElement?.value; - let formattedValue: any = newValue; - if (newValue !== undefined) { - if (type === FieldType.number) { - formattedValue = Number(newValue); - } else if (type === FieldType.percentage) { - formattedValue = Number(newValue) / 100; - } - - updateField({ - path: row._rowy_ref.path, - fieldName: column.key, - value: formattedValue, - }); - } - }; - }, [column.key, row._rowy_ref.path, type, updateField]); - - let inputType = "text"; - switch (type) { - case FieldType.email: - inputType = "email"; - break; - case FieldType.phone: - inputType = "tel"; - break; - case FieldType.url: - inputType = "url"; - break; - case FieldType.number: - case FieldType.percentage: - inputType = "number"; - break; - - default: - break; - } - - const { maxLength } = (column as any).config; - - return ( - theme.typography.body2.lineHeight, - maxHeight: "100%", - boxSizing: "border-box", - py: 3 / 8, - }, - }} - InputProps={{ - endAdornment: - (column as any).type === FieldType.percentage ? "%" : undefined, - }} - autoFocus - onKeyDown={(e) => { - if (e.key === "ArrowLeft" || e.key === "ArrowRight") { - e.stopPropagation(); - } - - if (e.key === "Escape") { - (e.target as any).value = defaultValue; - } - }} - /> - ); -} diff --git a/src/components/Table/withTableCell.tsx b/src/components/Table/withTableCell.tsx index 88aa7e68..5e0b5478 100644 --- a/src/components/Table/withTableCell.tsx +++ b/src/components/Table/withTableCell.tsx @@ -36,7 +36,7 @@ export interface ICellOptions { */ export default function withTableCell( DisplayCellComponent: React.ComponentType, - EditorCellComponent: React.ComponentType, + EditorCellComponent: React.ComponentType | null, editorMode: "focus" | "inline" | "popover" = "focus", options: ICellOptions = {} ) { @@ -115,13 +115,16 @@ export default function withTableCell( }); }; - const editorCell = ( + // Show displayCell as a fallback if intentionally null + const editorCell = EditorCellComponent ? ( + ) : ( + displayCell ); if (editorMode === "focus" && focusInsideCell) { diff --git a/src/components/fields/Email/EditorCell.tsx b/src/components/fields/Email/EditorCell.tsx new file mode 100644 index 00000000..c14a114a --- /dev/null +++ b/src/components/fields/Email/EditorCell.tsx @@ -0,0 +1,6 @@ +import type { IEditorCellProps } from "@src/components/fields/types"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; + +export default function Email(props: IEditorCellProps) { + return ; +} diff --git a/src/components/fields/Email/index.tsx b/src/components/fields/Email/index.tsx index 87b3a07a..d8a461ac 100644 --- a/src/components/fields/Email/index.tsx +++ b/src/components/fields/Email/index.tsx @@ -1,10 +1,10 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell"; +import withTableCell from "@src/components/Table/withTableCell"; import EmailIcon from "@mui/icons-material/MailOutlined"; -import BasicCell from "@src/components/fields/_BasicCell/BasicCellValue"; -import TextEditor from "@src/components/Table/editors/TextEditor"; +import DisplayCell from "@src/components/fields/_BasicCell/BasicCellValue"; +import EditorCell from "./EditorCell"; import { filterOperators } from "@src/components/fields/ShortText/Filter"; import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions"; @@ -23,8 +23,7 @@ export const config: IFieldConfig = { icon: , description: "Email address. Not validated.", contextMenuActions: BasicContextMenuActions, - TableCell: withBasicCell(BasicCell), - TableEditor: TextEditor, + TableCell: withTableCell(DisplayCell, EditorCell), SideDrawerField, filter: { operators: filterOperators, diff --git a/src/components/fields/Id/TableCell.tsx b/src/components/fields/Id/DisplayCell.tsx similarity index 69% rename from src/components/fields/Id/TableCell.tsx rename to src/components/fields/Id/DisplayCell.tsx index 2ff1feff..fe86e927 100644 --- a/src/components/fields/Id/TableCell.tsx +++ b/src/components/fields/Id/DisplayCell.tsx @@ -1,8 +1,8 @@ -import { IHeavyCellProps } from "@src/components/fields/types"; +import { IDisplayCellProps } from "@src/components/fields/types"; import { useTheme } from "@mui/material"; -export default function Id({ docRef }: IHeavyCellProps) { +export default function Id({ docRef }: IDisplayCellProps) { const theme = useTheme(); return ( diff --git a/src/components/fields/Id/index.tsx b/src/components/fields/Id/index.tsx index c6222990..757ed231 100644 --- a/src/components/fields/Id/index.tsx +++ b/src/components/fields/Id/index.tsx @@ -1,17 +1,9 @@ -import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withHeavyCell from "@src/components/fields/_withTableCell/withHeavyCell"; +import withTableCell from "@src/components/Table/withTableCell"; +import DisplayCell from "./DisplayCell"; +import SideDrawerField from "./SideDrawerField"; import { Id as IdIcon } from "@src/assets/icons"; -import BasicCell from "@src/components/fields/_BasicCell/BasicCellValue"; -import withSideDrawerEditor from "@src/components/Table/editors/withSideDrawerEditor"; - -const TableCell = lazy( - () => import("./TableCell" /* webpackChunkName: "TableCell-Id" */) -); -const SideDrawerField = lazy( - () => import("./SideDrawerField" /* webpackChunkName: "SideDrawerField-Id" */) -); export const config: IFieldConfig = { type: FieldType.id, @@ -21,8 +13,7 @@ export const config: IFieldConfig = { initialValue: "", icon: , description: "Displays the row’s ID. Read-only. Cannot be sorted.", - TableCell: withHeavyCell(BasicCell, TableCell), - TableEditor: withSideDrawerEditor(TableCell), + TableCell: withTableCell(DisplayCell, null), SideDrawerField, }; export default config; diff --git a/src/components/fields/LongText/BasicCell.tsx b/src/components/fields/LongText/DisplayCell.tsx similarity index 58% rename from src/components/fields/LongText/BasicCell.tsx rename to src/components/fields/LongText/DisplayCell.tsx index 14c6e23e..1e8e0fc2 100644 --- a/src/components/fields/LongText/BasicCell.tsx +++ b/src/components/fields/LongText/DisplayCell.tsx @@ -1,16 +1,15 @@ -import { IBasicCellProps } from "@src/components/fields/types"; +import { IDisplayCellProps } from "@src/components/fields/types"; import { useTheme } from "@mui/material"; -export default function LongText({ value }: IBasicCellProps) { +export default function LongText({ value }: IDisplayCellProps) { const theme = useTheme(); return (
) { + return ; +} diff --git a/src/components/fields/LongText/index.tsx b/src/components/fields/LongText/index.tsx index c4cec831..ef0ce27c 100644 --- a/src/components/fields/LongText/index.tsx +++ b/src/components/fields/LongText/index.tsx @@ -1,21 +1,15 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell"; +import withTableCell from "@src/components/Table/withTableCell"; import LongTextIcon from "@mui/icons-material/Notes"; -import BasicCell from "./BasicCell"; -import TextEditor from "@src/components/Table/editors/TextEditor"; +import DisplayCell from "./DisplayCell"; +import EditorCell from "./EditorCell"; +import SideDrawerField from "./SideDrawerField"; import { filterOperators } from "./Filter"; import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions"; -const SideDrawerField = lazy( - () => - import( - "./SideDrawerField" /* webpackChunkName: "SideDrawerField-LongText" */ - ) -); - const Settings = lazy( () => import("./Settings" /* webpackChunkName: "Settings-LongText" */) ); @@ -30,8 +24,7 @@ export const config: IFieldConfig = { icon: , description: "Text displayed on multiple lines.", contextMenuActions: BasicContextMenuActions, - TableCell: withBasicCell(BasicCell), - TableEditor: TextEditor, + TableCell: withTableCell(DisplayCell, EditorCell), SideDrawerField, settings: Settings, filter: { diff --git a/src/components/fields/Number/BasicCell.tsx b/src/components/fields/Number/BasicCell.tsx deleted file mode 100644 index 71d6be89..00000000 --- a/src/components/fields/Number/BasicCell.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { IBasicCellProps } from "@src/components/fields/types"; - -export default function Number_({ value }: IBasicCellProps) { - return <>{`${value ?? ""}`}; -} diff --git a/src/components/fields/Number/DisplayCell.tsx b/src/components/fields/Number/DisplayCell.tsx new file mode 100644 index 00000000..5256a741 --- /dev/null +++ b/src/components/fields/Number/DisplayCell.tsx @@ -0,0 +1,5 @@ +import { IDisplayCellProps } from "@src/components/fields/types"; + +export default function Number_({ value }: IDisplayCellProps) { + return <>{`${value ?? ""}`}; +} diff --git a/src/components/fields/Number/EditorCell.tsx b/src/components/fields/Number/EditorCell.tsx new file mode 100644 index 00000000..93c5c31c --- /dev/null +++ b/src/components/fields/Number/EditorCell.tsx @@ -0,0 +1,12 @@ +import type { IEditorCellProps } from "@src/components/fields/types"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; + +export default function Number_(props: IEditorCellProps) { + return ( + props.onSubmit(Number(v))} + /> + ); +} diff --git a/src/components/fields/Number/index.tsx b/src/components/fields/Number/index.tsx index a7fdc94c..7bed8e43 100644 --- a/src/components/fields/Number/index.tsx +++ b/src/components/fields/Number/index.tsx @@ -1,10 +1,10 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell"; +import withTableCell from "@src/components/Table/withTableCell"; import { Number as NumberIcon } from "@src/assets/icons"; -import BasicCell from "./BasicCell"; -import TextEditor from "@src/components/Table/editors/TextEditor"; +import DisplayCell from "./DisplayCell"; +import EditorCell from "./EditorCell"; import { filterOperators } from "./Filter"; import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions"; const SideDrawerField = lazy( @@ -22,8 +22,7 @@ export const config: IFieldConfig = { icon: , description: "Numeric value.", contextMenuActions: BasicContextMenuActions, - TableCell: withBasicCell(BasicCell), - TableEditor: TextEditor, + TableCell: withTableCell(DisplayCell, EditorCell), SideDrawerField, filter: { operators: filterOperators, diff --git a/src/components/fields/Percentage/BasicCell.tsx b/src/components/fields/Percentage/BasicCell.tsx deleted file mode 100644 index 5f6228aa..00000000 --- a/src/components/fields/Percentage/BasicCell.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { IBasicCellProps } from "@src/components/fields/types"; - -import { useTheme } from "@mui/material"; - -export default function Percentage({ value }: IBasicCellProps) { - const theme = useTheme(); - - if (value === null || value === undefined) return null; - - const percentage = typeof value === "number" ? value : 0; - return ( -
- {Math.round(percentage * 100)}% -
- ); -} diff --git a/src/components/fields/Percentage/TableCell.tsx b/src/components/fields/Percentage/DisplayCell.tsx similarity index 85% rename from src/components/fields/Percentage/TableCell.tsx rename to src/components/fields/Percentage/DisplayCell.tsx index 29174a39..532103e4 100644 --- a/src/components/fields/Percentage/TableCell.tsx +++ b/src/components/fields/Percentage/DisplayCell.tsx @@ -1,9 +1,9 @@ -import { IHeavyCellProps } from "@src/components/fields/types"; +import { IDisplayCellProps } from "@src/components/fields/types"; import { useTheme } from "@mui/material"; import { resultColorsScale } from "@src/utils/color"; -export default function Percentage({ column, value }: IHeavyCellProps) { +export default function Percentage({ column, value }: IDisplayCellProps) { const theme = useTheme(); const { colors } = (column as any).config; diff --git a/src/components/fields/Percentage/EditorCell.tsx b/src/components/fields/Percentage/EditorCell.tsx new file mode 100644 index 00000000..652cdcd0 --- /dev/null +++ b/src/components/fields/Percentage/EditorCell.tsx @@ -0,0 +1,13 @@ +import type { IEditorCellProps } from "@src/components/fields/types"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; + +export default function Percentage(props: IEditorCellProps) { + return ( + props.onSubmit(Number(v) / 100)} + /> + ); +} diff --git a/src/components/fields/Percentage/index.tsx b/src/components/fields/Percentage/index.tsx index f353774f..99239f7c 100644 --- a/src/components/fields/Percentage/index.tsx +++ b/src/components/fields/Percentage/index.tsx @@ -1,20 +1,13 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withHeavyCell from "@src/components/fields/_withTableCell/withHeavyCell"; +import withTableCell from "@src/components/Table/withTableCell"; import { Percentage as PercentageIcon } from "@src/assets/icons"; -import TextEditor from "@src/components/Table/editors/TextEditor"; +import DisplayCell from "./DisplayCell"; +import EditorCell from "./EditorCell"; import { filterOperators } from "@src/components/fields/Number/Filter"; import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions"; -const BasicCell = lazy( - () => import("./BasicCell" /* webpackChunkName: "BasicCell-Percentage" */) -); - -const TableCell = lazy( - () => import("./TableCell" /* webpackChunkName: "TableCell-Percentage" */) -); - const SideDrawerField = lazy( () => import( @@ -37,8 +30,7 @@ export const config: IFieldConfig = { requireConfiguration: true, description: "Percentage stored as a number between 0 and 1.", contextMenuActions: BasicContextMenuActions, - TableCell: withHeavyCell(BasicCell, TableCell), - TableEditor: TextEditor, + TableCell: withTableCell(DisplayCell, EditorCell), SideDrawerField, settings: Settings, filter: { diff --git a/src/components/fields/Phone/EditorCell.tsx b/src/components/fields/Phone/EditorCell.tsx new file mode 100644 index 00000000..825fd00f --- /dev/null +++ b/src/components/fields/Phone/EditorCell.tsx @@ -0,0 +1,6 @@ +import type { IEditorCellProps } from "@src/components/fields/types"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; + +export default function Phone(props: IEditorCellProps) { + return ; +} diff --git a/src/components/fields/Phone/index.tsx b/src/components/fields/Phone/index.tsx index 96115973..9e5c1ebe 100644 --- a/src/components/fields/Phone/index.tsx +++ b/src/components/fields/Phone/index.tsx @@ -1,10 +1,10 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell"; +import withTableCell from "@src/components/Table/withTableCell"; import PhoneIcon from "@mui/icons-material/PhoneOutlined"; -import BasicCell from "@src/components/fields/_BasicCell/BasicCellValue"; -import TextEditor from "@src/components/Table/editors/TextEditor"; +import DisplayCell from "@src/components/fields/_BasicCell/BasicCellValue"; +import EditorCell from "./EditorCell"; import { filterOperators } from "@src/components/fields/ShortText/Filter"; import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions"; @@ -23,8 +23,7 @@ export const config: IFieldConfig = { icon: , description: "Phone number stored as text. Not validated.", contextMenuActions: BasicContextMenuActions, - TableCell: withBasicCell(BasicCell), - TableEditor: TextEditor, + TableCell: withTableCell(DisplayCell, EditorCell), SideDrawerField, filter: { operators: filterOperators, diff --git a/src/components/fields/Reference/BasicCell.tsx b/src/components/fields/Reference/DisplayCell.tsx similarity index 81% rename from src/components/fields/Reference/BasicCell.tsx rename to src/components/fields/Reference/DisplayCell.tsx index b75b62d2..a15d9db4 100644 --- a/src/components/fields/Reference/BasicCell.tsx +++ b/src/components/fields/Reference/DisplayCell.tsx @@ -1,12 +1,12 @@ import { useAtom } from "jotai"; -import { IBasicCellProps } from "@src/components/fields/types"; +import { IDisplayCellProps } from "@src/components/fields/types"; import { Stack, IconButton } from "@mui/material"; import LaunchIcon from "@mui/icons-material/Launch"; import { projectScope, projectIdAtom } from "@src/atoms/projectScope"; -export default function Reference({ value }: IBasicCellProps) { +export default function Reference({ value }: IDisplayCellProps) { const [projectId] = useAtom(projectIdAtom, projectScope); const path = value?.path ?? ""; @@ -17,8 +17,7 @@ export default function Reference({ value }: IBasicCellProps) { direction="row" alignItems="center" justifyContent="space-between" - className="cell-collapse-padding" - sx={{ p: "var(--cell-padding)", pr: 0.5 }} + sx={{ p: "var(--cell-padding)", pr: 0.5, width: "100%" }} >
{path}
diff --git a/src/components/fields/Reference/EditorCell.tsx b/src/components/fields/Reference/EditorCell.tsx index 8b96b8b3..770f352c 100644 --- a/src/components/fields/Reference/EditorCell.tsx +++ b/src/components/fields/Reference/EditorCell.tsx @@ -1,100 +1,33 @@ -import { useRef, useLayoutEffect } from "react"; -import { useAtom, useSetAtom } from "jotai"; -import { EditorProps } from "react-data-grid"; -import { get } from "lodash-es"; -import { useSnackbar } from "notistack"; +import type { IEditorCellProps } from "@src/components/fields/types"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; -import { TextField } from "@mui/material"; +import { useAtom } from "jotai"; +import { doc, deleteField } from "firebase/firestore"; +import { useSnackbar } from "notistack"; import { projectScope } from "@src/atoms/projectScope"; import { firebaseDbAtom } from "@src/sources/ProjectSourceFirebase"; -import { tableScope, updateFieldAtom } from "@src/atoms/tableScope"; -import { doc, deleteField } from "firebase/firestore"; -/** WARNING: THIS DOES NOT WORK IN REACT 18 STRICT MODE */ -export default function TextEditor({ row, column }: EditorProps) { - const [firebaseDb] = useAtom(firebaseDbAtom, projectScope); - const updateField = useSetAtom(updateFieldAtom, tableScope); +export default function Reference( + props: IEditorCellProps> +) { const { enqueueSnackbar } = useSnackbar(); - - const inputRef = useRef(null); - - // WARNING: THIS DOES NOT WORK IN REACT 18 STRICT MODE - useLayoutEffect(() => { - const inputElement = inputRef.current; - return () => { - const newValue = inputElement?.value; - if (newValue !== undefined && newValue !== "") { - try { - const refValue = doc(firebaseDb, newValue); - - updateField({ - path: row._rowy_ref.path, - fieldName: column.key, - value: refValue, - }); - } catch (e: any) { - enqueueSnackbar(`Invalid path: ${e.message}`, { variant: "error" }); - } - } else { - updateField({ - path: row._rowy_ref.path, - fieldName: column.key, - value: deleteField(), - }); - } - }; - }, [column.key, row._rowy_ref.path, updateField]); - - const defaultValue = get(row, column.key)?.path ?? ""; - const { maxLength } = (column as any).config; + const [firebaseDb] = useAtom(firebaseDbAtom, projectScope); return ( - theme.typography.body2.lineHeight, - maxHeight: "100%", - boxSizing: "border-box", - py: 3 / 8, - }, - }} - // InputProps={{ - // endAdornment: - // (column as any).type === FieldType.percentage ? "%" : undefined, - // }} - autoFocus - onKeyDown={(e) => { - if (e.key === "ArrowLeft" || e.key === "ArrowRight") { - e.stopPropagation(); - } - - if (e.key === "Escape") { - (e.target as any).value = defaultValue; + { + if (newValue !== undefined && newValue !== "") { + try { + const refValue = doc(firebaseDb, newValue); + props.onSubmit(refValue); + } catch (e: any) { + enqueueSnackbar(`Invalid path: ${e.message}`, { variant: "error" }); + } + } else { + props.onSubmit(deleteField() as any); } }} /> diff --git a/src/components/fields/Reference/index.tsx b/src/components/fields/Reference/index.tsx index 9d5b0f43..fcf0345d 100644 --- a/src/components/fields/Reference/index.tsx +++ b/src/components/fields/Reference/index.tsx @@ -1,24 +1,18 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; +import withTableCell from "@src/components/Table/withTableCell"; import { Reference } from "@src/assets/icons"; -//import InlineCell from "./InlineCell"; -import BasicCell from "./BasicCell"; +import DisplayCell from "./DisplayCell"; +import EditorCell from "./EditorCell"; import { filterOperators } from "@src/components/fields/ShortText/Filter"; -import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell"; -const EditorCell = lazy( - () => import("./EditorCell" /* webpackChunkName: "EditorCell-Reference" */) -); const SideDrawerField = lazy( () => import( "./SideDrawerField" /* webpackChunkName: "SideDrawerField-Reference" */ ) ); -// const Settings = lazy( -// () => import("./Settings" /* webpackChunkName: "Settings-Reference" */) -// ); export const config: IFieldConfig = { type: FieldType.reference, @@ -29,10 +23,10 @@ export const config: IFieldConfig = { initializable: true, icon: , description: "Firestore document reference", - TableCell: withBasicCell(BasicCell), - TableEditor: EditorCell, + TableCell: withTableCell(DisplayCell, EditorCell, "focus", { + disablePadding: true, + }), SideDrawerField, - //settings: Settings, filter: { operators: filterOperators }, }; export default config; diff --git a/src/components/fields/ShortText/EditorCell.tsx b/src/components/fields/ShortText/EditorCell.tsx index 418ce4dd..0e34e5a8 100644 --- a/src/components/fields/ShortText/EditorCell.tsx +++ b/src/components/fields/ShortText/EditorCell.tsx @@ -1,61 +1,6 @@ import type { IEditorCellProps } from "@src/components/fields/types"; -import { useSaveOnUnmount } from "@src/hooks/useSaveOnUnmount"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; -import { InputBase } from "@mui/material"; - -export default function ShortText({ - column, - value, - onSubmit, - setFocusInsideCell, -}: IEditorCellProps) { - const [localValue, setLocalValue] = useSaveOnUnmount(value, onSubmit); - const maxLength = column.config?.maxLength; - - return ( - setLocalValue(e.target.value)} - fullWidth - inputProps={{ maxLength }} - sx={{ - width: "100%", - height: "calc(100% - 1px)", - marginTop: "1px", - paddingBottom: "1px", - - backgroundColor: "var(--cell-background-color)", - outline: "inherit", - outlineOffset: "inherit", - - font: "inherit", // Prevent text jumping - letterSpacing: "inherit", // Prevent text jumping - - "& .MuiInputBase-input": { p: "var(--cell-padding)" }, - - "& textarea.MuiInputBase-input": { - lineHeight: (theme) => theme.typography.body2.lineHeight, - maxHeight: "100%", - boxSizing: "border-box", - py: 3 / 8, - }, - }} - autoFocus - onKeyDown={(e) => { - if (e.key === "ArrowLeft" || e.key === "ArrowRight") { - e.stopPropagation(); - } - if (e.key === "Escape") { - // Escape removes focus inside cell, this runs before save on unmount - setLocalValue(value); - } - if (e.key === "Enter") { - // Removes focus from inside cell, triggering save on unmount - setFocusInsideCell(false); - } - }} - onClick={(e) => e.stopPropagation()} - onDoubleClick={(e) => e.stopPropagation()} - /> - ); +export default function ShortText(props: IEditorCellProps) { + return ; } diff --git a/src/components/fields/Url/TableCell.tsx b/src/components/fields/Url/DisplayCell.tsx similarity index 77% rename from src/components/fields/Url/TableCell.tsx rename to src/components/fields/Url/DisplayCell.tsx index 6c8affc3..b3145716 100644 --- a/src/components/fields/Url/TableCell.tsx +++ b/src/components/fields/Url/DisplayCell.tsx @@ -1,9 +1,9 @@ -import { IBasicCellProps } from "@src/components/fields/types"; +import { IDisplayCellProps } from "@src/components/fields/types"; import { Stack, IconButton } from "@mui/material"; import LaunchIcon from "@mui/icons-material/Launch"; -export default function Url({ value }: IBasicCellProps) { +export default function Url({ value }: IDisplayCellProps) { if (!value || typeof value !== "string") return null; const href = value.includes("http") ? value : `https://${value}`; @@ -13,8 +13,7 @@ export default function Url({ value }: IBasicCellProps) { direction="row" alignItems="center" justifyContent="space-between" - className="cell-collapse-padding" - sx={{ p: "var(--cell-padding)", pr: 0.5 }} + sx={{ p: "var(--cell-padding)", pr: 0.5, width: "100%" }} >
{value}
diff --git a/src/components/fields/Url/EditorCell.tsx b/src/components/fields/Url/EditorCell.tsx new file mode 100644 index 00000000..6f39d2d9 --- /dev/null +++ b/src/components/fields/Url/EditorCell.tsx @@ -0,0 +1,6 @@ +import type { IEditorCellProps } from "@src/components/fields/types"; +import EditorCellTextField from "@src/components/Table/EditorCellTextField"; + +export default function Url(props: IEditorCellProps) { + return ; +} diff --git a/src/components/fields/Url/index.tsx b/src/components/fields/Url/index.tsx index 95376b06..c6fb3fd7 100644 --- a/src/components/fields/Url/index.tsx +++ b/src/components/fields/Url/index.tsx @@ -1,10 +1,10 @@ import { lazy } from "react"; import { IFieldConfig, FieldType } from "@src/components/fields/types"; -import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell"; +import withTableCell from "@src/components/Table/withTableCell"; import UrlIcon from "@mui/icons-material/Link"; -import TableCell from "./TableCell"; -import TextEditor from "@src/components/Table/editors/TextEditor"; +import DisplayCell from "./DisplayCell"; +import EditorCell from "./EditorCell"; import { filterOperators } from "@src/components/fields/ShortText/Filter"; import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions"; @@ -23,8 +23,9 @@ export const config: IFieldConfig = { icon: , description: "Web address. Not validated.", contextMenuActions: BasicContextMenuActions, - TableCell: withBasicCell(TableCell), - TableEditor: TextEditor, + TableCell: withTableCell(DisplayCell, EditorCell, "focus", { + disablePadding: true, + }), SideDrawerField, filter: { operators: filterOperators,