@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
@@ -17,6 +17,8 @@ interface TreeState {
17
17
  lastInternalId: number;
18
18
  isSelectingEntityType: boolean;
19
19
  viewMode: ViewMode;
20
+ viewOnly: boolean;
21
+ matchMode: boolean;
20
22
  }
21
23
 
22
24
  type TextRange = {
@@ -48,13 +50,18 @@ type TreeAction =
48
50
  type: "update-entity-type";
49
51
  payload: { id: number; name: string; description: string; color: string };
50
52
  }
51
- | { type: "select-range"; payload: { ids: number[] } };
53
+ | { type: "select-range"; payload: { ids: number[] } }
54
+ | { type: "add-match"; payload: { id: number; payload: any } }
55
+ | { type: "remove-match"; payload: { id: number } }
56
+ | { type: "toggle-match-mode" };
52
57
 
53
58
  export type TreeDispatch = Dispatch<TreeAction>;
54
59
 
55
60
  export function useUpdatableTree(
56
61
  initialTree: TreeData[],
57
62
  entityTypes: Map<number, EntityType>,
63
+ viewOnly: boolean,
64
+ matchMode: boolean,
58
65
  ): [TreeState, TreeDispatch] {
59
66
  // Get the first entity type
60
67
  // issue: grabs second entity instead of selected one
@@ -69,6 +76,8 @@ export function useUpdatableTree(
69
76
  lastInternalId: 0,
70
77
  isSelectingEntityType: false,
71
78
  viewMode: ViewMode.Tree,
79
+ viewOnly,
80
+ matchMode,
72
81
  });
73
82
  }
74
83
 
@@ -83,6 +92,14 @@ export function useTreeDispatch() {
83
92
  }
84
93
 
85
94
  function treeReducer(state: TreeState, action: TreeAction) {
95
+ if (action.type === "toggle-match-mode") {
96
+ return { ...state, matchMode: !state.matchMode };
97
+ }
98
+
99
+ if (state.viewOnly) return viewMode(state, action);
100
+
101
+ if (state.matchMode) return matchMode(state, action);
102
+
86
103
  switch (action.type) {
87
104
  case "add-entity-type": {
88
105
  // Add a new entity type to the map
@@ -246,7 +263,6 @@ function treeReducer(state: TreeState, action: TreeAction) {
246
263
 
247
264
  case "delete-entity-type": {
248
265
  // Remove the entity type from the map
249
- console.log("Deleting entity type:", action.payload.id);
250
266
  const { id } = action.payload;
251
267
  const newEntityTypesMap = new Map(state.entityTypesMap);
252
268
  const oldType = newEntityTypesMap.get(id);
@@ -431,7 +447,7 @@ export function treeToGraph(tree: TreeData[]): GraphData {
431
447
  return { nodes, edges };
432
448
  }
433
449
 
434
- function findNodeById(tree, id) {
450
+ export function findNodeById(tree, id) {
435
451
  for (const node of tree) {
436
452
  if (node.id === id) {
437
453
  return node;
@@ -477,3 +493,83 @@ function flattenAndSort(nodes) {
477
493
  // sort by start
478
494
  return result.sort((a, b) => a.indices[0] - b.indices[0]);
479
495
  }
496
+
497
+ function matchMode(state, action) {
498
+ if (action.type === "select-node" || action.type === "toggle-node-selected") {
499
+ const { ids } = action.payload;
500
+
501
+ if (ids.length != 1) return state;
502
+
503
+ if (state.selectedNodes.length === 1) {
504
+ if (ids[0] === state.selectedNodes[0]) {
505
+ // If the selected node is the same as the current selection, deselect it
506
+ return { ...state, selectedNodes: [] };
507
+ }
508
+ }
509
+
510
+ const type =
511
+ action.payload.ids.length > 0
512
+ ? findNodeById(state.tree, ids[0])?.type
513
+ : null;
514
+
515
+ return { ...state, selectedNodes: ids, selectedEntityType: type };
516
+ }
517
+
518
+ if (action.type === "add-match") {
519
+ const { id } = action.payload;
520
+
521
+ // Find the node path
522
+ const keyPath = findNode(state.tree, id);
523
+ if (!keyPath) {
524
+ console.warn(`Node with id ${id} not found`);
525
+ return state;
526
+ }
527
+
528
+ // Build update spec to set the `match` property
529
+ const matchUpdateSpec = buildNestedSpec(keyPath, {
530
+ match: { $set: action.payload.payload },
531
+ });
532
+
533
+ const updatedTree = update(state.tree, matchUpdateSpec);
534
+
535
+ return {
536
+ ...state,
537
+ tree: updatedTree,
538
+ };
539
+ }
540
+
541
+ if (action.type === "remove-match") {
542
+ const { id } = action.payload;
543
+
544
+ console.log("Removing match for node with id:", id);
545
+
546
+ // Find the node path
547
+ const keyPath = findNode(state.tree, id);
548
+ if (!keyPath) {
549
+ console.warn(`Node with id ${id} not found`);
550
+ return state;
551
+ }
552
+
553
+ // Build update spec to unset the `match` property
554
+ const matchUpdateSpec = buildNestedSpec(keyPath, {
555
+ match: { $set: null },
556
+ });
557
+
558
+ const updatedTree = update(state.tree, matchUpdateSpec);
559
+
560
+ return {
561
+ ...state,
562
+ tree: updatedTree,
563
+ };
564
+ }
565
+
566
+ return state;
567
+ }
568
+
569
+ function viewMode(state, action) {
570
+ if (action.type === "set-view-mode") {
571
+ return { ...state, viewMode: action.payload };
572
+ }
573
+
574
+ return state;
575
+ }
@@ -31,7 +31,6 @@
31
31
  cursor: pointer
32
32
 
33
33
  circle
34
- cursor: pointer
35
34
  border: 1px solid black
36
35
 
37
36
  .selected
@@ -79,12 +78,14 @@ mark
79
78
  width: auto !important
80
79
 
81
80
  .highlight
82
- cursor: pointer
83
81
  padding: .2em 0
84
82
  border-radius: .2em
85
83
  position: relative
86
84
  zIndex: 10
87
85
 
86
+ .clickable
87
+ cursor: pointer
88
+
88
89
  .feedback-text-wrapper
89
90
  position: relative
90
91
  z-index: 0
@@ -115,7 +116,7 @@ mark
115
116
  align-items: center
116
117
 
117
118
  .add-type-overlay
118
- background-color: var(--secondary-color)
119
+ background-color: var(--tertiary-background)
119
120
  padding: .5em 1em
120
121
  display: flex
121
122
  flex-direction: column
@@ -146,4 +147,23 @@ mark
146
147
  .node-label
147
148
  fill: var(--text-emphasized-color)
148
149
  fontSize: 10px
149
- pointerEvents: none
150
+ pointerEvents: none
151
+
152
+ .match-item
153
+ color: var(--text-emphasized-color)
154
+ padding: .1em .2em
155
+ border-radius: .2em
156
+ margin-bottom: 4px
157
+ cursor: pointer
158
+
159
+ .match-label
160
+ margin: 0
161
+ color: var(--text-color)
162
+
163
+ .match-container
164
+ display: flex
165
+ justify-content: space-between
166
+ align-items: center
167
+
168
+ .close-btn
169
+ cursor: pointer
@@ -24,10 +24,11 @@ export function GraphView(props: {
24
24
  height: number;
25
25
  dispatch: (action: any) => void;
26
26
  selectedNodes: number[];
27
+ viewOnly?: boolean;
27
28
  }) {
28
29
  // A graph view with react-flow
29
30
  // Get positions of nodes using force simulation
30
- const { tree, width, height, dispatch, selectedNodes } = props;
31
+ const { tree, width, height, dispatch, selectedNodes, viewOnly } = props;
31
32
 
32
33
  const [nodes, setNodes] = useState<SimulationNodeDatum[]>(null);
33
34
  const [links, setLinks] = useState<SimulationLinkDatum[]>(null);
@@ -134,7 +135,9 @@ export function GraphView(props: {
134
135
  });
135
136
  }
136
137
  },
137
- className: active ? "selected" : "",
138
+ className: active
139
+ ? "selected"
140
+ : "" + (viewOnly ? "" : " clickable"),
138
141
  stroke,
139
142
  strokeWidth: 2,
140
143
  }),
@@ -18,26 +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";
40
- import { Switch } from "@blueprintjs/core";
32
+
33
+ import { Matches } from "./matches";
34
+ import { TypeList } from "./typelist";
41
35
 
42
36
  export type { GraphData } from "./edit-state";
43
37
  export { treeToGraph } from "./edit-state";
@@ -62,13 +56,19 @@ export function FeedbackComponent({
62
56
  onSave,
63
57
  allowOverlap,
64
58
  matchLinks,
59
+ view = false,
65
60
  }) {
61
+ const [viewOnly, setViewOnly] = useState(view);
62
+ const [match, setMatchLinks] = useState(matchLinks);
63
+ const matchMode = match !== null;
64
+
66
65
  // Get the input arguments
67
66
  const [state, dispatch] = useUpdatableTree(
68
67
  entities.map(processEntity) as any,
69
68
  entityTypes,
69
+ viewOnly,
70
+ matchMode,
70
71
  );
71
- const [match, setMatchLinks] = useState(matchLinks || {});
72
72
 
73
73
  const {
74
74
  selectedNodes,
@@ -84,6 +84,18 @@ export function FeedbackComponent({
84
84
  h(
85
85
  "div.feedback-container",
86
86
  h(TreeDispatchContext.Provider, { value: dispatch }, [
87
+ h.if(!view)(SegmentedControl, {
88
+ options: [
89
+ { label: "View", value: "view" },
90
+ { label: "Edit", value: "edit" },
91
+ ],
92
+ value: viewOnly ? "view" : "edit",
93
+ small: true,
94
+ onValueChange() {
95
+ setViewOnly(!viewOnly);
96
+ },
97
+ role: "toolbar",
98
+ }),
87
99
  h(
88
100
  ErrorBoundary,
89
101
  {
@@ -98,6 +110,7 @@ export function FeedbackComponent({
98
110
  selectedNodes,
99
111
  allowOverlap,
100
112
  matchLinks: match,
113
+ viewOnly,
101
114
  }),
102
115
  ),
103
116
  h(
@@ -139,6 +152,7 @@ export function FeedbackComponent({
139
152
  height,
140
153
  dispatch,
141
154
  selectedNodes,
155
+ viewOnly,
142
156
  }),
143
157
  ],
144
158
  ),
@@ -146,7 +160,7 @@ export function FeedbackComponent({
146
160
  ),
147
161
  h(Card, { className: "control-panel" }, [
148
162
  h("div.control-content", [
149
- h(
163
+ h.if(!viewOnly)(
150
164
  ButtonGroup,
151
165
  {
152
166
  vertical: true,
@@ -155,13 +169,6 @@ export function FeedbackComponent({
155
169
  alignText: "left",
156
170
  },
157
171
  [
158
- h.if(matchLinks)(Switch, {
159
- label: "Show matches",
160
- checked: match !== null,
161
- onChange: (e) => {
162
- setMatchLinks(match === null ? matchLinks || {} : null);
163
- },
164
- }),
165
172
  h(
166
173
  CancelButton,
167
174
  {
@@ -185,7 +192,15 @@ export function FeedbackComponent({
185
192
  ),
186
193
  ],
187
194
  ),
188
- h(Divider),
195
+ h.if(!viewOnly)(Matches, {
196
+ match,
197
+ setMatchLinks,
198
+ matchLinks,
199
+ selectedNodes,
200
+ tree,
201
+ dispatch,
202
+ }),
203
+ h.if(!viewOnly)(Divider),
189
204
  h(EntityTypeSelector, {
190
205
  entityTypes: entityTypesMap,
191
206
  selected: selectedEntityType,
@@ -201,6 +216,8 @@ export function FeedbackComponent({
201
216
  type: "toggle-entity-type-selector",
202
217
  payload: isOpen,
203
218
  }),
219
+ viewOnly,
220
+ matchMode,
204
221
  }),
205
222
  ]),
206
223
  ]),
@@ -227,6 +244,8 @@ function EntityTypeSelector({
227
244
  tree,
228
245
  dispatch,
229
246
  selectedNodes = [],
247
+ viewOnly,
248
+ matchMode,
230
249
  }) {
231
250
  // Show all entity types when selected is null
232
251
  const _selected = selected != null ? selected : undefined;
@@ -247,6 +266,7 @@ function EntityTypeSelector({
247
266
  dispatch,
248
267
  selectedNodes,
249
268
  tree,
269
+ viewOnly: viewOnly || matchMode,
250
270
  }),
251
271
  h(OmniboxSelector, {
252
272
  isOpen,
@@ -386,304 +406,3 @@ function ManagedSelectionTree(props) {
386
406
  }),
387
407
  );
388
408
  }
389
-
390
- function TypeList({ types, selected, dispatch, selectedNodes, tree }) {
391
- const [selectedType, setSelectedType] = useState(null);
392
- const isSelectedNodes = selectedNodes.length > 0;
393
- const darkMode = useInDarkMode();
394
- const luminance = darkMode ? 0.9 : 0.4;
395
-
396
- return h("div.type-list-container", [
397
- h(
398
- "div.type-list-header",
399
- isSelectedNodes && !selectedType
400
- ? "Change selected nodes to:"
401
- : "Entity Types",
402
- ),
403
- h(
404
- "div.type-list",
405
- Array.from(types.values()).map((type) =>
406
- h(TypeTag, {
407
- type,
408
- luminance,
409
- selectedType,
410
- setSelectedType,
411
- dispatch,
412
- tree,
413
- selectedNodes,
414
- selected,
415
- isSelectedNodes,
416
- }),
417
- ),
418
- ),
419
- h(AddType, { dispatch }),
420
- ]);
421
- }
422
-
423
- function collectMatchingIds(tree, id) {
424
- const ids = [];
425
-
426
- function traverse(node) {
427
- if (node.type.id === id) {
428
- ids.push(node.id);
429
- }
430
- if (Array.isArray(node.children)) {
431
- node.children.forEach(traverse);
432
- }
433
- }
434
-
435
- tree.forEach(traverse);
436
- return ids;
437
- }
438
-
439
- function AddType({ dispatch }) {
440
- const [overlayOpen, setOverlayOpen] = useState(false);
441
-
442
- const saveHandler = (payload) => {
443
- dispatch({
444
- type: "add-entity-type",
445
- payload,
446
- });
447
- setOverlayOpen(false);
448
- };
449
-
450
- return h("div.add-type-container", [
451
- h("div.add-type", { onClick: () => setOverlayOpen(true) }, [
452
- h("p.add-type-text", "Add new type"),
453
- h(Icon, { icon: "plus" }),
454
- ]),
455
- h(TypeOverlay, {
456
- setOverlayOpen,
457
- overlayOpen,
458
- title: "Add New Type",
459
- saveHandler,
460
- }),
461
- ]);
462
- }
463
-
464
- function EditType({ dispatch, type }) {
465
- const [editorOpen, setEditorOpen] = useState(false);
466
-
467
- const saveHandler = (payload) => {
468
- dispatch({
469
- type: "update-entity-type",
470
- payload,
471
- });
472
- setEditorOpen(false);
473
- };
474
-
475
- return h("div.edit-type", [
476
- h(Icon, {
477
- icon: "edit",
478
- className: "edit-icon",
479
- onClick: (e) => {
480
- e.stopPropagation();
481
- setEditorOpen(true);
482
- },
483
- }),
484
- h(TypeOverlay, {
485
- setOverlayOpen: setEditorOpen,
486
- overlayOpen: editorOpen,
487
- originalType: type,
488
- title: "Edit Type",
489
- saveHandler,
490
- }),
491
- ]);
492
- }
493
-
494
- function TypeOverlay({
495
- setOverlayOpen,
496
- overlayOpen,
497
- originalType,
498
- title,
499
- saveHandler,
500
- }) {
501
- const { name, description, color, id } = originalType || {};
502
-
503
- const [nameInput, setNameInput] = useState(name || "");
504
- const [descriptionInput, setDescriptionInput] = useState(description || "");
505
- const [colorInput, setColorInput] = useState(color || "#fff");
506
-
507
- return h(
508
- Overlay2,
509
- {
510
- isOpen: overlayOpen,
511
- },
512
- h(
513
- "div.overlay-container",
514
- h("div.add-type-overlay", [
515
- h("h2.title", [
516
- title,
517
- h(Icon, {
518
- icon: "cross",
519
- className: "close-icon",
520
- onClick: () => {
521
- setOverlayOpen(false);
522
- },
523
- style: { cursor: "pointer", color: "red" },
524
- }),
525
- ]),
526
- h("div.form-group", [
527
- h("div.text-inputs", [
528
- h("div.form-field.name", [
529
- h("p.label", "Name"),
530
- h("input", {
531
- type: "text",
532
- placeholder: "Enter type name",
533
- onChange: (e) => setNameInput(e.target.value),
534
- value: nameInput,
535
- }),
536
- ]),
537
- h("div.form-field.form-description", [
538
- h("p.label", "Description"),
539
- h("input", {
540
- type: "text",
541
- placeholder: "Enter type description",
542
- onChange: (e) => setDescriptionInput(e.target.value),
543
- value: descriptionInput,
544
- }),
545
- ]),
546
- ]),
547
- h("div.form-field.color", [
548
- h("p.label", "Color"),
549
- h(ColorPicker, {
550
- value: colorInput,
551
- onChange: (color) => setColorInput(color),
552
- style: { width: "100%" },
553
- }),
554
- ]),
555
- ]),
556
- h(
557
- SaveButton,
558
- {
559
- className: "save-btn",
560
- small: true,
561
- onClick: () =>
562
- saveHandler({
563
- name: nameInput,
564
- description: descriptionInput,
565
- color: colorInput,
566
- id,
567
- }),
568
- },
569
- "Save changes",
570
- ),
571
- ]),
572
- ),
573
- );
574
- }
575
-
576
- function TypeTag({
577
- type,
578
- luminance,
579
- selectedType,
580
- setSelectedType,
581
- dispatch,
582
- tree,
583
- selectedNodes,
584
- selected,
585
- isSelectedNodes,
586
- }) {
587
- const { color, name, id, description } = type;
588
- const darkMode = useInDarkMode();
589
- const isSelected = id === selected?.id && selectedNodes.length > 0;
590
-
591
- const style = getTagStyle(color, {
592
- active: isSelected,
593
- highlighted: selectedNodes.length === 0,
594
- });
595
-
596
- const payload = {
597
- id,
598
- name,
599
- color,
600
- description,
601
- };
602
-
603
- const ids = collectMatchingIds(tree, id);
604
-
605
- const handleTagClick = () => {
606
- if (!isSelectedNodes && selectedType === null) {
607
- if (ids.length > 0) {
608
- setSelectedType(type);
609
- dispatch({ type: "toggle-node-selected", payload: { ids } });
610
- }
611
- } else if (isSelectedNodes && selectedType === null) {
612
- if (id === selected?.id && selectedNodes.length > 0) {
613
- dispatch({
614
- type: "toggle-node-selected",
615
- payload: { ids: selectedNodes },
616
- });
617
- } else {
618
- dispatch({ type: "select-entity-type", payload });
619
- }
620
- } else if (isSelectedNodes && selectedType.id === id) {
621
- setSelectedType(null);
622
- dispatch({ type: "toggle-node-selected", payload: { ids } });
623
- } else if (isSelectedNodes && selectedType.id !== id) {
624
- if (ids.length > 0) {
625
- setSelectedType(type);
626
- const oldIds = collectMatchingIds(tree, selectedType.id);
627
-
628
- dispatch({ type: "toggle-node-selected", payload: { ids: oldIds } });
629
- dispatch({ type: "toggle-node-selected", payload: { ids } });
630
- }
631
- } else {
632
- console.warn("Unexpected state in TypeTag click handler", {
633
- isSelectedNodes,
634
- selectedType,
635
- selectedNodes,
636
- ids,
637
- id,
638
- selected,
639
- });
640
- }
641
- };
642
-
643
- return h(
644
- Popover,
645
- {
646
- autoFocus: false,
647
- content: h("div.description", description || "No description available"),
648
- interactionKind: "hover",
649
- },
650
- h(
651
- "div.type-tag",
652
- {
653
- onClick: handleTagClick,
654
- style: {
655
- cursor:
656
- ids.length > 0 || (isSelectedNodes && !selectedType)
657
- ? "pointer"
658
- : "",
659
- color: "black",
660
- backgroundColor: style.backgroundColor,
661
- border: isSelected
662
- ? `1px solid var(--text-emphasized-color)`
663
- : `1px solid var(--background-color)`,
664
- },
665
- },
666
- h("div.type-container", [
667
- h("div.type-name", name),
668
- h("div.icons", [
669
- h(EditType, {
670
- dispatch,
671
- type,
672
- }),
673
- h(Icon, {
674
- icon: "cross",
675
- className: "delete-type-icon",
676
- style: { color: "red", cursor: "pointer" },
677
- onClick: (e) => {
678
- e.stopPropagation();
679
- dispatch({
680
- type: "delete-entity-type",
681
- payload: { id },
682
- });
683
- },
684
- }),
685
- ]),
686
- ]),
687
- ),
688
- );
689
- }