diff --git a/client/src/App.scss b/client/src/App.scss index 9328f13..01e156d 100644 --- a/client/src/App.scss +++ b/client/src/App.scss @@ -492,6 +492,15 @@ fieldset { border:1px solid $greyyc-c} padding:0 1px 0 0; .sgspan { display: inline-block; width:100%; height:100%; padding:0 2px; } } + + .slick-row.active .slick-cell { + background-color: #d7edf7; + } + + .slick-row.active .slick-cell.active { + background-color: #c2e4f3; + box-shadow: inset 0 0 0 2px #1478a6; + } } @@ -514,6 +523,15 @@ fieldset { border:1px solid $greyyc-c} &.odd { background: #222222; } &.even { background: $bodybg-c; } .slick-cell { color: #f5f5f5; } + + &.active .slick-cell { + background-color: #174c61; + } + + &.active .slick-cell.active { + background-color: #1d6079; + box-shadow: inset 0 0 0 2px #67d8ff; + } } .slick-headerrow { diff --git a/client/src/components/AGrid.tsx b/client/src/components/AGrid.tsx index ca03c3e..abf6ea1 100644 --- a/client/src/components/AGrid.tsx +++ b/client/src/components/AGrid.tsx @@ -59,6 +59,7 @@ import { SetArgsType } from "../engine/queryEngine"; import { ThemeContext } from "../context"; import { DeepPartial } from "utility-types"; import { createAddlHeaderRow, doSparkLines, FilterMenu, FilterType, getFilterResult, getSlickFormatter } from "./AGridHelper"; +import { getGridCellSelection, GridCellPosition } from "./AGridSelection"; // Notice even though we currently only support bold/normal, we store them as same names as TextStyleFrow AND CSS. // This is in case we want to support more options in future. e.g. bolder/lighter. @@ -367,19 +368,28 @@ export default function AGrid(props:{ srs: SmartRs | null, subConfig:SubConfig, } }); + const updateSelectedArgs = (position:GridCellPosition) => { + const selection = getGridCellSelection(grid, position); + if(selection) { + props.setArgTyped({ series:selection.column.name, ...selectArgs(selection.item)}); + } + }; + + // SlickGrid emits this for both mouse selection and keyboard navigation. + grid.onActiveCellChanged.subscribe(function (e:any, position:GridCellPosition) { + updateSelectedArgs(position); + }); + // can't use this onClick as FlexLayout blocks the first ever click on a panel // grid.onClick.subscribe(function (e:any, args:{row:number, cell:number}) { - $('#'+id).on("mousedown", function (e) { const args = grid.getCellFromEvent(e); if(args && typeof args.cell == "number" && typeof args.row == "number") { grid.setActiveCell(args.row, args.cell); - const itm = grid.getDataItem(args.row) - const colDef = grid.getColumns()[args.cell]; - if(typeof itm === "object") { - props.setArgTyped({ series:colDef.name, ...selectArgs(itm)}); - const ahsToRun = subConfigRef.current.actionHandlers.filter(a => a.trigger === "Click" && (a.name === "" || a.name === colDef.name)); - props.actionRunner(ahsToRun, itm); + const selection = getGridCellSelection(grid, args); + if(selection) { + const ahsToRun = subConfigRef.current.actionHandlers.filter(a => a.trigger === "Click" && (a.name === "" || a.name === selection.column.name)); + props.actionRunner(ahsToRun, selection.item); } } }); diff --git a/client/src/components/AGridSelection.test.ts b/client/src/components/AGridSelection.test.ts new file mode 100644 index 0000000..f41b5ba --- /dev/null +++ b/client/src/components/AGridSelection.test.ts @@ -0,0 +1,23 @@ +import { getGridCellSelection } from "./AGridSelection"; + +describe("getGridCellSelection", () => { + const item = { symbol: "AAPL", price: 191.25 }; + const priceColumn = { name: "price" }; + const grid = { + getDataItem: (row: number) => row === 2 ? item : undefined, + getColumns: () => [{ name: "symbol" }, priceColumn], + }; + + it("returns the row item and active column", () => { + expect(getGridCellSelection(grid, { row: 2, cell: 1 })).toEqual({ + item, + column: priceColumn, + }); + }); + + it("ignores positions without a valid row item or column", () => { + expect(getGridCellSelection(grid, null)).toBeNull(); + expect(getGridCellSelection(grid, { row: 3, cell: 1 })).toBeNull(); + expect(getGridCellSelection(grid, { row: 2, cell: 4 })).toBeNull(); + }); +}); diff --git a/client/src/components/AGridSelection.ts b/client/src/components/AGridSelection.ts new file mode 100644 index 0000000..83211b1 --- /dev/null +++ b/client/src/components/AGridSelection.ts @@ -0,0 +1,23 @@ +export type GridCellPosition = { + row: number; + cell: number; +}; + +export type GridCellSelection = { + item: Record; + column: { name: string }; +}; + +export function getGridCellSelection(grid: any, position: GridCellPosition | null | undefined): GridCellSelection | null { + if (!position || !Number.isInteger(position.row) || !Number.isInteger(position.cell)) { + return null; + } + + const item = grid.getDataItem(position.row); + const column = grid.getColumns()?.[position.cell]; + if (item === null || typeof item !== "object" || !column) { + return null; + } + + return { item, column }; +}