diff --git a/src/Editor.tsx b/src/Editor.tsx index f371140..c6942d7 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -2,7 +2,7 @@ import React, { ReactNode, useState } from "react"; import "./Editor.css"; import { Highlight } from "./highlight"; import { buildShadow } from "./shadow"; -import OrgAst from "./OrgAst"; +import OrgAst, { OrgNodeReference } from "./OrgAst"; import { parse_org } from "../../organic/target/wasm32-unknown-unknown/js/wasm"; const default_org_source: string = `Welcome to the Organic Wasm Demo! @@ -31,8 +31,15 @@ function Editor({ defaultValue = default_org_source }) { const [highlights, setHighlights] = useState>([]); - function setHighlight(start: number, end: number) { - let new_highlights = [new Highlight(start, end)]; + function setHighlight(nodes: OrgNodeReference[]) { + let new_highlights = nodes.map((node: OrgNodeReference) => { + return new Highlight(node.start - 1, node.end - 1); + }); + new_highlights.sort(function (a, b) { + if (a.start < b.start) return -1; + if (a.start > b.start) return 1; + return 0; + }); setHighlights(new_highlights); } diff --git a/src/OrgAst.module.css b/src/OrgAst.module.css index 8a406d9..adaa885 100644 --- a/src/OrgAst.module.css +++ b/src/OrgAst.module.css @@ -35,6 +35,12 @@ } } +.OrgAstNode.hovered:not(.selected) { + > .OrgAstNodeType { + background: #70f8ba; + } +} + .OrgAstNodeType { font-size: 1.3rem; font-weight: 700; diff --git a/src/OrgAst.module.css.d.ts b/src/OrgAst.module.css.d.ts index f0a7a83..f2e03aa 100644 --- a/src/OrgAst.module.css.d.ts +++ b/src/OrgAst.module.css.d.ts @@ -3,5 +3,6 @@ export const OrgAstNode: string; export const OrgAstNodeType: string; export const OrgAstChildren: string; export const selected: string; +export const hovered: string; export const OrgAstProperties: string; export const OrgAstObjectTree: string; diff --git a/src/OrgAst.tsx b/src/OrgAst.tsx index 91d67a5..b8a31d9 100644 --- a/src/OrgAst.tsx +++ b/src/OrgAst.tsx @@ -8,11 +8,28 @@ const OrgAst = (props: { astTree: any; value: string; }) => { - const [selectedNode, setSelectedNode] = useState(""); + const [selectedNode, setSelectedNode] = useState( + null, + ); + const [hoveredNode, setHoveredNode] = useState(null); function selectNode(uid: string, start: number, end: number) { - props.setHighlight(start, end); - setSelectedNode(uid); + const new_node: OrgNodeReference = { uid: uid, start: start, end: end }; + props.setHighlight([new_node, hoveredNode].filter((node) => node !== null)); + setSelectedNode({ uid: uid, start: start, end: end }); + } + + function startHoverNode(uid: string, start: number, end: number) { + const new_node: OrgNodeReference = { uid: uid, start: start, end: end }; + props.setHighlight( + [selectedNode, new_node].filter((node) => node !== null), + ); + setHoveredNode({ uid: uid, start: start, end: end }); + } + + function endHoverNode(uid: string) { + props.setHighlight([selectedNode].filter((node) => node !== null)); + setHoveredNode(null); } if (props.astTree.status !== "success") { @@ -24,6 +41,8 @@ const OrgAst = (props: { key="^" uid="^" selectNode={selectNode} + startHoverNode={startHoverNode} + endHoverNode={endHoverNode} node={props.astTree.content} selectedNode={selectedNode} /> @@ -32,34 +51,68 @@ const OrgAst = (props: { } }; +interface OrgNodeReference { + uid: string; + start: number; + end: number; +} + const OrgAstNode = (props: { selectNode: Function; + startHoverNode: Function; + endHoverNode: Function; node: any; uid: string; - selectedNode: string; + selectedNode: OrgNodeReference | null; }) => { + const [isHovered, setIsHovered] = useState(false); + function selectNode() { props.selectNode( props.uid, - props.node["standard-properties"]["begin"] - 1, - props.node["standard-properties"]["end"] - 1, + props.node["standard-properties"]["begin"], + props.node["standard-properties"]["end"], ); } - const nodeClassName = - props.selectedNode === props.uid - ? styles.OrgAstNode + " " + styles.selected - : styles.OrgAstNode; + function hoverNode() { + props.startHoverNode( + props.uid, + props.node["standard-properties"]["begin"], + props.node["standard-properties"]["end"], + ); + setIsHovered(true); + } + + function endHoverNode() { + props.endHoverNode(props.uid); + setIsHovered(false); + } + + let nodeClassName = styles.OrgAstNode; + if (props.selectedNode?.uid === props.uid) { + nodeClassName = nodeClassName + " " + styles.selected; + } + if (isHovered) { + nodeClassName = nodeClassName + " " + styles.hovered; + } return (
-
+
{props.node["ast-node"]}
Standard Properties Properties { return props.node_list.map((node) => { @@ -116,6 +175,8 @@ const OrgAstNodeList = (props: { key={uid} uid={uid} selectNode={props.selectNode} + startHoverNode={props.startHoverNode} + endHoverNode={props.endHoverNode} selectedNode={props.selectedNode} node={node} /> @@ -125,8 +186,10 @@ const OrgAstNodeList = (props: { const OrgPropertiesList = (props: { selectNode: Function; + startHoverNode: Function; + endHoverNode: Function; parentUniqueId: string; - selectedNode: string; + selectedNode: OrgNodeReference | null; properties: Object; }): React.JSX.Element => { const entries = Object.entries(props.properties) @@ -150,6 +213,8 @@ const OrgPropertiesList = (props: { { if ( @@ -183,6 +250,8 @@ const OrgPropertyValue = (props: {