mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
row & derivatives context menu
This commit is contained in:
@@ -165,7 +165,6 @@ export default function useMonacoCustomizations({
|
||||
rendererOptions: { "just-types": "true" },
|
||||
});
|
||||
const newLib = result.lines.join("\n").replaceAll("export ", "");
|
||||
// console.log(newLib);
|
||||
monaco?.languages.typescript.javascriptDefaults.addExtraLib(newLib);
|
||||
}
|
||||
};
|
||||
@@ -191,7 +190,6 @@ export default function useMonacoCustomizations({
|
||||
// Set row definitions
|
||||
useEffect(() => {
|
||||
if (!monaco || !rowyRun || !tableState?.columns) return;
|
||||
console.log("setting row definitions");
|
||||
try {
|
||||
const rowDefinition =
|
||||
Object.keys(tableState.columns)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Menu } from "@mui/material";
|
||||
import { Divider, Menu } from "@mui/material";
|
||||
import { default as MenuItem } from "./MenuItem";
|
||||
import { IContextMenuItem } from "./MenuItem";
|
||||
|
||||
@@ -6,14 +6,14 @@ interface IMenuContents {
|
||||
anchorEl: HTMLElement;
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
items: IContextMenuItem[];
|
||||
groups: IContextMenuItem[][];
|
||||
}
|
||||
|
||||
export function MenuContents({
|
||||
anchorEl,
|
||||
open,
|
||||
handleClose,
|
||||
items,
|
||||
groups,
|
||||
}: IMenuContents) {
|
||||
const handleContext = (e: React.MouseEvent) => e.preventDefault();
|
||||
|
||||
@@ -41,8 +41,13 @@ export function MenuContents({
|
||||
}}
|
||||
onContextMenu={handleContext}
|
||||
>
|
||||
{items.map((item, indx: number) => (
|
||||
<MenuItem key={indx} {...item} />
|
||||
{groups.map((items, groupIndex) => (
|
||||
<>
|
||||
{groupIndex > 0 && <Divider />}
|
||||
{items.map((item, index: number) => (
|
||||
<MenuItem key={`contextMenu-${groupIndex}-${index}`} {...item} />
|
||||
))}
|
||||
</>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -3,26 +3,88 @@ import { getColumnType, getFieldProp } from "@src/components/fields";
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import { MenuContents } from "./MenuContent";
|
||||
import { useContextMenuAtom } from "@src/atoms/ContextMenu";
|
||||
|
||||
import { FieldType } from "@src/constants/fields";
|
||||
import DuplicateIcon from "@src/assets/icons/Copy";
|
||||
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
|
||||
import { useAppContext } from "@src/contexts/AppContext";
|
||||
import { IContextMenuItem } from "./MenuItem";
|
||||
import { useConfirmation } from "@src/components/ConfirmationDialog/Context";
|
||||
export default function ContextMenu() {
|
||||
const { tableState } = useProjectContext();
|
||||
const { requestConfirmation } = useConfirmation();
|
||||
const { tableState, deleteRow, addRow } = useProjectContext();
|
||||
const { userRoles } = useAppContext();
|
||||
const { anchorEle, selectedCell, resetContextMenu } = useContextMenuAtom();
|
||||
const columns = tableState?.columns;
|
||||
const selectedColIndex = selectedCell?.colIndex;
|
||||
const selectedColumn = _find(columns, { index: selectedColIndex });
|
||||
if (!selectedColumn) return <></>;
|
||||
const configActions =
|
||||
getFieldProp("contextMenuActions", selectedColumn.type) ||
|
||||
function empty() {};
|
||||
const actions = configActions(selectedCell, resetContextMenu) || [];
|
||||
const menuActions = getFieldProp("contextMenuActions", selectedColumn.type);
|
||||
|
||||
if (!anchorEle || actions.length === 0) return <></>;
|
||||
const actionGroups: IContextMenuItem[][] = [];
|
||||
|
||||
const actions = menuActions
|
||||
? menuActions(selectedCell, resetContextMenu)
|
||||
: [];
|
||||
if (actions.length > 0) actionGroups.push(actions);
|
||||
|
||||
let hasRenderedFieldActions = false;
|
||||
if (selectedColumn.type === FieldType.derivative) {
|
||||
const renderedFieldMenuActions = getFieldProp(
|
||||
"contextMenuActions",
|
||||
selectedColumn.config.renderFieldType
|
||||
);
|
||||
if (renderedFieldMenuActions) {
|
||||
actionGroups.push(
|
||||
renderedFieldMenuActions(selectedCell, resetContextMenu)
|
||||
);
|
||||
hasRenderedFieldActions = true;
|
||||
}
|
||||
}
|
||||
if (!anchorEle || (actions.length === 0 && !hasRenderedFieldActions))
|
||||
return <></>;
|
||||
const row = tableState?.rows[selectedCell!.rowIndex];
|
||||
if (userRoles.includes("ADMIN") && row) {
|
||||
const rowActions = [
|
||||
{
|
||||
label: "Duplicate Row",
|
||||
icon: <DuplicateIcon />,
|
||||
onClick: () => {
|
||||
const { ref, ...clonedRow } = row;
|
||||
addRow!(clonedRow, undefined, { type: "smaller" });
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Delete Row",
|
||||
variant: "secondary",
|
||||
icon: <DeleteIcon color="warning" />,
|
||||
onClick: () => {
|
||||
requestConfirmation({
|
||||
title: "Delete row?",
|
||||
customBody: (
|
||||
<>
|
||||
Row path:
|
||||
<br />
|
||||
<code style={{ userSelect: "all", wordBreak: "break-all" }}>
|
||||
{row.ref.path}
|
||||
</code>
|
||||
</>
|
||||
),
|
||||
confirm: "Delete",
|
||||
confirmColor: "error",
|
||||
handleConfirm: () => deleteRow?.(row.id),
|
||||
});
|
||||
resetContextMenu();
|
||||
},
|
||||
},
|
||||
];
|
||||
actionGroups.push(rowActions);
|
||||
}
|
||||
return (
|
||||
<MenuContents
|
||||
anchorEl={anchorEle}
|
||||
open={Boolean(anchorEle)}
|
||||
handleClose={resetContextMenu}
|
||||
items={actions}
|
||||
groups={actionGroups}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,14 +45,8 @@ export default function FinalColumn({ row }: FormatterProps<any, any>) {
|
||||
color="inherit"
|
||||
disabled={!addRow}
|
||||
onClick={() => {
|
||||
const clonedRow = { ...row };
|
||||
// remove metadata
|
||||
delete clonedRow.ref;
|
||||
delete clonedRow.rowHeight;
|
||||
Object.keys(clonedRow).forEach((key) => {
|
||||
if (clonedRow[key] === undefined) delete clonedRow[key];
|
||||
});
|
||||
if (addRow) addRow!(clonedRow, undefined, { type: "smaller" });
|
||||
const { ref, ...clonedRow } = row;
|
||||
addRow!(clonedRow, undefined, { type: "smaller" });
|
||||
}}
|
||||
aria-label="Duplicate row"
|
||||
className="row-hover-iconButton"
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import _find from "lodash/find";
|
||||
import _get from "lodash/get";
|
||||
|
||||
import Cut from "@mui/icons-material/ContentCut";
|
||||
import CopyCells from "@src/assets/icons/CopyCells";
|
||||
import Paste from "@mui/icons-material/ContentPaste";
|
||||
import EvalIcon from "@mui/icons-material/Replay";
|
||||
|
||||
import ReEvalIcon from "@mui/icons-material/Replay";
|
||||
import EvalIcon from "@mui/icons-material/PlayCircle";
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { SelectedCell } from "@src/atoms/ContextMenu";
|
||||
import { getFieldProp, getColumnType } from "@src/components/fields";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
|
||||
export interface IContextMenuActions {
|
||||
@@ -22,7 +18,7 @@ export default function ContextMenuActions(
|
||||
selectedCell: SelectedCell,
|
||||
reset: () => void | Promise<void>
|
||||
): IContextMenuActions[] {
|
||||
const { tableState, deleteCell, updateCell, rowyRun } = useProjectContext();
|
||||
const { tableState, rowyRun } = useProjectContext();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const columns = tableState?.columns;
|
||||
const rows = tableState?.rows;
|
||||
@@ -30,18 +26,18 @@ export default function ContextMenuActions(
|
||||
const selectedColIndex = selectedCell?.colIndex;
|
||||
const selectedCol = _find(columns, { index: selectedColIndex });
|
||||
if (!selectedCol) return [];
|
||||
// don't show evalute button if function has external dependency
|
||||
const code =
|
||||
selectedCol.config.derivativeFn ?? selectedCol.config.script ?? "";
|
||||
if (code.includes("require(")) return [];
|
||||
const selectedRow = rows?.[selectedRowIndex];
|
||||
const cellValue = _get(selectedRow, selectedCol.key);
|
||||
console.log({
|
||||
selectedCol,
|
||||
schemaDocPath: tableState?.config.tableConfig.path,
|
||||
});
|
||||
const handleClose = async () => await reset?.();
|
||||
|
||||
const handleEvaluate = async () => {
|
||||
try {
|
||||
if (!selectedCol || !rowyRun || !selectedRow) return;
|
||||
rowyRun({
|
||||
const result = await rowyRun({
|
||||
route: runRoutes.evaluateDerivative,
|
||||
body: {
|
||||
ref: {
|
||||
@@ -51,13 +47,22 @@ export default function ContextMenuActions(
|
||||
columnKey: selectedCol.key,
|
||||
},
|
||||
});
|
||||
if (result.success === false) {
|
||||
enqueueSnackbar(result.message, { variant: "error" });
|
||||
}
|
||||
} catch (error) {
|
||||
enqueueSnackbar(`Failed: ${error}`, { variant: "error" });
|
||||
}
|
||||
handleClose();
|
||||
};
|
||||
const isEmpty =
|
||||
cellValue === "" || cellValue === null || cellValue === undefined;
|
||||
const contextMenuActions = [
|
||||
{ label: "evalute", icon: <EvalIcon />, onClick: handleEvaluate },
|
||||
{
|
||||
label: isEmpty ? "Evaluate" : "Re-evaluate",
|
||||
icon: isEmpty ? <EvalIcon /> : <ReEvalIcon />,
|
||||
onClick: handleEvaluate,
|
||||
},
|
||||
];
|
||||
|
||||
return contextMenuActions;
|
||||
|
||||
@@ -35,8 +35,6 @@ const rowsReducer = (prevRows: any, update: any) => {
|
||||
switch (update.type) {
|
||||
case "onSnapshot":
|
||||
const snapshotDocs = update.docs;
|
||||
console.log("onSnapshot", snapshotDocs);
|
||||
|
||||
// Get rows that may not be part of the snapshot
|
||||
// Rows with missing required fields haven’t been written to the db yet
|
||||
// Out of order rows will appear on top
|
||||
@@ -168,7 +166,6 @@ const useTableData = () => {
|
||||
|
||||
const unsubscribe = query.limit(limit).onSnapshot(
|
||||
(snapshot) => {
|
||||
console.log("snapshot", snapshot);
|
||||
// if (snapshot.docs.length > 0) {
|
||||
rowsDispatch({
|
||||
type: "onSnapshot",
|
||||
|
||||
Reference in New Issue
Block a user