@macrostrat/feedback-components 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/esm/{feedback-components.7cd9b6cc.js → feedback-components.3b3a5357.js} +53 -30
- package/dist/esm/feedback-components.3b3a5357.js.map +1 -0
- package/dist/esm/{feedback-components.921dcd46.js → feedback-components.46a7a347.js} +42 -14
- package/dist/esm/feedback-components.46a7a347.js.map +1 -0
- package/dist/esm/{feedback-components.87533431.js → feedback-components.5509fab3.js} +5 -5
- package/dist/esm/{feedback-components.87533431.js.map → feedback-components.5509fab3.js.map} +1 -1
- package/dist/esm/{feedback-components.6a6c8af5.js → feedback-components.586103e8.js} +108 -82
- package/dist/esm/feedback-components.586103e8.js.map +1 -0
- package/dist/esm/{feedback-components.4359bc80.js → feedback-components.95dbe7d7.js} +13 -1
- package/dist/esm/feedback-components.95dbe7d7.js.map +1 -0
- package/dist/esm/{feedback-components.bf5f7cf7.js → feedback-components.fa1d3641.js} +29 -2
- package/dist/esm/feedback-components.fa1d3641.js.map +1 -0
- package/dist/esm/{feedback-components.b7d9b015.css → feedback-components.fb60c70d.css} +38 -14
- package/dist/esm/feedback-components.fb60c70d.css.map +1 -0
- package/dist/esm/index.d.ts +3 -82
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/node/feedback-components.561466ac.js +2 -0
- package/dist/node/feedback-components.561466ac.js.map +1 -0
- package/dist/node/feedback-components.571ee23c.js +2 -0
- package/dist/node/feedback-components.571ee23c.js.map +1 -0
- package/dist/node/{feedback-components.e8aa70b8.js → feedback-components.a39f7653.js} +2 -2
- package/dist/node/{feedback-components.e8aa70b8.js.map → feedback-components.a39f7653.js.map} +1 -1
- package/dist/node/feedback-components.b7946db4.js +2 -0
- package/dist/node/feedback-components.b7946db4.js.map +1 -0
- package/dist/node/feedback-components.c459cc27.js +2 -0
- package/dist/node/feedback-components.c459cc27.js.map +1 -0
- package/dist/node/feedback-components.c88cb37f.css +2 -0
- package/dist/node/feedback-components.c88cb37f.css.map +1 -0
- package/dist/node/feedback-components.ec54a1e7.js +2 -0
- package/dist/node/feedback-components.ec54a1e7.js.map +1 -0
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +3 -3
- package/src/feedback/edit-state.ts +41 -3
- package/src/feedback/feedback.module.sass +30 -10
- package/src/feedback/graph.ts +27 -10
- package/src/feedback/index.ts +146 -112
- package/src/feedback/text-visualizer.ts +43 -17
- package/dist/esm/feedback-components.4359bc80.js.map +0 -1
- package/dist/esm/feedback-components.6a6c8af5.js.map +0 -1
- package/dist/esm/feedback-components.7cd9b6cc.js.map +0 -1
- package/dist/esm/feedback-components.921dcd46.js.map +0 -1
- package/dist/esm/feedback-components.b7d9b015.css.map +0 -1
- package/dist/esm/feedback-components.bf5f7cf7.js.map +0 -1
- package/dist/node/feedback-components.15e1316d.js +0 -2
- package/dist/node/feedback-components.15e1316d.js.map +0 -1
- package/dist/node/feedback-components.65d8488e.js +0 -2
- package/dist/node/feedback-components.65d8488e.js.map +0 -1
- package/dist/node/feedback-components.6681dbde.js +0 -2
- package/dist/node/feedback-components.6681dbde.js.map +0 -1
- package/dist/node/feedback-components.77b6fc89.css +0 -2
- package/dist/node/feedback-components.77b6fc89.css.map +0 -1
- package/dist/node/feedback-components.7caa447a.js +0 -2
- package/dist/node/feedback-components.7caa447a.js.map +0 -1
- package/dist/node/feedback-components.e2f3c4b7.js +0 -2
- package/dist/node/feedback-components.e2f3c4b7.js.map +0 -1
|
@@ -47,7 +47,8 @@ type TreeAction =
|
|
|
47
47
|
| {
|
|
48
48
|
type: "update-entity-type";
|
|
49
49
|
payload: { id: number; name: string; description: string; color: string };
|
|
50
|
-
}
|
|
50
|
+
}
|
|
51
|
+
| { type: "select-range"; payload: { ids: number[] } };
|
|
51
52
|
|
|
52
53
|
export type TreeDispatch = Dispatch<TreeAction>;
|
|
53
54
|
|
|
@@ -134,6 +135,27 @@ function treeReducer(state: TreeState, action: TreeAction) {
|
|
|
134
135
|
selectedEntityType: updatedType,
|
|
135
136
|
};
|
|
136
137
|
}
|
|
138
|
+
case "select-range":
|
|
139
|
+
// Select a range of nodes by their IDs
|
|
140
|
+
const payloadIds = action.payload.ids;
|
|
141
|
+
const node1 = payloadIds[0];
|
|
142
|
+
const node2 = payloadIds[1];
|
|
143
|
+
|
|
144
|
+
// make list of nodes in order
|
|
145
|
+
const allNodes = flattenAndSort(state.tree);
|
|
146
|
+
|
|
147
|
+
// select all nodes between node1 and node2
|
|
148
|
+
const startIndex = allNodes.findIndex((node) => node.id === node1);
|
|
149
|
+
const endIndex = allNodes.findIndex((node) => node.id === node2);
|
|
150
|
+
|
|
151
|
+
const selectedNodes = allNodes.slice(startIndex, endIndex + 1);
|
|
152
|
+
|
|
153
|
+
console.log("Selecting range:", selectedNodes);
|
|
154
|
+
return {
|
|
155
|
+
...state,
|
|
156
|
+
selectedNodes: selectedNodes.map((node) => node.id),
|
|
157
|
+
};
|
|
158
|
+
|
|
137
159
|
case "move-node":
|
|
138
160
|
// For each node in the tree, if the node is in the dragIds, remove it from the tree and collect it
|
|
139
161
|
const [newTree, removedNodes] = removeNodes(
|
|
@@ -183,8 +205,6 @@ function treeReducer(state: TreeState, action: TreeAction) {
|
|
|
183
205
|
? findNodeById(state.tree, ids[0])?.type
|
|
184
206
|
: null;
|
|
185
207
|
|
|
186
|
-
console.log("Selecting nodes:", ids, "Type:", type);
|
|
187
|
-
|
|
188
208
|
return { ...state, selectedNodes: ids, selectedEntityType: type };
|
|
189
209
|
// otherwise fall through to toggle-node-selected for a single ID
|
|
190
210
|
case "toggle-node-selected":
|
|
@@ -439,3 +459,21 @@ function updateNodeType(node, oldType, defaultType) {
|
|
|
439
459
|
: [],
|
|
440
460
|
};
|
|
441
461
|
}
|
|
462
|
+
|
|
463
|
+
function flattenAndSort(nodes) {
|
|
464
|
+
const result = [];
|
|
465
|
+
|
|
466
|
+
function traverse(nodeList) {
|
|
467
|
+
for (const node of nodeList) {
|
|
468
|
+
result.push(node);
|
|
469
|
+
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
470
|
+
traverse(node.children);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
traverse(nodes);
|
|
476
|
+
|
|
477
|
+
// sort by start
|
|
478
|
+
return result.sort((a, b) => a.indices[0] - b.indices[0]);
|
|
479
|
+
}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
:root
|
|
2
|
+
--text-line-height: 3em
|
|
3
|
+
--main-extra-width: 200px
|
|
4
|
+
|
|
5
|
+
.page-wrapper
|
|
6
|
+
display: flex
|
|
7
|
+
flex-direction: row
|
|
8
|
+
position: relative
|
|
9
|
+
gap: 2em
|
|
10
|
+
align-items: flex-start // makes control-content lose stick
|
|
11
|
+
|
|
12
|
+
.feedback-container
|
|
13
|
+
flex: 4
|
|
14
|
+
|
|
15
|
+
.control-panel
|
|
16
|
+
flex: 1
|
|
17
|
+
height: auto
|
|
18
|
+
|
|
19
|
+
.control-content
|
|
20
|
+
position: sticky
|
|
21
|
+
top: 0
|
|
22
|
+
|
|
1
23
|
.feedback-component
|
|
2
24
|
position: relative
|
|
3
25
|
width: 800px
|
|
@@ -21,14 +43,7 @@ circle
|
|
|
21
43
|
.entity-panel
|
|
22
44
|
position: relative
|
|
23
45
|
max-height: 600px
|
|
24
|
-
|
|
25
|
-
.control-panel
|
|
26
|
-
position: absolute
|
|
27
|
-
top: 1em
|
|
28
|
-
right: 1em
|
|
29
|
-
padding: 0.2em 0.5em
|
|
30
|
-
|
|
31
|
-
.entity-panel
|
|
46
|
+
width: calc(100% - 2em)
|
|
32
47
|
flex: 1
|
|
33
48
|
min-height: 100px
|
|
34
49
|
padding: 1em
|
|
@@ -40,7 +55,6 @@ circle
|
|
|
40
55
|
.selection-tree
|
|
41
56
|
margin: -1em 0
|
|
42
57
|
padding: 1em 0
|
|
43
|
-
width: 70% !important
|
|
44
58
|
|
|
45
59
|
.type-list
|
|
46
60
|
display: grid
|
|
@@ -75,6 +89,7 @@ mark
|
|
|
75
89
|
position: relative
|
|
76
90
|
z-index: 0
|
|
77
91
|
overflow: visible
|
|
92
|
+
line-height: var(--text-line-height)
|
|
78
93
|
|
|
79
94
|
.type-container
|
|
80
95
|
display: flex
|
|
@@ -126,4 +141,9 @@ mark
|
|
|
126
141
|
|
|
127
142
|
.icons
|
|
128
143
|
display: flex
|
|
129
|
-
gap: .25em
|
|
144
|
+
gap: .25em
|
|
145
|
+
|
|
146
|
+
.node-label
|
|
147
|
+
fill: var(--text-emphasized-color)
|
|
148
|
+
fontSize: 10px
|
|
149
|
+
pointerEvents: none
|
package/src/feedback/graph.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TreeData } from "./types";
|
|
2
2
|
import { treeToGraph } from "./edit-state";
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import styles from "./feedback.module.sass";
|
|
4
|
+
import hyper from "@macrostrat/hyper";
|
|
5
5
|
import {
|
|
6
6
|
forceSimulation,
|
|
7
7
|
SimulationNodeDatum,
|
|
@@ -12,10 +12,12 @@ import {
|
|
|
12
12
|
forceCollide,
|
|
13
13
|
} from "d3-force";
|
|
14
14
|
import { useEffect, useState } from "react";
|
|
15
|
-
import { Spinner,
|
|
15
|
+
import { Spinner, Switch } from "@blueprintjs/core";
|
|
16
16
|
import { ErrorBoundary } from "@macrostrat/ui-components";
|
|
17
17
|
import { getTagStyle } from "../extractions";
|
|
18
18
|
|
|
19
|
+
const h = hyper.styled(styles);
|
|
20
|
+
|
|
19
21
|
export function GraphView(props: {
|
|
20
22
|
tree: TreeData[];
|
|
21
23
|
width: number;
|
|
@@ -29,6 +31,7 @@ export function GraphView(props: {
|
|
|
29
31
|
|
|
30
32
|
const [nodes, setNodes] = useState<SimulationNodeDatum[]>(null);
|
|
31
33
|
const [links, setLinks] = useState<SimulationLinkDatum[]>(null);
|
|
34
|
+
const [showLabels, setShowLabels] = useState(false);
|
|
32
35
|
|
|
33
36
|
useEffect(() => {
|
|
34
37
|
const { nodes, edges } = treeToGraph(tree);
|
|
@@ -78,6 +81,12 @@ export function GraphView(props: {
|
|
|
78
81
|
description: "An error occurred while rendering the graph view.",
|
|
79
82
|
},
|
|
80
83
|
h("div.graph-view", { style: { width, height } }, [
|
|
84
|
+
h(Switch, {
|
|
85
|
+
className: "show-labels-switch",
|
|
86
|
+
label: "Show Labels",
|
|
87
|
+
checked: showLabels,
|
|
88
|
+
onChange: (e) => setShowLabels(e.target.checked),
|
|
89
|
+
}),
|
|
81
90
|
h("svg", { width, height }, [
|
|
82
91
|
h(
|
|
83
92
|
"g.links",
|
|
@@ -99,12 +108,11 @@ export function GraphView(props: {
|
|
|
99
108
|
const highlighted = isHighlighted(d.id, selectedNodes, nodes);
|
|
100
109
|
const style = getTagStyle(d.color, { highlighted, active });
|
|
101
110
|
|
|
102
|
-
return h(
|
|
103
|
-
"circle",
|
|
104
|
-
{
|
|
111
|
+
return h("g", [
|
|
112
|
+
h("circle", {
|
|
105
113
|
cx: d.x,
|
|
106
114
|
cy: d.y,
|
|
107
|
-
r:
|
|
115
|
+
r: 8,
|
|
108
116
|
fill: style.backgroundColor || "blue",
|
|
109
117
|
onClick: (e) => {
|
|
110
118
|
e.stopPropagation();
|
|
@@ -116,9 +124,18 @@ export function GraphView(props: {
|
|
|
116
124
|
className: active ? "selected" : "",
|
|
117
125
|
stroke,
|
|
118
126
|
strokeWidth: 2,
|
|
119
|
-
},
|
|
120
|
-
h(
|
|
121
|
-
|
|
127
|
+
}),
|
|
128
|
+
h.if(showLabels)(
|
|
129
|
+
"text",
|
|
130
|
+
{
|
|
131
|
+
x: d.x + 10,
|
|
132
|
+
y: d.y + 4,
|
|
133
|
+
className: "node-label",
|
|
134
|
+
},
|
|
135
|
+
d.name || `Node ${d.id}`,
|
|
136
|
+
),
|
|
137
|
+
h.if(!showLabels)("title", d.name || `Node ${d.id}`),
|
|
138
|
+
]);
|
|
122
139
|
}),
|
|
123
140
|
),
|
|
124
141
|
]),
|
package/src/feedback/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import Node from "./node";
|
|
|
6
6
|
import { FeedbackText } from "./text-visualizer";
|
|
7
7
|
import type { InternalEntity, TreeData } from "./types";
|
|
8
8
|
import type { Entity } from "../extractions";
|
|
9
|
-
import { ModelInfo } from "../extractions";
|
|
9
|
+
import { getTagStyle, ModelInfo } from "../extractions";
|
|
10
10
|
import {
|
|
11
11
|
TreeDispatchContext,
|
|
12
12
|
treeToGraph,
|
|
@@ -59,7 +59,6 @@ export function FeedbackComponent({
|
|
|
59
59
|
entityTypes,
|
|
60
60
|
matchComponent,
|
|
61
61
|
onSave,
|
|
62
|
-
lineHeight,
|
|
63
62
|
allowOverlap,
|
|
64
63
|
}) {
|
|
65
64
|
// Get the input arguments
|
|
@@ -78,112 +77,122 @@ export function FeedbackComponent({
|
|
|
78
77
|
|
|
79
78
|
const [{ width, height }, ref] = useElementDimensions();
|
|
80
79
|
|
|
81
|
-
return h(
|
|
80
|
+
return h("div.page-wrapper", [
|
|
82
81
|
h(
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
selectedNodes,
|
|
94
|
-
lineHeight,
|
|
95
|
-
allowOverlap,
|
|
96
|
-
}),
|
|
97
|
-
),
|
|
98
|
-
h(FlexRow, { alignItems: "baseline", justifyContent: "space-between" }, [
|
|
99
|
-
h(ModelInfo, { data: model }),
|
|
100
|
-
h(SegmentedControl, {
|
|
101
|
-
options: [
|
|
102
|
-
{ label: "Tree", value: "tree" },
|
|
103
|
-
{ label: "Graph", value: "graph" },
|
|
104
|
-
],
|
|
105
|
-
value: state.viewMode,
|
|
106
|
-
small: true,
|
|
107
|
-
onValueChange(value: ViewMode) {
|
|
108
|
-
console.log("Setting view mode", value);
|
|
109
|
-
dispatch({ type: "set-view-mode", payload: value });
|
|
110
|
-
},
|
|
111
|
-
}),
|
|
112
|
-
]),
|
|
113
|
-
h(
|
|
114
|
-
"div.entity-panel",
|
|
115
|
-
{
|
|
116
|
-
ref,
|
|
117
|
-
},
|
|
118
|
-
[
|
|
119
|
-
h(Card, { className: "control-panel" }, [
|
|
120
|
-
h(
|
|
121
|
-
ButtonGroup,
|
|
122
|
-
{
|
|
123
|
-
vertical: true,
|
|
124
|
-
fill: true,
|
|
125
|
-
minimal: true,
|
|
126
|
-
alignText: "left",
|
|
127
|
-
},
|
|
128
|
-
[
|
|
129
|
-
h(
|
|
130
|
-
CancelButton,
|
|
131
|
-
{
|
|
132
|
-
icon: "trash",
|
|
133
|
-
disabled: state.initialTree == state.tree,
|
|
134
|
-
onClick() {
|
|
135
|
-
dispatch({ type: "reset" });
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
"Reset",
|
|
139
|
-
),
|
|
140
|
-
h(
|
|
141
|
-
SaveButton,
|
|
142
|
-
{
|
|
143
|
-
onClick() {
|
|
144
|
-
onSave(state.tree);
|
|
145
|
-
},
|
|
146
|
-
disabled: state.initialTree == state.tree,
|
|
147
|
-
},
|
|
148
|
-
"Save",
|
|
149
|
-
),
|
|
150
|
-
],
|
|
151
|
-
),
|
|
152
|
-
h(Divider),
|
|
153
|
-
h(EntityTypeSelector, {
|
|
154
|
-
entityTypes: entityTypesMap,
|
|
155
|
-
selected: selectedEntityType,
|
|
156
|
-
onChange(payload) {
|
|
157
|
-
dispatch({ type: "select-entity-type", payload });
|
|
158
|
-
},
|
|
82
|
+
"div.feedback-container",
|
|
83
|
+
h(TreeDispatchContext.Provider, { value: dispatch }, [
|
|
84
|
+
h(
|
|
85
|
+
ErrorBoundary,
|
|
86
|
+
{
|
|
87
|
+
description:
|
|
88
|
+
"An error occurred while rendering the feedback text component.",
|
|
89
|
+
},
|
|
90
|
+
h(FeedbackText, {
|
|
91
|
+
text,
|
|
159
92
|
dispatch,
|
|
160
|
-
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
nodes: tree,
|
|
161
95
|
selectedNodes,
|
|
162
|
-
|
|
163
|
-
setOpen: (isOpen: boolean) =>
|
|
164
|
-
dispatch({
|
|
165
|
-
type: "toggle-entity-type-selector",
|
|
166
|
-
payload: isOpen,
|
|
167
|
-
}),
|
|
96
|
+
allowOverlap,
|
|
168
97
|
}),
|
|
169
|
-
|
|
170
|
-
h
|
|
171
|
-
|
|
98
|
+
),
|
|
99
|
+
h(
|
|
100
|
+
FlexRow,
|
|
101
|
+
{ alignItems: "baseline", justifyContent: "space-between" },
|
|
102
|
+
[
|
|
103
|
+
h(ModelInfo, { data: model }),
|
|
104
|
+
h(SegmentedControl, {
|
|
105
|
+
options: [
|
|
106
|
+
{ label: "Tree", value: "tree" },
|
|
107
|
+
{ label: "Graph", value: "graph" },
|
|
108
|
+
],
|
|
109
|
+
value: state.viewMode,
|
|
110
|
+
small: true,
|
|
111
|
+
onValueChange(value: ViewMode) {
|
|
112
|
+
console.log("Setting view mode", value);
|
|
113
|
+
dispatch({ type: "set-view-mode", payload: value });
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
],
|
|
117
|
+
),
|
|
118
|
+
h(
|
|
119
|
+
"div.entity-panel",
|
|
120
|
+
{
|
|
121
|
+
ref,
|
|
122
|
+
},
|
|
123
|
+
[
|
|
124
|
+
h.if(state.viewMode == "tree")(ManagedSelectionTree, {
|
|
125
|
+
selectedNodes,
|
|
126
|
+
dispatch,
|
|
127
|
+
tree,
|
|
128
|
+
width,
|
|
129
|
+
height,
|
|
130
|
+
matchComponent,
|
|
131
|
+
}),
|
|
132
|
+
h.if(state.viewMode == "graph")(GraphView, {
|
|
133
|
+
tree,
|
|
134
|
+
width,
|
|
135
|
+
height,
|
|
136
|
+
dispatch,
|
|
137
|
+
selectedNodes,
|
|
138
|
+
}),
|
|
139
|
+
],
|
|
140
|
+
),
|
|
141
|
+
]),
|
|
142
|
+
),
|
|
143
|
+
h(Card, { className: "control-panel" }, [
|
|
144
|
+
h("div.control-content", [
|
|
145
|
+
h(
|
|
146
|
+
ButtonGroup,
|
|
147
|
+
{
|
|
148
|
+
vertical: true,
|
|
149
|
+
fill: true,
|
|
150
|
+
minimal: true,
|
|
151
|
+
alignText: "left",
|
|
152
|
+
},
|
|
153
|
+
[
|
|
154
|
+
h(
|
|
155
|
+
CancelButton,
|
|
156
|
+
{
|
|
157
|
+
icon: "trash",
|
|
158
|
+
disabled: state.initialTree == state.tree,
|
|
159
|
+
onClick() {
|
|
160
|
+
dispatch({ type: "reset" });
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
"Reset",
|
|
164
|
+
),
|
|
165
|
+
h(
|
|
166
|
+
SaveButton,
|
|
167
|
+
{
|
|
168
|
+
onClick() {
|
|
169
|
+
onSave(state.tree);
|
|
170
|
+
},
|
|
171
|
+
disabled: state.initialTree == state.tree,
|
|
172
|
+
},
|
|
173
|
+
"Save",
|
|
174
|
+
),
|
|
175
|
+
],
|
|
176
|
+
),
|
|
177
|
+
h(Divider),
|
|
178
|
+
h(EntityTypeSelector, {
|
|
179
|
+
entityTypes: entityTypesMap,
|
|
180
|
+
selected: selectedEntityType,
|
|
181
|
+
onChange(payload) {
|
|
182
|
+
dispatch({ type: "select-entity-type", payload });
|
|
183
|
+
},
|
|
172
184
|
dispatch,
|
|
173
185
|
tree,
|
|
174
|
-
width,
|
|
175
|
-
height,
|
|
176
|
-
matchComponent,
|
|
177
|
-
}),
|
|
178
|
-
h.if(state.viewMode == "graph")(GraphView, {
|
|
179
|
-
tree,
|
|
180
|
-
width,
|
|
181
|
-
height,
|
|
182
|
-
dispatch,
|
|
183
186
|
selectedNodes,
|
|
187
|
+
isOpen: isSelectingEntityType,
|
|
188
|
+
setOpen: (isOpen: boolean) =>
|
|
189
|
+
dispatch({
|
|
190
|
+
type: "toggle-entity-type-selector",
|
|
191
|
+
payload: isOpen,
|
|
192
|
+
}),
|
|
184
193
|
}),
|
|
185
|
-
],
|
|
186
|
-
),
|
|
194
|
+
]),
|
|
195
|
+
]),
|
|
187
196
|
]);
|
|
188
197
|
}
|
|
189
198
|
|
|
@@ -296,18 +305,39 @@ function ManagedSelectionTree(props) {
|
|
|
296
305
|
clickedRef.current = true;
|
|
297
306
|
}
|
|
298
307
|
|
|
308
|
+
const ctrlPressedRef = useRef(false);
|
|
309
|
+
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
const down = (e) => {
|
|
312
|
+
if (e.ctrlKey || e.metaKey) ctrlPressedRef.current = true;
|
|
313
|
+
};
|
|
314
|
+
const up = () => (ctrlPressedRef.current = false);
|
|
315
|
+
|
|
316
|
+
window.addEventListener("keydown", down);
|
|
317
|
+
window.addEventListener("keyup", up);
|
|
318
|
+
return () => {
|
|
319
|
+
window.removeEventListener("keydown", down);
|
|
320
|
+
window.removeEventListener("keyup", up);
|
|
321
|
+
};
|
|
322
|
+
}, []);
|
|
323
|
+
|
|
299
324
|
const handleSelect = useCallback(
|
|
300
325
|
(nodes) => {
|
|
301
326
|
if (!clickedRef.current) return;
|
|
302
327
|
clickedRef.current = false;
|
|
303
|
-
|
|
328
|
+
const isMultiSelect = ctrlPressedRef.current;
|
|
304
329
|
|
|
305
330
|
let ids = nodes.map((d) => parseInt(d.id));
|
|
306
|
-
if (ids.length === 1 && ids[0] === selectedNodes[0]) {
|
|
307
|
-
ids = [];
|
|
308
|
-
}
|
|
309
331
|
|
|
310
|
-
|
|
332
|
+
if (isMultiSelect) {
|
|
333
|
+
dispatch({ type: "toggle-node-selected", payload: { ids } });
|
|
334
|
+
} else {
|
|
335
|
+
if (ids.length === 1 && ids[0] === selectedNodes[0]) {
|
|
336
|
+
ids = [];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
dispatch({ type: "select-node", payload: { ids } });
|
|
340
|
+
}
|
|
311
341
|
},
|
|
312
342
|
[selectedNodes, dispatch],
|
|
313
343
|
);
|
|
@@ -544,8 +574,13 @@ function TypeTag({
|
|
|
544
574
|
isSelectedNodes,
|
|
545
575
|
}) {
|
|
546
576
|
const { color, name, id, description } = type;
|
|
547
|
-
const chromaColor = asChromaColor(color ?? "#000000");
|
|
548
577
|
const darkMode = useInDarkMode();
|
|
578
|
+
const isSelected = id === selected?.id && selectedNodes.length > 0;
|
|
579
|
+
|
|
580
|
+
const style = getTagStyle(color, {
|
|
581
|
+
active: isSelected,
|
|
582
|
+
highlighted: selectedNodes.length === 0,
|
|
583
|
+
});
|
|
549
584
|
|
|
550
585
|
const payload = {
|
|
551
586
|
id,
|
|
@@ -610,12 +645,11 @@ function TypeTag({
|
|
|
610
645
|
ids.length > 0 || (isSelectedNodes && !selectedType)
|
|
611
646
|
? "pointer"
|
|
612
647
|
: "",
|
|
613
|
-
color:
|
|
614
|
-
backgroundColor:
|
|
615
|
-
border:
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
: `1px solid var(--background-color)`,
|
|
648
|
+
color: "black",
|
|
649
|
+
backgroundColor: style.backgroundColor,
|
|
650
|
+
border: isSelected
|
|
651
|
+
? `1px solid var(--text-emphasized-color)`
|
|
652
|
+
: `1px solid var(--background-color)`,
|
|
619
653
|
},
|
|
620
654
|
},
|
|
621
655
|
h("div.type-container", [
|
|
@@ -73,8 +73,7 @@ function isHighlighted(tag: Highlight, selectedNodes: number[]) {
|
|
|
73
73
|
|
|
74
74
|
export function FeedbackText(props: FeedbackTextProps) {
|
|
75
75
|
// Convert input to tags
|
|
76
|
-
const { text, selectedNodes, nodes, dispatch,
|
|
77
|
-
props;
|
|
76
|
+
const { text, selectedNodes, nodes, dispatch, allowOverlap } = props;
|
|
78
77
|
const allTags: AnnotateBlendTag[] = buildTags(
|
|
79
78
|
buildHighlights(nodes, null),
|
|
80
79
|
selectedNodes,
|
|
@@ -96,7 +95,6 @@ export function FeedbackText(props: FeedbackTextProps) {
|
|
|
96
95
|
h(HighlightedText, {
|
|
97
96
|
text,
|
|
98
97
|
allTags,
|
|
99
|
-
lineHeight,
|
|
100
98
|
allowOverlap,
|
|
101
99
|
dispatch,
|
|
102
100
|
selectedNodes,
|
|
@@ -143,7 +141,18 @@ function createTagFromSelection({
|
|
|
143
141
|
}
|
|
144
142
|
|
|
145
143
|
function addTag({ tag, dispatch, text, allTags, allowOverlap }) {
|
|
146
|
-
|
|
144
|
+
let { start, end } = tag;
|
|
145
|
+
// snap to text
|
|
146
|
+
if (text[end - 1] != " ") {
|
|
147
|
+
// double clicking word overselects by one, shouldn't increase to next word
|
|
148
|
+
while (start > 0 && /\w/.test(text[start - 1])) {
|
|
149
|
+
start--;
|
|
150
|
+
}
|
|
151
|
+
while (end < text.length && /\w/.test(text[end])) {
|
|
152
|
+
end++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
147
156
|
let payload = { start, end, text: text.slice(start, end) };
|
|
148
157
|
|
|
149
158
|
if (payload.text.trim() === "") {
|
|
@@ -242,10 +251,13 @@ function renderNode(
|
|
|
242
251
|
|
|
243
252
|
const { tag, children } = node;
|
|
244
253
|
const isSelected = selectedNodes?.includes(tag.id);
|
|
254
|
+
const showBorder = selectedNodes.length === 0 || isSelected;
|
|
245
255
|
|
|
246
256
|
const style = {
|
|
247
257
|
...tag,
|
|
248
258
|
zIndex: parentSelected ? -1 : 1,
|
|
259
|
+
border: "1px solid " + (showBorder ? tag.color : "transparent"),
|
|
260
|
+
margin: "-1px",
|
|
249
261
|
};
|
|
250
262
|
|
|
251
263
|
let moveText = [];
|
|
@@ -269,10 +281,31 @@ function renderNode(
|
|
|
269
281
|
style,
|
|
270
282
|
onClick: (e: MouseEvent) => {
|
|
271
283
|
e.stopPropagation();
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
284
|
+
if (
|
|
285
|
+
e.ctrlKey ||
|
|
286
|
+
e.metaKey ||
|
|
287
|
+
(selectedNodes[0] === tag.id && selectedNodes.length === 1)
|
|
288
|
+
) {
|
|
289
|
+
// Toggle selection on ctrl/cmd click or when node is only selected node
|
|
290
|
+
e.stopPropagation();
|
|
291
|
+
dispatch({
|
|
292
|
+
type: "toggle-node-selected",
|
|
293
|
+
payload: { ids: [tag.id] },
|
|
294
|
+
});
|
|
295
|
+
} else if (e.shiftKey && selectedNodes.length > 0) {
|
|
296
|
+
// Select range from last selected node to this one
|
|
297
|
+
const lastSelected = selectedNodes[selectedNodes.length - 1];
|
|
298
|
+
|
|
299
|
+
dispatch({
|
|
300
|
+
type: "select-range",
|
|
301
|
+
payload: { ids: [lastSelected, tag.id] },
|
|
302
|
+
});
|
|
303
|
+
} else {
|
|
304
|
+
dispatch({
|
|
305
|
+
type: "select-node",
|
|
306
|
+
payload: { ids: [tag.id] },
|
|
307
|
+
});
|
|
308
|
+
}
|
|
276
309
|
},
|
|
277
310
|
},
|
|
278
311
|
isSelected
|
|
@@ -291,14 +324,7 @@ export function HighlightedText(props: {
|
|
|
291
324
|
dispatch: TreeDispatch;
|
|
292
325
|
selectedNodes: number[];
|
|
293
326
|
}) {
|
|
294
|
-
const {
|
|
295
|
-
text,
|
|
296
|
-
allTags = [],
|
|
297
|
-
lineHeight,
|
|
298
|
-
dispatch,
|
|
299
|
-
selectedNodes,
|
|
300
|
-
allowOverlap,
|
|
301
|
-
} = props;
|
|
327
|
+
const { text, allTags = [], dispatch, selectedNodes, allowOverlap } = props;
|
|
302
328
|
|
|
303
329
|
const tree = nestHighlights(text, allTags);
|
|
304
330
|
|
|
@@ -319,7 +345,7 @@ export function HighlightedText(props: {
|
|
|
319
345
|
|
|
320
346
|
return h(
|
|
321
347
|
"span",
|
|
322
|
-
{
|
|
348
|
+
{ ref: spanRef },
|
|
323
349
|
tree.children.map((child: any, i: number) =>
|
|
324
350
|
renderNode(child, dispatch, selectedNodes, false),
|
|
325
351
|
),
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AAnBA,4CAA6B,CAAC,eAAe,CAAC;AAC9C,4CAAqC,CAAC,uBAAuB,CAAC;AAC9D,4CAAkC,CAAC,oBAAoB,CAAC;AACxD,4CAAgC,CAAC,kBAAkB,CAAC;AACpD,2CAAiC,CAAC,mBAAmB,CAAC;AACtD,4CAAuC,CAAC,yBAAyB,CAAC;AAClE,4CAAkC,CAAC,oBAAoB,CAAC;AACxD,2CAA0C,CAAC,4BAA4B,CAAC;AACxE,4CAA+B,CAAC,iBAAiB,CAAC;AAClD,4CAA8B,CAAC,gBAAgB,CAAC;AAChD,4CAA0B,CAAC,YAAY,CAAC;AACxC,4CAAyB,CAAC,WAAW,CAAC;AACtC,4CAAsC,CAAC,wBAAwB,CAAC;AAChE,4CAA6B,CAAC,eAAe,CAAC;AAC9C,4CAAmC,CAAC,qBAAqB,CAAC;AAC1D,2CAAgC,CAAC,kBAAkB,CAAC;AACpD,4CAA0B,CAAC,YAAY,CAAC;AACxC,4CAAmC,CAAC,qBAAqB,CAAC;AAC1D,4CAA8B,CAAC,gBAAgB,CAAC;AAChD,2CAA6B,CAAC,eAAe,CAAC","sources":["packages/feedback-components/src/feedback/feedback.module.sass"],"sourcesContent":[".feedback-component\n position: relative\n width: 800px\n\n & > svg\n width: 800px\n\n.node\n cursor: pointer\n\ncircle\n cursor: pointer\n border: 1px solid black\n\n.selected\n border: 1px solid white\n \n.feedback-text\n margin-bottom: 2em\n\n.entity-panel\n position: relative\n max-height: 600px\n\n.control-panel\n position: absolute\n top: 1em\n right: 1em\n padding: 0.2em 0.5em\n\n.entity-panel\n flex: 1\n min-height: 100px\n padding: 1em\n background: var(--panel-secondary-background-color)\n border-radius: 4px\n // Inset box shadow\n box-shadow: 0 0 0 1px var(--panel-border-color) inset\n\n.selection-tree\n margin: -1em 0\n padding: 1em 0\n width: 70% !important\n \n.type-list\n display: grid\n grid-auto-flow: column\n grid-template-rows: repeat(10, auto)\n gap: 0.2em\n\n .type-tag\n padding: .2em .5em\n border-radius: .2em\n\n.description\n max-width: 300px\n padding: .5em\n\nmark\n border-radius: .2em\n cursor: pointer\n color: black !important\n\n[role=\"treeitem\"]\n width: auto !important\n\n.highlight\n cursor: pointer\n padding: .2em 0\n border-radius: .2em\n position: relative\n zIndex: 10\n\n.feedback-text-wrapper \n position: relative\n z-index: 0\n overflow: visible\n\n.type-container\n display: flex\n justify-content: space-between\n align-items: center\n column-gap: 1em\n\n.add-type\n cursor: pointer\n display: flex\n justify-content: space-between\n padding: 0 .5em\n align-items: center\n\n p\n margin: 0\n\n.overlay-container\n height: 80vh\n width: 100vw\n display: flex\n justify-content: center\n align-items: center\n\n .add-type-overlay\n background-color: var(--secondary-color)\n padding: .5em 1em\n display: flex\n flex-direction: column\n gap: 1em\n border-radius: .2em\n\n .title\n display: flex\n justify-content: space-between\n align-items: center\n\n h2\n margin: 0\n\n .form-group\n display: flex\n gap: 3em\n\n .text-inputs\n display: flex\n flex-direction: column\n gap: 1em\n\n.icons\n display: flex\n gap: .25em"],"names":[],"version":3,"file":"feedback-components.4359bc80.js.map"}
|