@macrostrat/feedback-components 1.0.0 → 1.1.0

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 (86) hide show
  1. package/CHANGELOG.md +13 -5
  2. package/dist/esm/feedback-components.4359bc80.js +70 -0
  3. package/dist/esm/feedback-components.4359bc80.js.map +1 -0
  4. package/dist/{extractions.54be85f8.js → esm/feedback-components.5df2a926.js} +52 -24
  5. package/dist/esm/feedback-components.5df2a926.js.map +1 -0
  6. package/dist/esm/feedback-components.6a6c8af5.js +552 -0
  7. package/dist/esm/feedback-components.6a6c8af5.js.map +1 -0
  8. package/dist/{main.module.ca3db294.js → esm/feedback-components.6d32ee91.js} +1 -1
  9. package/dist/{main.module.ca3db294.js.map → esm/feedback-components.6d32ee91.js.map} +1 -1
  10. package/dist/esm/feedback-components.7cd9b6cc.js +114 -0
  11. package/dist/esm/feedback-components.7cd9b6cc.js.map +1 -0
  12. package/dist/{node.30d0b8c3.js → esm/feedback-components.87533431.js} +12 -7
  13. package/dist/esm/feedback-components.87533431.js.map +1 -0
  14. package/dist/esm/feedback-components.921dcd46.js +241 -0
  15. package/dist/esm/feedback-components.921dcd46.js.map +1 -0
  16. package/dist/{type-selector.e75dd247.js → esm/feedback-components.ad9f284e.js} +11 -10
  17. package/dist/esm/feedback-components.ad9f284e.js.map +1 -0
  18. package/dist/esm/feedback-components.b7d9b015.css +156 -0
  19. package/dist/esm/feedback-components.b7d9b015.css.map +1 -0
  20. package/dist/{edit-state.e8edb13a.js → esm/feedback-components.bf5f7cf7.js} +105 -14
  21. package/dist/esm/feedback-components.bf5f7cf7.js.map +1 -0
  22. package/dist/{main.module.f9f92ece.css → esm/feedback-components.bf93773c.css} +1 -1
  23. package/dist/{main.module.f9f92ece.css.map → esm/feedback-components.bf93773c.css.map} +1 -1
  24. package/dist/{main.module.d6508c0e.css → esm/feedback-components.e273ed5b.css} +1 -1
  25. package/dist/{main.module.d6508c0e.css.map → esm/feedback-components.e273ed5b.css.map} +1 -1
  26. package/dist/{main.module.21bbfaf4.js → esm/feedback-components.f9850d85.js} +1 -1
  27. package/dist/{main.module.21bbfaf4.js.map → esm/feedback-components.f9850d85.js.map} +1 -1
  28. package/dist/{index.d.ts → esm/index.d.ts} +38 -13
  29. package/dist/esm/index.d.ts.map +1 -0
  30. package/dist/{index.js → esm/index.js} +2 -2
  31. package/dist/node/feedback-components.15e1316d.js +2 -0
  32. package/dist/node/feedback-components.15e1316d.js.map +1 -0
  33. package/dist/node/feedback-components.2f391fa4.js +2 -0
  34. package/dist/node/feedback-components.2f391fa4.js.map +1 -0
  35. package/dist/node/feedback-components.65d8488e.js +2 -0
  36. package/dist/node/feedback-components.65d8488e.js.map +1 -0
  37. package/dist/node/feedback-components.6681dbde.js +2 -0
  38. package/dist/node/feedback-components.6681dbde.js.map +1 -0
  39. package/dist/node/feedback-components.77b6fc89.css +2 -0
  40. package/dist/node/feedback-components.77b6fc89.css.map +1 -0
  41. package/dist/node/feedback-components.794f429b.js +2 -0
  42. package/dist/node/feedback-components.794f429b.js.map +1 -0
  43. package/dist/node/feedback-components.7caa447a.js +2 -0
  44. package/dist/node/feedback-components.7caa447a.js.map +1 -0
  45. package/dist/node/feedback-components.83c21466.css +2 -0
  46. package/dist/node/feedback-components.83c21466.css.map +1 -0
  47. package/dist/node/feedback-components.8b03e8be.js +2 -0
  48. package/dist/node/feedback-components.8b03e8be.js.map +1 -0
  49. package/dist/node/feedback-components.9eb1d41a.css +2 -0
  50. package/dist/node/feedback-components.9eb1d41a.css.map +1 -0
  51. package/dist/node/feedback-components.acac789b.js +2 -0
  52. package/dist/node/feedback-components.acac789b.js.map +1 -0
  53. package/dist/node/feedback-components.e2f3c4b7.js +2 -0
  54. package/dist/node/feedback-components.e2f3c4b7.js.map +1 -0
  55. package/dist/node/feedback-components.e8aa70b8.js +2 -0
  56. package/dist/node/feedback-components.e8aa70b8.js.map +1 -0
  57. package/dist/node/index.js +2 -0
  58. package/dist/node/index.js.map +1 -0
  59. package/package.json +29 -15
  60. package/src/extractions/index.ts +76 -21
  61. package/src/extractions/types.ts +6 -1
  62. package/src/feedback/edit-state.ts +146 -16
  63. package/src/feedback/feedback.module.sass +93 -1
  64. package/src/feedback/graph.ts +71 -30
  65. package/src/feedback/index.ts +444 -71
  66. package/src/feedback/node.ts +7 -1
  67. package/src/feedback/text-visualizer.ts +258 -47
  68. package/src/feedback/type-selector/index.ts +4 -2
  69. package/dist/edit-state.e8edb13a.js.map +0 -1
  70. package/dist/extractions.54be85f8.js.map +0 -1
  71. package/dist/feedback.46c2b5c4.js +0 -252
  72. package/dist/feedback.46c2b5c4.js.map +0 -1
  73. package/dist/feedback.module.7e16830e.css +0 -44
  74. package/dist/feedback.module.7e16830e.css.map +0 -1
  75. package/dist/feedback.module.c28cbac7.js +0 -28
  76. package/dist/feedback.module.c28cbac7.js.map +0 -1
  77. package/dist/graph.cb42b871.js +0 -83
  78. package/dist/graph.cb42b871.js.map +0 -1
  79. package/dist/index.d.ts.map +0 -1
  80. package/dist/node.30d0b8c3.js.map +0 -1
  81. package/dist/text-visualizer.77af0d24.js +0 -101
  82. package/dist/text-visualizer.77af0d24.js.map +0 -1
  83. package/dist/type-selector.e75dd247.js.map +0 -1
  84. package/stories/feedback.stories.ts +0 -40
  85. package/stories/test-data.ts +0 -330
  86. /package/dist/{index.js.map → esm/index.js.map} +0 -0
@@ -13,18 +13,30 @@ import {
13
13
  useUpdatableTree,
14
14
  ViewMode,
15
15
  } from "./edit-state";
16
- import { useCallback, useEffect, useRef } from "react";
17
- import { ButtonGroup, Card, SegmentedControl } from "@blueprintjs/core";
16
+ import { useCallback, useEffect, useRef, useState } from "react";
17
+ import {
18
+ ButtonGroup,
19
+ Card,
20
+ SegmentedControl,
21
+ Icon,
22
+ Popover,
23
+ Divider,
24
+ Overlay2,
25
+ } from "@blueprintjs/core";
18
26
  import { OmniboxSelector } from "./type-selector";
19
27
  import {
20
28
  CancelButton,
21
29
  DataField,
30
+ ErrorBoundary,
22
31
  FlexBox,
23
32
  FlexRow,
24
33
  SaveButton,
25
34
  } from "@macrostrat/ui-components";
26
35
  import useElementDimensions from "use-element-dimensions";
27
36
  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";
28
40
 
29
41
  export type { GraphData } from "./edit-state";
30
42
  export { treeToGraph } from "./edit-state";
@@ -47,27 +59,42 @@ export function FeedbackComponent({
47
59
  entityTypes,
48
60
  matchComponent,
49
61
  onSave,
62
+ lineHeight,
63
+ allowOverlap,
50
64
  }) {
51
65
  // Get the input arguments
52
-
53
66
  const [state, dispatch] = useUpdatableTree(
54
67
  entities.map(processEntity) as any,
55
- entityTypes
68
+ entityTypes,
56
69
  );
57
70
 
58
- const { selectedNodes, tree, selectedEntityType, isSelectingEntityType } =
59
- state;
71
+ const {
72
+ selectedNodes,
73
+ tree,
74
+ selectedEntityType,
75
+ isSelectingEntityType,
76
+ entityTypesMap,
77
+ } = state;
60
78
 
61
79
  const [{ width, height }, ref] = useElementDimensions();
62
80
 
63
81
  return h(TreeDispatchContext.Provider, { value: dispatch }, [
64
- h(FeedbackText, {
65
- text,
66
- dispatch,
67
- // @ts-ignore
68
- nodes: tree,
69
- selectedNodes,
70
- }),
82
+ 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
+ ),
71
98
  h(FlexRow, { alignItems: "baseline", justifyContent: "space-between" }, [
72
99
  h(ModelInfo, { data: model }),
73
100
  h(SegmentedControl, {
@@ -108,7 +135,7 @@ export function FeedbackComponent({
108
135
  dispatch({ type: "reset" });
109
136
  },
110
137
  },
111
- "Reset"
138
+ "Reset",
112
139
  ),
113
140
  h(
114
141
  SaveButton,
@@ -118,16 +145,20 @@ export function FeedbackComponent({
118
145
  },
119
146
  disabled: state.initialTree == state.tree,
120
147
  },
121
- "Save"
148
+ "Save",
122
149
  ),
123
- ]
150
+ ],
124
151
  ),
152
+ h(Divider),
125
153
  h(EntityTypeSelector, {
126
- entityTypes,
154
+ entityTypes: entityTypesMap,
127
155
  selected: selectedEntityType,
128
156
  onChange(payload) {
129
157
  dispatch({ type: "select-entity-type", payload });
130
158
  },
159
+ dispatch,
160
+ tree,
161
+ selectedNodes,
131
162
  isOpen: isSelectingEntityType,
132
163
  setOpen: (isOpen: boolean) =>
133
164
  dispatch({
@@ -148,8 +179,10 @@ export function FeedbackComponent({
148
179
  tree,
149
180
  width,
150
181
  height,
182
+ dispatch,
183
+ selectedNodes,
151
184
  }),
152
- ]
185
+ ],
153
186
  ),
154
187
  ]);
155
188
  }
@@ -171,27 +204,41 @@ function EntityTypeSelector({
171
204
  isOpen,
172
205
  setOpen,
173
206
  onChange,
207
+ tree,
208
+ dispatch,
209
+ selectedNodes = [],
174
210
  }) {
175
211
  // Show all entity types when selected is null
176
212
  const _selected = selected != null ? selected : undefined;
177
- return h(DataField, { label: "Entity type", inline: true }, [
178
- h(
179
- "code.bp5-code",
180
- {
181
- onClick() {
182
- setOpen((d) => !d);
183
- },
184
- },
185
- selected.name
186
- ),
213
+ const [inputValue, setInputValue] = useState("");
214
+ const types = Array.from(entityTypes.values());
215
+
216
+ const items =
217
+ inputValue !== ""
218
+ ? types.filter((d) =>
219
+ d.name.toLowerCase().includes(inputValue.toLowerCase()),
220
+ )
221
+ : types;
222
+
223
+ return h("div.entity-type-selector", [
224
+ h(TypeList, {
225
+ types: entityTypes,
226
+ selected: _selected,
227
+ dispatch,
228
+ selectedNodes,
229
+ tree,
230
+ }),
187
231
  h(OmniboxSelector, {
188
232
  isOpen,
189
- items: Array.from(entityTypes.values()),
233
+ items,
190
234
  selectedItem: _selected,
191
235
  onSelectItem(item) {
192
236
  setOpen(false);
193
237
  onChange(item);
194
238
  },
239
+ onQueryChange(query) {
240
+ setInputValue(query);
241
+ },
195
242
  onClose() {
196
243
  setOpen(false);
197
244
  },
@@ -199,33 +246,44 @@ function EntityTypeSelector({
199
246
  ]);
200
247
  }
201
248
 
249
+ function countNodes(tree) {
250
+ if (!tree) return 0;
251
+ let count = 0;
252
+
253
+ function recurse(nodes) {
254
+ for (const node of nodes) {
255
+ count++;
256
+ if (node.children && Array.isArray(node.children)) {
257
+ recurse(node.children);
258
+ }
259
+ }
260
+ }
261
+
262
+ recurse(tree);
263
+ return count;
264
+ }
265
+
202
266
  function ManagedSelectionTree(props) {
203
- const {
204
- selectedNodes,
205
- dispatch,
206
- tree,
207
- height,
208
- width,
209
- matchComponent,
210
- ...rest
211
- } = props;
267
+ const { selectedNodes, dispatch, tree, height, width, matchComponent } =
268
+ props;
212
269
 
213
270
  const ref = useRef<TreeApi<TreeData>>();
271
+ // Use a ref to track clicks (won't cause rerender)
272
+ const clickedRef = useRef(false);
214
273
 
215
274
  const _Node = useCallback(
216
275
  (props) => h(Node, { ...props, matchComponent }),
217
- [matchComponent]
276
+ [matchComponent],
218
277
  );
219
278
 
279
+ // Update Tree selection when selectedNodes change
220
280
  useEffect(() => {
221
281
  if (ref.current == null) return;
222
- // Check if selection matches current
282
+
223
283
  const selection = new Set(selectedNodes.map((d) => d.toString()));
224
284
  const currentSelection = ref.current.selectedIds;
225
285
  if (setsAreTheSame(selection, currentSelection)) return;
226
- // If the selection is the same, do nothing
227
286
 
228
- // Set selection
229
287
  ref.current.setSelection({
230
288
  ids: selectedNodes.map((d) => d.toString()),
231
289
  anchor: null,
@@ -233,39 +291,354 @@ function ManagedSelectionTree(props) {
233
291
  });
234
292
  }, [selectedNodes]);
235
293
 
236
- return h(Tree, {
237
- className: "selection-tree",
238
- height,
239
- width,
240
- ref,
241
- data: tree,
242
- onMove({ dragIds, parentId, index }) {
243
- dispatch({
244
- type: "move-node",
245
- payload: {
246
- dragIds: dragIds.map((d) => parseInt(d)),
247
- parentId: parentId ? parseInt(parentId) : null,
248
- index,
249
- },
250
- });
251
- },
252
- onDelete({ ids }) {
253
- dispatch({
254
- type: "delete-node",
255
- payload: { ids: ids.map((d) => parseInt(d)) },
256
- });
257
- },
258
- onSelect(nodes) {
294
+ // Mark clicked when user clicks inside the tree container
295
+ function handleClick() {
296
+ clickedRef.current = true;
297
+ }
298
+
299
+ const handleSelect = useCallback(
300
+ (nodes) => {
301
+ if (!clickedRef.current) return;
302
+ clickedRef.current = false;
303
+ console.log("Clicked nodes:", nodes);
304
+
259
305
  let ids = nodes.map((d) => parseInt(d.id));
260
- if (ids.length == 1 && ids[0] == selectedNodes[0]) {
261
- // Deselect
306
+ if (ids.length === 1 && ids[0] === selectedNodes[0]) {
262
307
  ids = [];
263
308
  }
309
+
264
310
  dispatch({ type: "select-node", payload: { ids } });
265
311
  },
266
- children: _Node,
267
- idAccessor(d: TreeData) {
268
- return d.id.toString();
312
+ [selectedNodes, dispatch],
313
+ );
314
+
315
+ return h(
316
+ "div.selection-tree-wrapper",
317
+ { onPointerDown: handleClick },
318
+ h(Tree, {
319
+ className: "selection-tree",
320
+ height,
321
+ width,
322
+ ref,
323
+ data: tree,
324
+ onMove({ dragIds, parentId, index }) {
325
+ dispatch({
326
+ type: "move-node",
327
+ payload: {
328
+ dragIds: dragIds.map((d) => parseInt(d)),
329
+ parentId: parentId ? parseInt(parentId) : null,
330
+ index,
331
+ },
332
+ });
333
+ },
334
+ onDelete({ ids }) {
335
+ dispatch({
336
+ type: "delete-node",
337
+ payload: { ids: ids.map((d) => parseInt(d)) },
338
+ });
339
+ },
340
+ onSelect: handleSelect,
341
+ children: _Node,
342
+ idAccessor(d) {
343
+ return d.id.toString();
344
+ },
345
+ }),
346
+ );
347
+ }
348
+
349
+ function TypeList({ types, selected, dispatch, selectedNodes, tree }) {
350
+ const [selectedType, setSelectedType] = useState(null);
351
+ const isSelectedNodes = selectedNodes.length > 0;
352
+ const darkMode = useInDarkMode();
353
+ const luminance = darkMode ? 0.9 : 0.4;
354
+
355
+ return h("div.type-list-container", [
356
+ h(
357
+ "div.type-list-header",
358
+ isSelectedNodes && !selectedType
359
+ ? "Change selected nodes to:"
360
+ : "Entity Types",
361
+ ),
362
+ h(
363
+ "div.type-list",
364
+ Array.from(types.values()).map((type) =>
365
+ h(TypeTag, {
366
+ type,
367
+ luminance,
368
+ selectedType,
369
+ setSelectedType,
370
+ dispatch,
371
+ tree,
372
+ selectedNodes,
373
+ selected,
374
+ isSelectedNodes,
375
+ }),
376
+ ),
377
+ ),
378
+ h(AddType, { dispatch }),
379
+ ]);
380
+ }
381
+
382
+ function collectMatchingIds(tree, id) {
383
+ const ids = [];
384
+
385
+ function traverse(node) {
386
+ if (node.type.id === id) {
387
+ ids.push(node.id);
388
+ }
389
+ if (Array.isArray(node.children)) {
390
+ node.children.forEach(traverse);
391
+ }
392
+ }
393
+
394
+ tree.forEach(traverse);
395
+ return ids;
396
+ }
397
+
398
+ function AddType({ dispatch }) {
399
+ const [overlayOpen, setOverlayOpen] = useState(false);
400
+
401
+ const saveHandler = (payload) => {
402
+ dispatch({
403
+ type: "add-entity-type",
404
+ payload,
405
+ });
406
+ setOverlayOpen(false);
407
+ };
408
+
409
+ return h("div.add-type-container", [
410
+ h("div.add-type", { onClick: () => setOverlayOpen(true) }, [
411
+ h("p.add-type-text", "Add new type"),
412
+ h(Icon, { icon: "plus" }),
413
+ ]),
414
+ h(TypeOverlay, {
415
+ setOverlayOpen,
416
+ overlayOpen,
417
+ title: "Add New Type",
418
+ saveHandler,
419
+ }),
420
+ ]);
421
+ }
422
+
423
+ function EditType({ dispatch, type }) {
424
+ const [editorOpen, setEditorOpen] = useState(false);
425
+
426
+ const saveHandler = (payload) => {
427
+ dispatch({
428
+ type: "update-entity-type",
429
+ payload,
430
+ });
431
+ setEditorOpen(false);
432
+ };
433
+
434
+ return h("div.edit-type", [
435
+ h(Icon, {
436
+ icon: "edit",
437
+ className: "edit-icon",
438
+ onClick: (e) => {
439
+ e.stopPropagation();
440
+ setEditorOpen(true);
441
+ },
442
+ }),
443
+ h(TypeOverlay, {
444
+ setOverlayOpen: setEditorOpen,
445
+ overlayOpen: editorOpen,
446
+ originalType: type,
447
+ title: "Edit Type",
448
+ saveHandler,
449
+ }),
450
+ ]);
451
+ }
452
+
453
+ function TypeOverlay({
454
+ setOverlayOpen,
455
+ overlayOpen,
456
+ originalType,
457
+ title,
458
+ saveHandler,
459
+ }) {
460
+ const { name, description, color, id } = originalType || {};
461
+
462
+ const [nameInput, setNameInput] = useState(name || "");
463
+ const [descriptionInput, setDescriptionInput] = useState(description || "");
464
+ const [colorInput, setColorInput] = useState(color || "#fff");
465
+
466
+ return h(
467
+ Overlay2,
468
+ {
469
+ isOpen: overlayOpen,
470
+ },
471
+ h(
472
+ "div.overlay-container",
473
+ h("div.add-type-overlay", [
474
+ h("h2.title", [
475
+ title,
476
+ h(Icon, {
477
+ icon: "cross",
478
+ className: "close-icon",
479
+ onClick: () => {
480
+ setOverlayOpen(false);
481
+ },
482
+ style: { cursor: "pointer", color: "red" },
483
+ }),
484
+ ]),
485
+ h("div.form-group", [
486
+ h("div.text-inputs", [
487
+ h("div.form-field.name", [
488
+ h("p.label", "Name"),
489
+ h("input", {
490
+ type: "text",
491
+ placeholder: "Enter type name",
492
+ onChange: (e) => setNameInput(e.target.value),
493
+ value: nameInput,
494
+ }),
495
+ ]),
496
+ h("div.form-field.form-description", [
497
+ h("p.label", "Description"),
498
+ h("input", {
499
+ type: "text",
500
+ placeholder: "Enter type description",
501
+ onChange: (e) => setDescriptionInput(e.target.value),
502
+ value: descriptionInput,
503
+ }),
504
+ ]),
505
+ ]),
506
+ h("div.form-field.color", [
507
+ h("p.label", "Color"),
508
+ h(ColorPicker, {
509
+ value: colorInput,
510
+ onChange: (color) => setColorInput(color),
511
+ style: { width: "100%" },
512
+ }),
513
+ ]),
514
+ ]),
515
+ h(
516
+ SaveButton,
517
+ {
518
+ className: "save-btn",
519
+ small: true,
520
+ onClick: () =>
521
+ saveHandler({
522
+ name: nameInput,
523
+ description: descriptionInput,
524
+ color: colorInput,
525
+ id,
526
+ }),
527
+ },
528
+ "Save changes",
529
+ ),
530
+ ]),
531
+ ),
532
+ );
533
+ }
534
+
535
+ function TypeTag({
536
+ type,
537
+ luminance,
538
+ selectedType,
539
+ setSelectedType,
540
+ dispatch,
541
+ tree,
542
+ selectedNodes,
543
+ selected,
544
+ isSelectedNodes,
545
+ }) {
546
+ const { color, name, id, description } = type;
547
+ const chromaColor = asChromaColor(color ?? "#000000");
548
+ const darkMode = useInDarkMode();
549
+
550
+ const payload = {
551
+ id,
552
+ name,
553
+ color,
554
+ description,
555
+ };
556
+
557
+ const ids = collectMatchingIds(tree, id);
558
+
559
+ const handleTagClick = () => {
560
+ if (!isSelectedNodes && selectedType === null) {
561
+ if (ids.length > 0) {
562
+ setSelectedType(type);
563
+ dispatch({ type: "toggle-node-selected", payload: { ids } });
564
+ }
565
+ } else if (isSelectedNodes && selectedType === null) {
566
+ if (id === selected?.id && selectedNodes.length > 0) {
567
+ dispatch({
568
+ type: "toggle-node-selected",
569
+ payload: { ids: selectedNodes },
570
+ });
571
+ } else {
572
+ dispatch({ type: "select-entity-type", payload });
573
+ }
574
+ } else if (isSelectedNodes && selectedType.id === id) {
575
+ setSelectedType(null);
576
+ dispatch({ type: "toggle-node-selected", payload: { ids } });
577
+ } else if (isSelectedNodes && selectedType.id !== id) {
578
+ if (ids.length > 0) {
579
+ setSelectedType(type);
580
+ const oldIds = collectMatchingIds(tree, selectedType.id);
581
+
582
+ dispatch({ type: "toggle-node-selected", payload: { ids: oldIds } });
583
+ dispatch({ type: "toggle-node-selected", payload: { ids } });
584
+ }
585
+ } else {
586
+ console.warn("Unexpected state in TypeTag click handler", {
587
+ isSelectedNodes,
588
+ selectedType,
589
+ selectedNodes,
590
+ ids,
591
+ id,
592
+ selected,
593
+ });
594
+ }
595
+ };
596
+
597
+ return h(
598
+ Popover,
599
+ {
600
+ autoFocus: false,
601
+ content: h("div.description", description || "No description available"),
602
+ interactionKind: "hover",
269
603
  },
270
- });
604
+ h(
605
+ "div.type-tag",
606
+ {
607
+ onClick: handleTagClick,
608
+ style: {
609
+ cursor:
610
+ ids.length > 0 || (isSelectedNodes && !selectedType)
611
+ ? "pointer"
612
+ : "",
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)`,
619
+ },
620
+ },
621
+ h("div.type-container", [
622
+ h("div.type-name", name),
623
+ h("div.icons", [
624
+ h(EditType, {
625
+ dispatch,
626
+ type,
627
+ }),
628
+ h(Icon, {
629
+ icon: "cross",
630
+ className: "delete-type-icon",
631
+ style: { color: "red", cursor: "pointer" },
632
+ onClick: (e) => {
633
+ e.stopPropagation();
634
+ dispatch({
635
+ type: "delete-entity-type",
636
+ payload: { id },
637
+ });
638
+ },
639
+ }),
640
+ ]),
641
+ ]),
642
+ ),
643
+ );
271
644
  }
@@ -47,6 +47,12 @@ function Node({ node, style, dragHandle, tree, matchComponent }: any) {
47
47
 
48
48
  const dispatch = useTreeDispatch();
49
49
 
50
+ // console.log("Node render", node.data, highlighted, active);
51
+
52
+ if (!node.data?.type) {
53
+ node.data.type = { name: "lith", color: "rgb(107, 255, 91)" };
54
+ }
55
+
50
56
  return h(
51
57
  "div.node",
52
58
  { style, ref: dragHandle },
@@ -58,7 +64,7 @@ function Node({ node, style, dragHandle, tree, matchComponent }: any) {
58
64
  onClickType() {
59
65
  dispatch({ type: "toggle-entity-type-selector" });
60
66
  },
61
- })
67
+ }),
62
68
  );
63
69
  }
64
70