import React, { ReactNode, useState } from "react"; import styles from "./OrgAst.module.css"; import { Fragment } from "react"; const OrgAst = (props: { setHighlight: Function; clearHighlights: Function; astTree: any; value: string; }) => { const [selectedNode, setSelectedNode] = useState( null, ); const [hoveredNode, setHoveredNode] = useState(null); function selectNode(uid: string, start: number, end: number) { if (selectedNode !== null && selectedNode.uid === uid) { props.setHighlight([]); setSelectedNode(null); setHoveredNode(null); } else { 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") { return
Error! {props.astTree.content}
; } else { return (
); } }; interface OrgNodeReference { uid: string; start: number; end: number; } const OrgAstNode = (props: { selectNode: Function; startHoverNode: Function; endHoverNode: Function; node: any; uid: string; selectedNode: OrgNodeReference | null; fullSource: string; }) => { const [isHovered, setIsHovered] = useState(false); function selectNode() { props.selectNode( props.uid, props.node["standard-properties"]["begin"], props.node["standard-properties"]["end"], ); } 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); } function unicodeAwareSlice(text: string, start: number, end: number) { // Boooo javascript let i = 0; let output = ""; for (const chr of text) { if (i >= end) { break; } if (i >= start) { output += chr; } ++i; } return output; } let nodeClassName = styles.OrgAstNode; if (props.selectedNode?.uid === props.uid) { nodeClassName = nodeClassName + " " + styles.selected; } if (isHovered) { nodeClassName = nodeClassName + " " + styles.hovered; } const selfSource = JSON.stringify( unicodeAwareSlice( props.fullSource, props.node["standard-properties"].begin - 1, props.node["standard-properties"].end - 1, ), ); if (props.node["ast-node"] === "plain-text") { return (
{props.node["ast-node"]} {selfSource}
); } return (
{props.node["ast-node"]} {selfSource}
Standard Properties
{!!Object.keys(props.node.properties).length ? ( <>
Properties
) : null} {Array.isArray(props.node.children) && props.node.children.length > 0 ? (
Children
) : null}
); }; const OrgAstNodeList = (props: { selectNode: Function; startHoverNode: Function; endHoverNode: Function; parentUniqueId: string; selectedNode: OrgNodeReference | null; node_list: any[]; fullSource: string; }): React.JSX.Element[] => { return props.node_list.map((node) => { const uid = props.parentUniqueId + "_" + node["ast-node"] + "/" + node["standard-properties"]["begin"] + "/" + node["standard-properties"]["end"] + "#"; return ( ); }); }; const OrgPropertiesList = (props: { selectNode: Function; startHoverNode: Function; endHoverNode: Function; parentUniqueId: string; selectedNode: OrgNodeReference | null; properties: Object; fullSource: string; }): React.JSX.Element => { const entries = Object.entries(props.properties) .sort((a, b) => { if (a[0] < b[0]) { return -1; } else if (a[0] > b[0]) { return 1; } else { return 0; } }) .filter((entry) => { return !(is_object(entry[1]) && entry[1]["noop"] == "Noop"); }) .map(([key, value]) => { return ( {key}: ); }); return ( {entries}
); }; const OrgPropertyValue = (props: { selectNode: Function; startHoverNode: Function; endHoverNode: Function; parentUniqueId: string; selectedNode: OrgNodeReference | null; value: any; fullSource: string; }): React.ReactNode => { if ( props.value === null || is_primitive(props.value) || (is_array(props.value) && props.value.length === 0) ) { return JSON.stringify(props.value); } else if (is_list_of_ast_nodes(props.value)) { return (
); } else if (is_optional_pair(props.value)) { return (
Optional value: {JSON.stringify(props.value.optval)}
Value: {JSON.stringify(props.value.val)}
); } else if (is_object_tree(props.value)) { return ( ); } else { alert("Unhandled property value! " + JSON.stringify(props.value)); } }; interface OrgObjectTreeProps { selectNode: Function; startHoverNode: Function; endHoverNode: Function; parentUniqueId: string; selectedNode: OrgNodeReference | null; value: any; fullSource: string; } function OrgObjectTree({ selectNode, startHoverNode, endHoverNode, parentUniqueId, selectedNode, value, fullSource, }: OrgObjectTreeProps): React.ReactNode { const entries = value["object-tree"].map((entry: any) => { return ( Optional value: Value: ); }); return {entries}
; } function is_object(val: any): boolean { return val instanceof Object && !(val instanceof Array); } function is_array(val: any): boolean { return val instanceof Array; } function is_primitive(val: any): boolean | null { if (val === null) { return null; } return !(val instanceof Object || val instanceof Array); } function is_list_of_ast_nodes(val: any): boolean { if (!is_array(val)) { return false; } return val.every((entry: any) => { return is_object(entry) && entry.hasOwnProperty("ast-node"); }); } function is_optional_pair(val: any): boolean { return ( is_object(val) && val.hasOwnProperty("optval") && val.hasOwnProperty("val") ); } function is_object_tree(val: any): boolean { return is_object(val) && val.hasOwnProperty("object-tree"); } export { OrgAst as default, OrgNodeReference };