@macrostrat/feedback-components 1.1.2 → 1.1.4

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 (76) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/esm/{feedback-components.95dbe7d7.js → feedback-components.00434ff7.js} +10 -1
  3. package/dist/esm/feedback-components.00434ff7.js.map +1 -0
  4. package/dist/esm/{feedback-components.46a7a347.js → feedback-components.1c15f37f.js} +101 -12
  5. package/dist/esm/feedback-components.1c15f37f.js.map +1 -0
  6. package/dist/esm/{feedback-components.586103e8.js → feedback-components.204f7d2b.js} +27 -276
  7. package/dist/esm/feedback-components.204f7d2b.js.map +1 -0
  8. package/dist/esm/{feedback-components.3b3a5357.js → feedback-components.28ba71be.js} +19 -8
  9. package/dist/esm/feedback-components.28ba71be.js.map +1 -0
  10. package/dist/esm/{feedback-components.5df2a926.js → feedback-components.707e3490.js} +5 -7
  11. package/dist/esm/feedback-components.707e3490.js.map +1 -0
  12. package/dist/esm/feedback-components.7e879290.js +286 -0
  13. package/dist/esm/feedback-components.7e879290.js.map +1 -0
  14. package/dist/esm/{feedback-components.5509fab3.js → feedback-components.d55a1d18.js} +6 -6
  15. package/dist/esm/{feedback-components.5509fab3.js.map → feedback-components.d55a1d18.js.map} +1 -1
  16. package/dist/esm/{feedback-components.fb60c70d.css → feedback-components.d591ffec.css} +20 -1
  17. package/dist/esm/feedback-components.d591ffec.css.map +1 -0
  18. package/dist/esm/{feedback-components.fa1d3641.js → feedback-components.f6605b83.js} +49 -7
  19. package/dist/esm/feedback-components.f6605b83.js.map +1 -0
  20. package/dist/esm/feedback-components.fd8ac9ca.js +230 -0
  21. package/dist/esm/feedback-components.fd8ac9ca.js.map +1 -0
  22. package/dist/esm/index.d.ts +5 -2
  23. package/dist/esm/index.d.ts.map +1 -1
  24. package/dist/esm/index.js +2 -2
  25. package/dist/node/feedback-components.0eef8d0c.js +2 -0
  26. package/dist/node/feedback-components.0eef8d0c.js.map +1 -0
  27. package/dist/node/{feedback-components.a39f7653.js → feedback-components.2f740fc7.js} +2 -2
  28. package/dist/node/{feedback-components.a39f7653.js.map → feedback-components.2f740fc7.js.map} +1 -1
  29. package/dist/node/feedback-components.41db283a.js +2 -0
  30. package/dist/node/feedback-components.41db283a.js.map +1 -0
  31. package/dist/node/feedback-components.69d0ccd0.js +2 -0
  32. package/dist/node/feedback-components.69d0ccd0.js.map +1 -0
  33. package/dist/node/feedback-components.a7b43cfa.js +2 -0
  34. package/dist/node/feedback-components.a7b43cfa.js.map +1 -0
  35. package/dist/node/feedback-components.b22d37d1.js +2 -0
  36. package/dist/node/feedback-components.b22d37d1.js.map +1 -0
  37. package/dist/node/feedback-components.b9317f9c.js +2 -0
  38. package/dist/node/feedback-components.b9317f9c.js.map +1 -0
  39. package/dist/node/{feedback-components.c88cb37f.css → feedback-components.e096504e.css} +2 -2
  40. package/dist/node/feedback-components.e096504e.css.map +1 -0
  41. package/dist/node/feedback-components.e140ac86.js +2 -0
  42. package/dist/node/feedback-components.e140ac86.js.map +1 -0
  43. package/dist/node/feedback-components.f8373b58.js +2 -0
  44. package/dist/node/feedback-components.f8373b58.js.map +1 -0
  45. package/dist/node/index.js +1 -1
  46. package/dist/node/index.js.map +1 -1
  47. package/package.json +3 -2
  48. package/src/extractions/index.ts +7 -2
  49. package/src/extractions/types.ts +1 -0
  50. package/src/feedback/edit-state.ts +52 -3
  51. package/src/feedback/feedback.module.sass +17 -1
  52. package/src/feedback/graph.ts +17 -4
  53. package/src/feedback/index.ts +14 -309
  54. package/src/feedback/matches.ts +241 -0
  55. package/src/feedback/text-visualizer.ts +114 -4
  56. package/src/feedback/typelist.ts +312 -0
  57. package/dist/esm/feedback-components.3b3a5357.js.map +0 -1
  58. package/dist/esm/feedback-components.46a7a347.js.map +0 -1
  59. package/dist/esm/feedback-components.586103e8.js.map +0 -1
  60. package/dist/esm/feedback-components.5df2a926.js.map +0 -1
  61. package/dist/esm/feedback-components.95dbe7d7.js.map +0 -1
  62. package/dist/esm/feedback-components.fa1d3641.js.map +0 -1
  63. package/dist/esm/feedback-components.fb60c70d.css.map +0 -1
  64. package/dist/node/feedback-components.561466ac.js +0 -2
  65. package/dist/node/feedback-components.561466ac.js.map +0 -1
  66. package/dist/node/feedback-components.571ee23c.js +0 -2
  67. package/dist/node/feedback-components.571ee23c.js.map +0 -1
  68. package/dist/node/feedback-components.8b03e8be.js +0 -2
  69. package/dist/node/feedback-components.8b03e8be.js.map +0 -1
  70. package/dist/node/feedback-components.b7946db4.js +0 -2
  71. package/dist/node/feedback-components.b7946db4.js.map +0 -1
  72. package/dist/node/feedback-components.c459cc27.js +0 -2
  73. package/dist/node/feedback-components.c459cc27.js.map +0 -1
  74. package/dist/node/feedback-components.c88cb37f.css.map +0 -1
  75. package/dist/node/feedback-components.ec54a1e7.js +0 -2
  76. package/dist/node/feedback-components.ec54a1e7.js.map +0 -1
@@ -18,25 +18,20 @@ import {
18
18
  ButtonGroup,
19
19
  Card,
20
20
  SegmentedControl,
21
- Icon,
22
- Popover,
23
21
  Divider,
24
- Overlay2,
25
22
  } from "@blueprintjs/core";
26
23
  import { OmniboxSelector } from "./type-selector";
27
24
  import {
28
25
  CancelButton,
29
- DataField,
30
26
  ErrorBoundary,
31
- FlexBox,
32
27
  FlexRow,
33
28
  SaveButton,
34
29
  } from "@macrostrat/ui-components";
35
30
  import useElementDimensions from "use-element-dimensions";
36
31
  import { GraphView } from "./graph";
37
- import { useInDarkMode } from "@macrostrat/ui-components";
38
- import { asChromaColor } from "@macrostrat/color-utils";
39
- import { ColorPicker } from "@macrostrat/data-sheet";
32
+
33
+ import { Matches } from "./matches";
34
+ import { TypeList } from "./typelist";
40
35
 
41
36
  export type { GraphData } from "./edit-state";
42
37
  export { treeToGraph } from "./edit-state";
@@ -60,12 +55,14 @@ export function FeedbackComponent({
60
55
  matchComponent,
61
56
  onSave,
62
57
  allowOverlap,
58
+ matchLinks,
63
59
  }) {
64
60
  // Get the input arguments
65
61
  const [state, dispatch] = useUpdatableTree(
66
62
  entities.map(processEntity) as any,
67
63
  entityTypes,
68
64
  );
65
+ const [match, setMatchLinks] = useState(matchLinks || {});
69
66
 
70
67
  const {
71
68
  selectedNodes,
@@ -94,6 +91,7 @@ export function FeedbackComponent({
94
91
  nodes: tree,
95
92
  selectedNodes,
96
93
  allowOverlap,
94
+ matchLinks: match,
97
95
  }),
98
96
  ),
99
97
  h(
@@ -174,6 +172,14 @@ export function FeedbackComponent({
174
172
  ),
175
173
  ],
176
174
  ),
175
+ h(Matches, {
176
+ match,
177
+ setMatchLinks,
178
+ matchLinks,
179
+ selectedNodes,
180
+ tree,
181
+ dispatch,
182
+ }),
177
183
  h(Divider),
178
184
  h(EntityTypeSelector, {
179
185
  entityTypes: entityTypesMap,
@@ -375,304 +381,3 @@ function ManagedSelectionTree(props) {
375
381
  }),
376
382
  );
377
383
  }
378
-
379
- function TypeList({ types, selected, dispatch, selectedNodes, tree }) {
380
- const [selectedType, setSelectedType] = useState(null);
381
- const isSelectedNodes = selectedNodes.length > 0;
382
- const darkMode = useInDarkMode();
383
- const luminance = darkMode ? 0.9 : 0.4;
384
-
385
- return h("div.type-list-container", [
386
- h(
387
- "div.type-list-header",
388
- isSelectedNodes && !selectedType
389
- ? "Change selected nodes to:"
390
- : "Entity Types",
391
- ),
392
- h(
393
- "div.type-list",
394
- Array.from(types.values()).map((type) =>
395
- h(TypeTag, {
396
- type,
397
- luminance,
398
- selectedType,
399
- setSelectedType,
400
- dispatch,
401
- tree,
402
- selectedNodes,
403
- selected,
404
- isSelectedNodes,
405
- }),
406
- ),
407
- ),
408
- h(AddType, { dispatch }),
409
- ]);
410
- }
411
-
412
- function collectMatchingIds(tree, id) {
413
- const ids = [];
414
-
415
- function traverse(node) {
416
- if (node.type.id === id) {
417
- ids.push(node.id);
418
- }
419
- if (Array.isArray(node.children)) {
420
- node.children.forEach(traverse);
421
- }
422
- }
423
-
424
- tree.forEach(traverse);
425
- return ids;
426
- }
427
-
428
- function AddType({ dispatch }) {
429
- const [overlayOpen, setOverlayOpen] = useState(false);
430
-
431
- const saveHandler = (payload) => {
432
- dispatch({
433
- type: "add-entity-type",
434
- payload,
435
- });
436
- setOverlayOpen(false);
437
- };
438
-
439
- return h("div.add-type-container", [
440
- h("div.add-type", { onClick: () => setOverlayOpen(true) }, [
441
- h("p.add-type-text", "Add new type"),
442
- h(Icon, { icon: "plus" }),
443
- ]),
444
- h(TypeOverlay, {
445
- setOverlayOpen,
446
- overlayOpen,
447
- title: "Add New Type",
448
- saveHandler,
449
- }),
450
- ]);
451
- }
452
-
453
- function EditType({ dispatch, type }) {
454
- const [editorOpen, setEditorOpen] = useState(false);
455
-
456
- const saveHandler = (payload) => {
457
- dispatch({
458
- type: "update-entity-type",
459
- payload,
460
- });
461
- setEditorOpen(false);
462
- };
463
-
464
- return h("div.edit-type", [
465
- h(Icon, {
466
- icon: "edit",
467
- className: "edit-icon",
468
- onClick: (e) => {
469
- e.stopPropagation();
470
- setEditorOpen(true);
471
- },
472
- }),
473
- h(TypeOverlay, {
474
- setOverlayOpen: setEditorOpen,
475
- overlayOpen: editorOpen,
476
- originalType: type,
477
- title: "Edit Type",
478
- saveHandler,
479
- }),
480
- ]);
481
- }
482
-
483
- function TypeOverlay({
484
- setOverlayOpen,
485
- overlayOpen,
486
- originalType,
487
- title,
488
- saveHandler,
489
- }) {
490
- const { name, description, color, id } = originalType || {};
491
-
492
- const [nameInput, setNameInput] = useState(name || "");
493
- const [descriptionInput, setDescriptionInput] = useState(description || "");
494
- const [colorInput, setColorInput] = useState(color || "#fff");
495
-
496
- return h(
497
- Overlay2,
498
- {
499
- isOpen: overlayOpen,
500
- },
501
- h(
502
- "div.overlay-container",
503
- h("div.add-type-overlay", [
504
- h("h2.title", [
505
- title,
506
- h(Icon, {
507
- icon: "cross",
508
- className: "close-icon",
509
- onClick: () => {
510
- setOverlayOpen(false);
511
- },
512
- style: { cursor: "pointer", color: "red" },
513
- }),
514
- ]),
515
- h("div.form-group", [
516
- h("div.text-inputs", [
517
- h("div.form-field.name", [
518
- h("p.label", "Name"),
519
- h("input", {
520
- type: "text",
521
- placeholder: "Enter type name",
522
- onChange: (e) => setNameInput(e.target.value),
523
- value: nameInput,
524
- }),
525
- ]),
526
- h("div.form-field.form-description", [
527
- h("p.label", "Description"),
528
- h("input", {
529
- type: "text",
530
- placeholder: "Enter type description",
531
- onChange: (e) => setDescriptionInput(e.target.value),
532
- value: descriptionInput,
533
- }),
534
- ]),
535
- ]),
536
- h("div.form-field.color", [
537
- h("p.label", "Color"),
538
- h(ColorPicker, {
539
- value: colorInput,
540
- onChange: (color) => setColorInput(color),
541
- style: { width: "100%" },
542
- }),
543
- ]),
544
- ]),
545
- h(
546
- SaveButton,
547
- {
548
- className: "save-btn",
549
- small: true,
550
- onClick: () =>
551
- saveHandler({
552
- name: nameInput,
553
- description: descriptionInput,
554
- color: colorInput,
555
- id,
556
- }),
557
- },
558
- "Save changes",
559
- ),
560
- ]),
561
- ),
562
- );
563
- }
564
-
565
- function TypeTag({
566
- type,
567
- luminance,
568
- selectedType,
569
- setSelectedType,
570
- dispatch,
571
- tree,
572
- selectedNodes,
573
- selected,
574
- isSelectedNodes,
575
- }) {
576
- const { color, name, id, description } = type;
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
- });
584
-
585
- const payload = {
586
- id,
587
- name,
588
- color,
589
- description,
590
- };
591
-
592
- const ids = collectMatchingIds(tree, id);
593
-
594
- const handleTagClick = () => {
595
- if (!isSelectedNodes && selectedType === null) {
596
- if (ids.length > 0) {
597
- setSelectedType(type);
598
- dispatch({ type: "toggle-node-selected", payload: { ids } });
599
- }
600
- } else if (isSelectedNodes && selectedType === null) {
601
- if (id === selected?.id && selectedNodes.length > 0) {
602
- dispatch({
603
- type: "toggle-node-selected",
604
- payload: { ids: selectedNodes },
605
- });
606
- } else {
607
- dispatch({ type: "select-entity-type", payload });
608
- }
609
- } else if (isSelectedNodes && selectedType.id === id) {
610
- setSelectedType(null);
611
- dispatch({ type: "toggle-node-selected", payload: { ids } });
612
- } else if (isSelectedNodes && selectedType.id !== id) {
613
- if (ids.length > 0) {
614
- setSelectedType(type);
615
- const oldIds = collectMatchingIds(tree, selectedType.id);
616
-
617
- dispatch({ type: "toggle-node-selected", payload: { ids: oldIds } });
618
- dispatch({ type: "toggle-node-selected", payload: { ids } });
619
- }
620
- } else {
621
- console.warn("Unexpected state in TypeTag click handler", {
622
- isSelectedNodes,
623
- selectedType,
624
- selectedNodes,
625
- ids,
626
- id,
627
- selected,
628
- });
629
- }
630
- };
631
-
632
- return h(
633
- Popover,
634
- {
635
- autoFocus: false,
636
- content: h("div.description", description || "No description available"),
637
- interactionKind: "hover",
638
- },
639
- h(
640
- "div.type-tag",
641
- {
642
- onClick: handleTagClick,
643
- style: {
644
- cursor:
645
- ids.length > 0 || (isSelectedNodes && !selectedType)
646
- ? "pointer"
647
- : "",
648
- color: "black",
649
- backgroundColor: style.backgroundColor,
650
- border: isSelected
651
- ? `1px solid var(--text-emphasized-color)`
652
- : `1px solid var(--background-color)`,
653
- },
654
- },
655
- h("div.type-container", [
656
- h("div.type-name", name),
657
- h("div.icons", [
658
- h(EditType, {
659
- dispatch,
660
- type,
661
- }),
662
- h(Icon, {
663
- icon: "cross",
664
- className: "delete-type-icon",
665
- style: { color: "red", cursor: "pointer" },
666
- onClick: (e) => {
667
- e.stopPropagation();
668
- dispatch({
669
- type: "delete-entity-type",
670
- payload: { id },
671
- });
672
- },
673
- }),
674
- ]),
675
- ]),
676
- ),
677
- );
678
- }
@@ -0,0 +1,241 @@
1
+ import { Switch } from "@blueprintjs/core";
2
+ import { Match } from "./text-visualizer";
3
+ import { Select } from "@blueprintjs/select";
4
+ import styles from "./feedback.module.sass";
5
+ import hyper from "@macrostrat/hyper";
6
+ import { useState } from "react";
7
+ import { Icon, Divider, Overlay2 } from "@blueprintjs/core";
8
+ import { JSONView, SaveButton } from "@macrostrat/ui-components";
9
+ import { useAPIResult, DataField } from "@macrostrat/ui-components";
10
+ import { LithologyTag } from "@macrostrat/data-components";
11
+
12
+ const h = hyper.styled(styles);
13
+
14
+ export function Matches({
15
+ match,
16
+ setMatchLinks,
17
+ matchLinks,
18
+ selectedNodes,
19
+ tree,
20
+ dispatch,
21
+ }) {
22
+ const [overlayOpen, setOverlayOpen] = useState(false);
23
+
24
+ let nodeMatch = null;
25
+ if (selectedNodes.length === 1) {
26
+ nodeMatch = findMatchingNode(tree, selectedNodes[0]);
27
+ }
28
+
29
+ return h.if(matchLinks)("div", [
30
+ h(Divider),
31
+ h(Switch, {
32
+ label: "Show matches",
33
+ checked: match !== null,
34
+ onChange: (e) => {
35
+ setMatchLinks(match === null ? matchLinks || {} : null);
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) => {
99
+ const type = data.type || "";
100
+
101
+ if (type === "lith") {
102
+ return h(
103
+ "div",
104
+ {
105
+ onClick: () => {
106
+ setPayload({ lith_id: data.id, name: data.name });
107
+ },
108
+ },
109
+ h(DataField, {
110
+ className: "match-item",
111
+ label: "Lithology",
112
+ value: h(LithologyTag, {
113
+ data: { name: data.name, id: data.lith_id, color: data.color },
114
+ }),
115
+ }),
116
+ );
117
+ }
118
+
119
+ if (type === "strat_name") {
120
+ return h(
121
+ "div",
122
+ {
123
+ onClick: () => {
124
+ setPayload({ strat_name_id: data.id, name: data.name });
125
+ },
126
+ },
127
+ h(DataField, {
128
+ className: "match-item",
129
+ label: "Stratigraphic name",
130
+ value: h(LithologyTag, {
131
+ data: { name: data.name, id: data.id, color: data.color },
132
+ }),
133
+ }),
134
+ );
135
+ }
136
+
137
+ if (type === "lith_att") {
138
+ return h(
139
+ "div",
140
+ {
141
+ onClick: () => {
142
+ setPayload({ lith_att_id: data.lith_att_id, name: data.name });
143
+ },
144
+ },
145
+ h(DataField, {
146
+ className: "match-item",
147
+ label: "Lithology attribute",
148
+ value: h(LithologyTag, {
149
+ data: { name: data.name, id: data.lith_att_id },
150
+ }),
151
+ onClick: () => {
152
+ setPayload({ lith_att_id: data.lith_att_id, name: data.name });
153
+ },
154
+ }),
155
+ );
156
+ }
157
+
158
+ if (type === "interval") {
159
+ h(
160
+ "div",
161
+ {
162
+ onClick: () => {
163
+ setPayload({ int_id: data.id, name: data.name });
164
+ },
165
+ },
166
+ h(DataField, {
167
+ label: "Interval",
168
+ className: "match-item",
169
+ value: h(LithologyTag, {
170
+ data: { name: data.name, id: data.id },
171
+ }),
172
+ onClick: () => {
173
+ setPayload({ int_id: data.id, name: data.name });
174
+ },
175
+ }),
176
+ );
177
+ }
178
+
179
+ return h(JSONView, { data });
180
+ });
181
+
182
+ return h(
183
+ Overlay2,
184
+ {
185
+ isOpen,
186
+ },
187
+ h(
188
+ "div.overlay-container",
189
+ h("div.add-type-overlay", [
190
+ h("h2.title", [
191
+ "Add match with " + nodeMatch.name,
192
+ h(Icon, {
193
+ icon: "cross",
194
+ className: "close-icon",
195
+ onClick: () => {
196
+ setOverlayOpen(false);
197
+ },
198
+ style: { cursor: "pointer", color: "red" },
199
+ }),
200
+ ]),
201
+ h("div.form-group", [
202
+ h(
203
+ Select,
204
+ {
205
+ items: items || [],
206
+ itemRenderer: (item, { handleClick }) => {
207
+ return h("div.match-item", { onClick: handleClick }, item);
208
+ },
209
+ onItemSelect: (item) => {
210
+ setDisabled(false);
211
+ setSelectedItem(item);
212
+ },
213
+ onQueryChange: (query) => setInputValue(query),
214
+ popoverProps: { minimal: true },
215
+ query: inputValue,
216
+ placeholder: "Enter match name",
217
+ },
218
+ selectedItem,
219
+ ),
220
+ ]),
221
+ h(
222
+ SaveButton,
223
+ {
224
+ className: "save-btn",
225
+ small: true,
226
+ onClick: () => {
227
+ // Handle save changes
228
+ dispatch({
229
+ type: "add-match",
230
+ payload: { id: nodeMatch.id, payload },
231
+ });
232
+ setOverlayOpen(false);
233
+ },
234
+ disabled,
235
+ },
236
+ "Save changes",
237
+ ),
238
+ ]),
239
+ ),
240
+ );
241
+ }