@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/esm/{feedback-components.7cd9b6cc.js → feedback-components.3b3a5357.js} +53 -30
  3. package/dist/esm/feedback-components.3b3a5357.js.map +1 -0
  4. package/dist/esm/{feedback-components.921dcd46.js → feedback-components.46a7a347.js} +42 -14
  5. package/dist/esm/feedback-components.46a7a347.js.map +1 -0
  6. package/dist/esm/{feedback-components.87533431.js → feedback-components.5509fab3.js} +5 -5
  7. package/dist/esm/{feedback-components.87533431.js.map → feedback-components.5509fab3.js.map} +1 -1
  8. package/dist/esm/{feedback-components.6a6c8af5.js → feedback-components.586103e8.js} +108 -82
  9. package/dist/esm/feedback-components.586103e8.js.map +1 -0
  10. package/dist/esm/{feedback-components.4359bc80.js → feedback-components.95dbe7d7.js} +13 -1
  11. package/dist/esm/feedback-components.95dbe7d7.js.map +1 -0
  12. package/dist/esm/{feedback-components.bf5f7cf7.js → feedback-components.fa1d3641.js} +29 -2
  13. package/dist/esm/feedback-components.fa1d3641.js.map +1 -0
  14. package/dist/esm/{feedback-components.b7d9b015.css → feedback-components.fb60c70d.css} +38 -14
  15. package/dist/esm/feedback-components.fb60c70d.css.map +1 -0
  16. package/dist/esm/index.d.ts +3 -82
  17. package/dist/esm/index.d.ts.map +1 -1
  18. package/dist/esm/index.js +1 -1
  19. package/dist/node/feedback-components.561466ac.js +2 -0
  20. package/dist/node/feedback-components.561466ac.js.map +1 -0
  21. package/dist/node/feedback-components.571ee23c.js +2 -0
  22. package/dist/node/feedback-components.571ee23c.js.map +1 -0
  23. package/dist/node/{feedback-components.e8aa70b8.js → feedback-components.a39f7653.js} +2 -2
  24. package/dist/node/{feedback-components.e8aa70b8.js.map → feedback-components.a39f7653.js.map} +1 -1
  25. package/dist/node/feedback-components.b7946db4.js +2 -0
  26. package/dist/node/feedback-components.b7946db4.js.map +1 -0
  27. package/dist/node/feedback-components.c459cc27.js +2 -0
  28. package/dist/node/feedback-components.c459cc27.js.map +1 -0
  29. package/dist/node/feedback-components.c88cb37f.css +2 -0
  30. package/dist/node/feedback-components.c88cb37f.css.map +1 -0
  31. package/dist/node/feedback-components.ec54a1e7.js +2 -0
  32. package/dist/node/feedback-components.ec54a1e7.js.map +1 -0
  33. package/dist/node/index.js +1 -1
  34. package/dist/node/index.js.map +1 -1
  35. package/package.json +3 -3
  36. package/src/feedback/edit-state.ts +41 -3
  37. package/src/feedback/feedback.module.sass +30 -10
  38. package/src/feedback/graph.ts +27 -10
  39. package/src/feedback/index.ts +146 -112
  40. package/src/feedback/text-visualizer.ts +43 -17
  41. package/dist/esm/feedback-components.4359bc80.js.map +0 -1
  42. package/dist/esm/feedback-components.6a6c8af5.js.map +0 -1
  43. package/dist/esm/feedback-components.7cd9b6cc.js.map +0 -1
  44. package/dist/esm/feedback-components.921dcd46.js.map +0 -1
  45. package/dist/esm/feedback-components.b7d9b015.css.map +0 -1
  46. package/dist/esm/feedback-components.bf5f7cf7.js.map +0 -1
  47. package/dist/node/feedback-components.15e1316d.js +0 -2
  48. package/dist/node/feedback-components.15e1316d.js.map +0 -1
  49. package/dist/node/feedback-components.65d8488e.js +0 -2
  50. package/dist/node/feedback-components.65d8488e.js.map +0 -1
  51. package/dist/node/feedback-components.6681dbde.js +0 -2
  52. package/dist/node/feedback-components.6681dbde.js.map +0 -1
  53. package/dist/node/feedback-components.77b6fc89.css +0 -2
  54. package/dist/node/feedback-components.77b6fc89.css.map +0 -1
  55. package/dist/node/feedback-components.7caa447a.js +0 -2
  56. package/dist/node/feedback-components.7caa447a.js.map +0 -1
  57. package/dist/node/feedback-components.e2f3c4b7.js +0 -2
  58. 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
@@ -1,7 +1,7 @@
1
1
  import { TreeData } from "./types";
2
2
  import { treeToGraph } from "./edit-state";
3
- import h from "@macrostrat/hyper";
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, Popover } from "@blueprintjs/core";
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: 5,
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("title", d.name || `Node ${d.id}`),
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
  ]),
@@ -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(TreeDispatchContext.Provider, { value: dispatch }, [
80
+ return h("div.page-wrapper", [
82
81
  h(
83
- ErrorBoundary,
84
- {
85
- description:
86
- "An error occurred while rendering the feedback text component.",
87
- },
88
- h(FeedbackText, {
89
- text,
90
- dispatch,
91
- // @ts-ignore
92
- nodes: tree,
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
- tree,
93
+ // @ts-ignore
94
+ nodes: tree,
161
95
  selectedNodes,
162
- isOpen: isSelectingEntityType,
163
- setOpen: (isOpen: boolean) =>
164
- dispatch({
165
- type: "toggle-entity-type-selector",
166
- payload: isOpen,
167
- }),
96
+ allowOverlap,
168
97
  }),
169
- ]),
170
- h.if(state.viewMode == "tree")(ManagedSelectionTree, {
171
- selectedNodes,
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
- console.log("Clicked nodes:", nodes);
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
- dispatch({ type: "select-node", payload: { ids } });
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: darkMode ? "white" : "black",
614
- backgroundColor: chromaColor?.luminance(1 - luminance).hex(),
615
- border:
616
- id === selected?.id && selectedNodes.length > 0
617
- ? `1px solid var(--text-emphasized-color)`
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, lineHeight, allowOverlap } =
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
- const { start, end } = tag;
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
- dispatch({
273
- type: "toggle-node-selected",
274
- payload: { ids: [tag.id] },
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
- { style: { lineHeight }, ref: spanRef },
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"}