Skip to content

Commit

Permalink
Merge branch 'sarda-devesh/main'
Browse files Browse the repository at this point in the history
* sarda-devesh/main:
  Fixed deps
  Added single example of updated feedback component
  • Loading branch information
davenquinn committed Apr 30, 2024
2 parents 0b36fb3 + df412b9 commit 91cf816
Show file tree
Hide file tree
Showing 8 changed files with 648 additions and 247 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@
"prettier": {},
"dependencies": {
"@babel/preset-react": "^7.18.6",
"poplar-annotation": "^2.0.3"
"poplar-annotation": "^2.0.3",
"react-arborist": "^3.4.0",
"react-text-annotate-blend": "^1.2.0"
},
"packageManager": "[email protected]",
"dependenciesMeta": {
Expand Down
6 changes: 4 additions & 2 deletions packages/feedback-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
"author": "Daven Quinn",
"license": "ISC",
"dependencies": {
"@babel/preset-react": "^7.18.6",
"@blueprintjs/core": "^4.17.8",
"poplar-annotation": "^2.0.3",
"react": "^17.0.2||^18"
"react": "^17.0.2||^18",
"react-arborist": "^3.4.0",
"react-text-annotate-blend": "^1.2.0"
},
"devDependencies": {
"typescript": "^4.2.3||^5"
Expand Down
276 changes: 276 additions & 0 deletions packages/feedback-components/src/FeedbackWrap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import React, { useEffect, useRef, useState } from 'react';
import example_data from './tree_example.json';
import {StatefulBlendProps, StatefulBlend} from './TextVisualizer';
import { NodeApi, Tree, TreeApi } from "react-arborist";
import Node from './Node';
import {Entity, Result, TreeData} from './types';
import { TextAnnotateBlend, AnnotateBlendTag } from "react-text-annotate-blend";


function process_entity(paragraph : string, entity : Entity, depth: number) : TreeData {
// Record its children
let curr_children : TreeData[] = [];
if(entity.children) {
for(var child of entity.children) {
curr_children.push(process_entity(paragraph, child, depth + 1));
}
}

// Create the current node
let entity_tag = "" + depth;
return {
id : entity_tag + "_" + entity.txt_range[0][0] + "_" + entity.txt_range[0][1],
name: paragraph.substring(entity.txt_range[0][0], entity.txt_range[0][1]),
children : curr_children
}
}

function formatForVisualization(initial_tree : Result) : [string, TreeData[]] {
let paragraph : string = initial_tree.text;
let tree_entities : TreeData[] = [];
if(initial_tree.strats) {
for(var curr_strat of initial_tree.strats) {
tree_entities.push(process_entity(paragraph, curr_strat, 0));
}
}

let root : TreeData = {
id : "root",
name : "All entities",
children : tree_entities
};

return [paragraph, [root]];
}

function update_tree(current_node: TreeData, nodes_set: Set<string>) {
// Update the children
let new_children : TreeData[] = [];
for(var curr_child of current_node.children) {
if(nodes_set.has(curr_child.id)) {
// We want to keep this child
new_children.push(curr_child);
nodes_set.delete(curr_child.id);
} else {
// We don't want to keep this node so make its grand children its child
for(var grand_child of curr_child.children) {
nodes_set.delete(grand_child.id);
new_children.push(grand_child);
}
}
}

// Call update on each of the children
for(var curr_new_child of new_children) {
update_tree(curr_new_child, nodes_set);
}

// Update the children of this node
current_node.children = new_children;
}

function perform_reset(node : TreeData, depth: number) {
// Reset the depth
let old_id_parts = node.id.split("_");
let new_id = depth.toString() + "_" + old_id_parts[1] + "_" + old_id_parts[2];
node.id = new_id;

for(var child of node.children) {
perform_reset(child, depth + 1);
}
}

function removeNodes(current_node: TreeData, nodes_to_remove: Set<string>, removed_nodes : TreeData[]) {
let new_children : TreeData[] = [];
for(var curr_child of current_node.children) {
if(nodes_to_remove.has(curr_child.id)) {
// Record that we removed this node
removed_nodes.push(curr_child);
} else {
// Keep this child
new_children.push(curr_child);
}
}

// Process the children
for(var curr_new_child of new_children) {
removeNodes(curr_new_child, nodes_to_remove, removed_nodes);
}

// Update the children for this node
current_node.children = new_children;
}

function addInChild(current_node : TreeData, target_node: string, removed_nodes: TreeData[]) {
if(current_node.id.valueOf() == target_node.valueOf()) {
// Add in the children to the target node
for(var node_to_add of removed_nodes) {
current_node.children.push(node_to_add);
}
} else {
// This node is not the target node so its a child
for(var curr_child of current_node.children) {
addInChild(curr_child, target_node, removed_nodes);
}
}

}

function recordNode(node: TreeData, nodes_set : Set<string>) {
nodes_set.add(node.id);
for(var child of node.children) {
recordNode(child, nodes_set);
}
}

export function FeedbackWrap({data}) {
let input_data : Result = data;
let [current_text, tree_entities] : [string, TreeData[]] = formatForVisualization(input_data);
let [current_tree, setTree] = useState(tree_entities);
let no_nodes : string[] = [];
let [nodes_to_show, setNodesToShow] = useState(no_nodes);

// Processing update from the text visualization
let process_update = (nodes: string[]) => {
let nodes_set = new Set<string>(nodes);
let old_root = current_tree[0];
let new_root = JSON.parse(JSON.stringify(old_root));
console.log("Old root of", old_root);

// Update the tree
console.log("Nodes to keep", nodes);
update_tree(new_root, nodes_set);
console.log("New root of", new_root);

// Add the remaining children as
nodes_set.forEach((node) => {
// Get the id details
let node_parts = node.split("_");
let start_idx : number = parseInt(node_parts[1]);
let end_idx : number = parseInt(node_parts[2]);

// Create the new node
let new_id : string = "0_" + start_idx.toString() + "_" + end_idx.toString();
let name : string = current_text.substring(start_idx, end_idx);
new_root.children.push({
id : new_id,
name : name,
children: []
})
});

// Reset the level of all of nodes
for(var root_child of new_root.children) {
perform_reset(root_child, 0);
}

setTree([new_root]);
};

// If the user rename the tree
const treeRef = useRef();
const onMove = ({ dragIds, parentId, index } : any) => {
// Cast the input data
let input_children_ids : string[] = dragIds;
let input_parent_id : string = parentId;
let input_index : number = index;

// Create the new root
let old_root = current_tree[0];
let new_root = JSON.parse(JSON.stringify(old_root));

// Remove the children from there location
let nodes_to_remove : Set<string> = new Set<string>(input_children_ids);
let removed_nodes : TreeData[] = [];
let updated_children: TreeData[] = [];
for(var root_child of new_root.children) {
if(nodes_to_remove.has(root_child.id)) {
// Don't record this is a root child
removed_nodes.push(root_child);
} else {
// Record it as a child
removeNodes(root_child, nodes_to_remove, removed_nodes);
updated_children.push(root_child);
}
}
new_root.children = updated_children;

if(input_parent_id == "root") {
// Add in the children to the root
for(var node_to_add of removed_nodes) {
new_root.children.push(node_to_add);
}
} else {
// Add in to the target node
for(var root_child of new_root.children) {
addInChild(root_child, input_parent_id, removed_nodes);
}
}

// Reset the level of all of nodes
for(var root_child of new_root.children) {
perform_reset(root_child, 0);
}
setTree([new_root]);
};

const onDelete = ({ ids } : any) => {
let delete_ids : string[] = ids;

// Create the new root
let old_root = current_tree[0];
let new_root = JSON.parse(JSON.stringify(old_root));

// Remove the children from there location
let nodes_to_remove : Set<string> = new Set<string>(delete_ids);
let removed_nodes : TreeData[] = [];
let updated_children: TreeData[] = [];
for(var root_child of new_root.children) {
if(nodes_to_remove.has(root_child.id)) {
// Don't record this is a root child
removed_nodes.push(root_child);
} else {
// Record it as a child
removeNodes(root_child, nodes_to_remove, removed_nodes);
updated_children.push(root_child);
}
}
new_root.children = updated_children;

// Reset the level of all of nodes
for(var root_child of new_root.children) {
perform_reset(root_child, 0);
}
setTree([new_root]);
};

const onSelect = (nodes : NodeApi<TreeData>[]) => {
let nodes_to_show = new Set<string>();
for(var curr_node of nodes) {
recordNode(curr_node.data, nodes_to_show);
}

setNodesToShow(Array.from(nodes_to_show));
};

return (
<>
<div style={{display:"grid", gridTemplateColumns:"1fr 1fr"}}>
<div>
<p>
Click on a entity to see its type as well as the type of its children.
You can also drag and drop entities up and down the heirachy, thus changing their type.
</p>
<Tree data={current_tree} ref={treeRef} onMove={onMove} onDelete={onDelete} onSelect={onSelect}>
{Node}
</Tree>
</div>

<StatefulBlend formatted_text={current_text} tree_data={current_tree[0].children} update_nodes={process_update} nodes_to_show={nodes_to_show} />
</div>
</>
);

}

export default FeedbackWrap;
58 changes: 58 additions & 0 deletions packages/feedback-components/src/Node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { AiFillFolder, AiFillFile } from "react-icons/ai";
import { MdArrowRight, MdArrowDropDown, MdEdit } from "react-icons/md";
import { RxCross2 } from "react-icons/rx";
import { NodeApi, Tree, TreeApi } from "react-arborist";
import React from "react";
import {Entity, Child, Result, TreeData} from './types';

function isSelected(search_node : TreeData, tree_node: TreeData) {
if(search_node.id == tree_node.id) {
return true;
}

for(var child of tree_node.children) {
if(isSelected(search_node, child)) {
return true;
}
}

return false;
}

function isNodeSelected(node : NodeApi<TreeData>, tree: TreeApi<TreeData>) {
for(var selected_node of tree.selectedNodes) {
if(isSelected(node.data, selected_node.data)) {
return true;
}
}

return false;
}

type COLOR_TYPE = {
[key: string]: string;
0: string;
1: string;
2: string;
};

const COLORS: COLOR_TYPE = {
0: "rgb(179, 245, 66)",
1: "#42f5f5",
2: "#4b46cd",
};

const Node = ({ node, style, dragHandle, tree } : any) => {
let selected : boolean = isNodeSelected(node, tree);
let node_level : string = node.data.id.split("_")[0];
let nameStyle = selected ? { backgroundColor: COLORS[node_level] } : {};

return (
<div style={{ ...style, ...nameStyle }} ref={dragHandle}>
{"🍁"}
{node.data.name}
</div>
);
};

export default Node;
Loading

0 comments on commit 91cf816

Please sign in to comment.