@macrostrat/feedback-components 1.1.3 → 1.1.5

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 (69) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/esm/feedback-components.03f08dc7.js +287 -0
  3. package/dist/esm/feedback-components.03f08dc7.js.map +1 -0
  4. package/dist/esm/feedback-components.06a79c6a.js +354 -0
  5. package/dist/esm/feedback-components.06a79c6a.js.map +1 -0
  6. package/dist/esm/{feedback-components.f577ebea.js → feedback-components.3f59f2a5.js} +14 -89
  7. package/dist/esm/feedback-components.3f59f2a5.js.map +1 -0
  8. package/dist/esm/{feedback-components.45d25912.js → feedback-components.4cbd249a.js} +5 -5
  9. package/dist/esm/{feedback-components.45d25912.js.map → feedback-components.4cbd249a.js.map} +1 -1
  10. package/dist/esm/{feedback-components.fb60c70d.css → feedback-components.5a8f0185.css} +29 -4
  11. package/dist/esm/feedback-components.5a8f0185.css.map +1 -0
  12. package/dist/esm/{feedback-components.fa1d3641.js → feedback-components.6cec1102.js} +84 -9
  13. package/dist/esm/feedback-components.6cec1102.js.map +1 -0
  14. package/dist/esm/{feedback-components.95dbe7d7.js → feedback-components.7c2fe400.js} +16 -1
  15. package/dist/esm/feedback-components.7c2fe400.js.map +1 -0
  16. package/dist/esm/{feedback-components.832b2eae.js → feedback-components.939a3a9f.js} +7 -7
  17. package/dist/esm/feedback-components.939a3a9f.js.map +1 -0
  18. package/dist/esm/feedback-components.e53837d9.js +248 -0
  19. package/dist/esm/feedback-components.e53837d9.js.map +1 -0
  20. package/dist/esm/index.d.ts +2 -1
  21. package/dist/esm/index.d.ts.map +1 -1
  22. package/dist/esm/index.js +1 -1
  23. package/dist/node/{feedback-components.f9abf0d6.js → feedback-components.3888aa2a.js} +2 -2
  24. package/dist/node/{feedback-components.f9abf0d6.js.map → feedback-components.3888aa2a.js.map} +1 -1
  25. package/dist/node/feedback-components.388de4ac.js +2 -0
  26. package/dist/node/feedback-components.388de4ac.js.map +1 -0
  27. package/dist/node/feedback-components.827f8d80.js +2 -0
  28. package/dist/node/feedback-components.827f8d80.js.map +1 -0
  29. package/dist/node/feedback-components.9e1d4e4c.js +2 -0
  30. package/dist/node/feedback-components.9e1d4e4c.js.map +1 -0
  31. package/dist/node/feedback-components.b8da3bce.js +2 -0
  32. package/dist/node/feedback-components.b8da3bce.js.map +1 -0
  33. package/dist/node/feedback-components.c31cf831.js +2 -0
  34. package/dist/node/feedback-components.c31cf831.js.map +1 -0
  35. package/dist/node/feedback-components.db4d0a14.css +2 -0
  36. package/dist/node/feedback-components.db4d0a14.css.map +1 -0
  37. package/dist/node/feedback-components.f91331e9.js +2 -0
  38. package/dist/node/feedback-components.f91331e9.js.map +1 -0
  39. package/dist/node/feedback-components.fc0395df.js +2 -0
  40. package/dist/node/feedback-components.fc0395df.js.map +1 -0
  41. package/dist/node/index.js +1 -1
  42. package/dist/node/index.js.map +1 -1
  43. package/package.json +2 -2
  44. package/src/feedback/edit-state.ts +99 -3
  45. package/src/feedback/feedback.module.sass +24 -4
  46. package/src/feedback/graph.ts +5 -2
  47. package/src/feedback/index.ts +39 -320
  48. package/src/feedback/matches.ts +279 -0
  49. package/src/feedback/text-visualizer.ts +34 -88
  50. package/src/feedback/typelist.ts +321 -0
  51. package/dist/esm/feedback-components.1e7da538.js +0 -587
  52. package/dist/esm/feedback-components.1e7da538.js.map +0 -1
  53. package/dist/esm/feedback-components.832b2eae.js.map +0 -1
  54. package/dist/esm/feedback-components.95dbe7d7.js.map +0 -1
  55. package/dist/esm/feedback-components.f577ebea.js.map +0 -1
  56. package/dist/esm/feedback-components.fa1d3641.js.map +0 -1
  57. package/dist/esm/feedback-components.fb60c70d.css.map +0 -1
  58. package/dist/node/feedback-components.25f1909a.js +0 -2
  59. package/dist/node/feedback-components.25f1909a.js.map +0 -1
  60. package/dist/node/feedback-components.4cd6b208.js +0 -2
  61. package/dist/node/feedback-components.4cd6b208.js.map +0 -1
  62. package/dist/node/feedback-components.9328e8ba.js +0 -2
  63. package/dist/node/feedback-components.9328e8ba.js.map +0 -1
  64. package/dist/node/feedback-components.b7946db4.js +0 -2
  65. package/dist/node/feedback-components.b7946db4.js.map +0 -1
  66. package/dist/node/feedback-components.c459cc27.js +0 -2
  67. package/dist/node/feedback-components.c459cc27.js.map +0 -1
  68. package/dist/node/feedback-components.c88cb37f.css +0 -2
  69. package/dist/node/feedback-components.c88cb37f.css.map +0 -1
@@ -0,0 +1,279 @@
1
+ import { Switch } from "@blueprintjs/core";
2
+ import { Select } from "@blueprintjs/select";
3
+ import styles from "./feedback.module.sass";
4
+ import hyper from "@macrostrat/hyper";
5
+ import { useState } from "react";
6
+ import { Icon, Divider, Overlay2 } from "@blueprintjs/core";
7
+ import { JSONView, SaveButton } from "@macrostrat/ui-components";
8
+ import { useAPIResult, DataField } from "@macrostrat/ui-components";
9
+ import { LithologyTag } from "@macrostrat/data-components";
10
+
11
+ const h = hyper.styled(styles);
12
+
13
+ export function Matches({
14
+ match,
15
+ setMatchLinks,
16
+ matchLinks,
17
+ selectedNodes,
18
+ tree,
19
+ dispatch,
20
+ }) {
21
+ const [overlayOpen, setOverlayOpen] = useState(false);
22
+
23
+ let nodeMatch = null;
24
+ if (selectedNodes.length === 1) {
25
+ nodeMatch = findMatchingNode(tree, selectedNodes[0]);
26
+ }
27
+
28
+ return h.if(matchLinks)("div", [
29
+ h(Divider),
30
+ h(Switch, {
31
+ label: "Match mode",
32
+ checked: match !== null,
33
+ onChange: (e) => {
34
+ setMatchLinks(match === null ? matchLinks || {} : null);
35
+ dispatch({ type: "toggle-match-mode" });
36
+ },
37
+ }),
38
+ h.if(nodeMatch && match)(Match, {
39
+ data: nodeMatch?.match,
40
+ matchLinks: matchLinks,
41
+ dispatch,
42
+ nodeId: nodeMatch?.id,
43
+ }),
44
+ h.if(selectedNodes.length == 1 && !nodeMatch?.match && match)(
45
+ "div.add-match-container",
46
+ [
47
+ h(
48
+ "div.add-type",
49
+ {
50
+ onClick: () => {
51
+ setOverlayOpen(true);
52
+ },
53
+ },
54
+ [h("p.add-match-text", "Add match"), h(Icon, { icon: "plus" })],
55
+ ),
56
+ h(MatchOverlay, {
57
+ isOpen: overlayOpen,
58
+ setOverlayOpen,
59
+ nodeMatch,
60
+ dispatch,
61
+ }),
62
+ ],
63
+ ),
64
+ ]);
65
+ }
66
+
67
+ function findMatchingNode(tree, nodeId) {
68
+ let match = null;
69
+
70
+ function traverse(node) {
71
+ if (node.id === nodeId) {
72
+ match = node;
73
+ return true;
74
+ }
75
+ if (Array.isArray(node.children)) {
76
+ for (const child of node.children) {
77
+ if (traverse(child)) return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+
83
+ tree.forEach(traverse);
84
+ return match;
85
+ }
86
+
87
+ function MatchOverlay({ isOpen, setOverlayOpen, nodeMatch, dispatch }) {
88
+ const [inputValue, setInputValue] = useState(nodeMatch?.name || "");
89
+ const [selectedItem, setSelectedItem] = useState(h("div", "Select a match"));
90
+ const [disabled, setDisabled] = useState(true);
91
+ const [payload, setPayload] = useState({});
92
+
93
+ const data = useAPIResult(
94
+ "https://dev.macrostrat.org/api/pg/type_lookup?name=ilike.*" +
95
+ inputValue +
96
+ "*",
97
+ );
98
+ const items = data?.map((data) => h(MatchTag, { data, setPayload }));
99
+
100
+ return h(
101
+ Overlay2,
102
+ {
103
+ isOpen,
104
+ },
105
+ h(
106
+ "div.overlay-container",
107
+ h("div.add-type-overlay", [
108
+ h("h2.title", [
109
+ "Add match with " + nodeMatch.name,
110
+ h(Icon, {
111
+ icon: "cross",
112
+ className: "close-icon",
113
+ onClick: () => {
114
+ setOverlayOpen(false);
115
+ },
116
+ style: { cursor: "pointer", color: "red" },
117
+ }),
118
+ ]),
119
+ h("div.form-group", [
120
+ h(
121
+ Select,
122
+ {
123
+ items: items || [],
124
+ itemRenderer: (item, { handleClick }) => {
125
+ return h("div.match-item", { onClick: handleClick }, item);
126
+ },
127
+ onItemSelect: (item) => {
128
+ setDisabled(false);
129
+ setSelectedItem(item);
130
+ },
131
+ onQueryChange: (query) => setInputValue(query),
132
+ popoverProps: { minimal: true },
133
+ query: inputValue,
134
+ placeholder: "Enter match name",
135
+ },
136
+ selectedItem,
137
+ ),
138
+ ]),
139
+ h(
140
+ SaveButton,
141
+ {
142
+ className: "save-btn",
143
+ small: true,
144
+ onClick: () => {
145
+ // Handle save changes
146
+ dispatch({
147
+ type: "add-match",
148
+ payload: { id: nodeMatch.id, payload },
149
+ });
150
+ setOverlayOpen(false);
151
+ },
152
+ disabled,
153
+ },
154
+ "Save changes",
155
+ ),
156
+ ]),
157
+ ),
158
+ );
159
+ }
160
+
161
+ function Match({ data, matchLinks, dispatch, nodeId }) {
162
+ return h.if(data)("div.match-container", [
163
+ MatchTag({ data, matchLinks }),
164
+ h(Icon, {
165
+ icon: "cross",
166
+ color: "red",
167
+ className: "close-btn",
168
+ onClick: () => {
169
+ dispatch({ type: "remove-match", payload: { id: nodeId } });
170
+ },
171
+ }),
172
+ ]);
173
+ }
174
+
175
+ interface MatchTagProps {
176
+ data: any;
177
+ matchLinks?: Record<string, string>;
178
+ setPayload?: (payload: Record<string, any>) => void;
179
+ }
180
+
181
+ function MatchTag({ data, matchLinks, setPayload }: MatchTagProps) {
182
+ if (!data || Object.keys(data).length === 0) return;
183
+
184
+ if (data.lith_id || data?.type === "lith") {
185
+ return h(
186
+ "div",
187
+ {
188
+ onClick: () => {
189
+ data.type === "lith"
190
+ ? setPayload({ lith_id: data.id, name: data.name })
191
+ : null;
192
+ },
193
+ },
194
+ h(DataField, {
195
+ className: "match-item",
196
+ label: "Stratigraphic name",
197
+ value: h(LithologyTag, {
198
+ data: { name: data.name, id: data.id, color: data.color },
199
+ onClick: () =>
200
+ window.open(
201
+ matchLinks.strat_name + "/" + data.strat_name_id,
202
+ "_blank",
203
+ ),
204
+ }),
205
+ }),
206
+ );
207
+ }
208
+
209
+ if (data.strat_name_id || data?.type === "strat_name") {
210
+ return h(
211
+ "div",
212
+ {
213
+ onClick: () => {
214
+ data.type === "strat_name"
215
+ ? setPayload({ strat_name_id: data.id, name: data.name })
216
+ : null;
217
+ },
218
+ },
219
+ h(DataField, {
220
+ className: "match-item",
221
+ label: "Stratigraphic name",
222
+ value: h(LithologyTag, {
223
+ data: { name: data.name, id: data.id, color: data.color },
224
+ onClick: () =>
225
+ window.open(
226
+ matchLinks.strat_name + "/" + data.strat_name_id,
227
+ "_blank",
228
+ ),
229
+ }),
230
+ }),
231
+ );
232
+ }
233
+
234
+ if (data.lith_att_id || data?.type === "lith_att") {
235
+ return h(
236
+ "div",
237
+ {
238
+ onClick: () => {
239
+ data.type === "lith_att"
240
+ ? setPayload({ lith_att_id: data.id, name: data.name })
241
+ : null;
242
+ },
243
+ },
244
+ h(DataField, {
245
+ className: "match-item",
246
+ label: "Lithology attribute",
247
+ value: h(LithologyTag, {
248
+ data: { name: data.name, id: data.lith_att_id },
249
+ onClick: () =>
250
+ window.open(matchLinks.lith_att + "/" + data.lith_att_id, "_blank"),
251
+ }),
252
+ }),
253
+ );
254
+ }
255
+
256
+ if (data.int_id || data?.type === "interval") {
257
+ return h(
258
+ "div",
259
+ {
260
+ onClick: () => {
261
+ data.type === "interval"
262
+ ? setPayload({ int_id: data.id, name: data.name })
263
+ : null;
264
+ },
265
+ },
266
+ h(DataField, {
267
+ label: "Interval",
268
+ className: "match-item",
269
+ value: h(LithologyTag, {
270
+ data: { name: data.name, id: data.id },
271
+ onClick: () =>
272
+ window.open(matchLinks.interval + "/" + data.int_id, "_blank"),
273
+ }),
274
+ }),
275
+ );
276
+ }
277
+
278
+ return h(JSONView, { data });
279
+ }
@@ -6,7 +6,7 @@ import hyper from "@macrostrat/hyper";
6
6
  import { buildHighlights, getTagStyle } from "../extractions";
7
7
  import { Highlight } from "../extractions/types";
8
8
  import { useEffect, useRef } from "react";
9
- import { Popover } from "@blueprintjs/core";
9
+ import { Icon } from "@blueprintjs/core";
10
10
  import { DataField, JSONView } from "@macrostrat/ui-components";
11
11
  import { LithologyList, LithologyTag } from "@macrostrat/data-components";
12
12
 
@@ -25,6 +25,7 @@ export interface FeedbackTextProps {
25
25
  strat_name: string;
26
26
  lith_att: string;
27
27
  };
28
+ viewOnly?: boolean;
28
29
  }
29
30
 
30
31
  function buildTags(
@@ -81,8 +82,15 @@ function isHighlighted(tag: Highlight, selectedNodes: number[]) {
81
82
 
82
83
  export function FeedbackText(props: FeedbackTextProps) {
83
84
  // Convert input to tags
84
- const { text, selectedNodes, nodes, dispatch, allowOverlap, matchLinks } =
85
- props;
85
+ const {
86
+ text,
87
+ selectedNodes,
88
+ nodes,
89
+ dispatch,
90
+ allowOverlap,
91
+ matchLinks,
92
+ viewOnly,
93
+ } = props;
86
94
  const allTags: AnnotateBlendTag[] = buildTags(
87
95
  buildHighlights(nodes, null),
88
96
  selectedNodes,
@@ -107,6 +115,7 @@ export function FeedbackText(props: FeedbackTextProps) {
107
115
  allowOverlap,
108
116
  dispatch,
109
117
  selectedNodes,
118
+ viewOnly,
110
119
  matchLinks,
111
120
  }),
112
121
  );
@@ -261,17 +270,25 @@ function renderNode(
261
270
  strat_name: string;
262
271
  lith_att: string;
263
272
  },
273
+ viewOnly?: boolean,
264
274
  ): any {
265
275
  if (typeof node === "string") return node;
266
276
 
267
277
  const { tag, children } = node;
268
278
  const isSelected = selectedNodes?.includes(tag.id);
269
279
  const showBorder = selectedNodes.length === 0 || isSelected;
280
+ const match = tag.match;
270
281
 
271
282
  const style = {
272
283
  ...tag,
273
284
  zIndex: parentSelected ? -1 : 1,
274
- border: "1px solid " + (showBorder ? tag.color : "transparent"),
285
+ border:
286
+ "1px solid " +
287
+ (match != undefined && matchLinks
288
+ ? "orange"
289
+ : showBorder
290
+ ? tag.color
291
+ : "transparent"),
275
292
  margin: "-1px",
276
293
  };
277
294
 
@@ -289,15 +306,13 @@ function renderNode(
289
306
  }
290
307
  }
291
308
 
292
- const match = tag.match;
293
-
294
- const TagComponent = h(
309
+ return h(
295
310
  "span",
296
311
  {
297
312
  onMouseEnter: (e: MouseEvent) => {
298
313
  e.stopPropagation();
299
314
  },
300
- className: "highlight",
315
+ className: "highlight" + (!viewOnly ? " clickable" : ""),
301
316
  style,
302
317
  onClick: (e: MouseEvent) => {
303
318
  e.stopPropagation();
@@ -331,21 +346,16 @@ function renderNode(
331
346
  isSelected
332
347
  ? moveText.flat()
333
348
  : children.map((child: any, i: number) =>
334
- renderNode(child, dispatch, selectedNodes, isSelected, matchLinks),
349
+ renderNode(
350
+ child,
351
+ dispatch,
352
+ selectedNodes,
353
+ isSelected,
354
+ matchLinks,
355
+ viewOnly,
356
+ ),
335
357
  ),
336
358
  );
337
-
338
- return matchLinks && match
339
- ? h(
340
- Popover,
341
- {
342
- autoFocus: false,
343
- content: h("div.description", h(Match, { data: match, matchLinks })),
344
- interactionKind: "hover",
345
- },
346
- TagComponent,
347
- )
348
- : TagComponent;
349
359
  }
350
360
 
351
361
  export function HighlightedText(props: {
@@ -360,6 +370,7 @@ export function HighlightedText(props: {
360
370
  strat_name: string;
361
371
  lith_att: string;
362
372
  };
373
+ viewOnly?: boolean;
363
374
  }) {
364
375
  const {
365
376
  text,
@@ -368,6 +379,7 @@ export function HighlightedText(props: {
368
379
  selectedNodes,
369
380
  allowOverlap,
370
381
  matchLinks,
382
+ viewOnly,
371
383
  } = props;
372
384
 
373
385
  const tree = nestHighlights(text, allTags);
@@ -391,73 +403,7 @@ export function HighlightedText(props: {
391
403
  "span",
392
404
  { ref: spanRef },
393
405
  tree.children.map((child: any, i: number) =>
394
- renderNode(child, dispatch, selectedNodes, false, matchLinks),
406
+ renderNode(child, dispatch, selectedNodes, false, matchLinks, viewOnly),
395
407
  ),
396
408
  );
397
409
  }
398
-
399
- function Match({ data, matchLinks }) {
400
- if (data.lith_id) {
401
- return h(DataField, {
402
- label: "Lithology",
403
- value: h(LithologyTag, {
404
- data: { name: data.name, id: data.lith_id, color: data.color },
405
- onClick: (e) => {
406
- e.stopPropagation();
407
- if (matchLinks.lithology) {
408
- window.open(matchLinks.lithology + "/" + data.lith_id, "_blank");
409
- }
410
- },
411
- }),
412
- });
413
- }
414
-
415
- if (data.strat_name_id) {
416
- return h("div", [
417
- h(DataField, {
418
- label: "Stratigraphic name",
419
- value: h(LithologyTag, {
420
- data: { name: data.name, id: data.strat_name_id, color: data.color },
421
- onClick: (e) => {
422
- e.stopPropagation();
423
- if (matchLinks.strat_name) {
424
- window.open(
425
- matchLinks.strat_name + "/" + data.strat_name_id,
426
- "_blank",
427
- );
428
- }
429
- },
430
- }),
431
- }),
432
- h.if(data.concept_id)(DataField, {
433
- label: "Stratigraphic concept",
434
- value: h(LithologyTag, {
435
- data: { name: data.name, id: data.concept_id, color: data.color },
436
- onClick: (e) => {
437
- e.stopPropagation();
438
- if (matchLinks.concept) {
439
- window.open(matchLinks.concept + "/" + data.concept_id, "_blank");
440
- }
441
- },
442
- }),
443
- }),
444
- ]);
445
- }
446
-
447
- if (data.lith_att_id) {
448
- return h(DataField, {
449
- label: "Lithology attribute",
450
- value: h(LithologyTag, {
451
- data: { name: data.name, id: data.lith_att_id },
452
- onClick: (e) => {
453
- e.stopPropagation();
454
- if (matchLinks.lith_att) {
455
- window.open(matchLinks.lith_att + "/" + data.lith_att_id, "_blank");
456
- }
457
- },
458
- }),
459
- });
460
- }
461
-
462
- return h(JSONView, { data });
463
- }