@macrostrat/feedback-components 1.0.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 (48) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -0
  3. package/dist/edit-state.e8edb13a.js +251 -0
  4. package/dist/edit-state.e8edb13a.js.map +1 -0
  5. package/dist/extractions.54be85f8.js +177 -0
  6. package/dist/extractions.54be85f8.js.map +1 -0
  7. package/dist/feedback.46c2b5c4.js +252 -0
  8. package/dist/feedback.46c2b5c4.js.map +1 -0
  9. package/dist/feedback.module.7e16830e.css +44 -0
  10. package/dist/feedback.module.7e16830e.css.map +1 -0
  11. package/dist/feedback.module.c28cbac7.js +28 -0
  12. package/dist/feedback.module.c28cbac7.js.map +1 -0
  13. package/dist/graph.cb42b871.js +83 -0
  14. package/dist/graph.cb42b871.js.map +1 -0
  15. package/dist/index.d.ts +145 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +9 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/main.module.21bbfaf4.js +19 -0
  20. package/dist/main.module.21bbfaf4.js.map +1 -0
  21. package/dist/main.module.ca3db294.js +16 -0
  22. package/dist/main.module.ca3db294.js.map +1 -0
  23. package/dist/main.module.d6508c0e.css +14 -0
  24. package/dist/main.module.d6508c0e.css.map +1 -0
  25. package/dist/main.module.f9f92ece.css +17 -0
  26. package/dist/main.module.f9f92ece.css.map +1 -0
  27. package/dist/node.30d0b8c3.js +59 -0
  28. package/dist/node.30d0b8c3.js.map +1 -0
  29. package/dist/text-visualizer.77af0d24.js +101 -0
  30. package/dist/text-visualizer.77af0d24.js.map +1 -0
  31. package/dist/type-selector.e75dd247.js +62 -0
  32. package/dist/type-selector.e75dd247.js.map +1 -0
  33. package/package.json +48 -0
  34. package/src/extractions/index.ts +219 -0
  35. package/src/extractions/main.module.sass +10 -0
  36. package/src/extractions/types.ts +30 -0
  37. package/src/feedback/edit-state.ts +311 -0
  38. package/src/feedback/feedback.module.sass +37 -0
  39. package/src/feedback/graph.ts +98 -0
  40. package/src/feedback/index.ts +271 -0
  41. package/src/feedback/node.ts +65 -0
  42. package/src/feedback/text-visualizer.ts +116 -0
  43. package/src/feedback/type-selector/index.ts +75 -0
  44. package/src/feedback/type-selector/main.module.sass +13 -0
  45. package/src/feedback/types.ts +76 -0
  46. package/src/index.ts +2 -0
  47. package/stories/feedback.stories.ts +40 -0
  48. package/stories/test-data.ts +330 -0
@@ -0,0 +1,16 @@
1
+
2
+ function $parcel$export(e, n, v, s) {
3
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
4
+ }
5
+ var $83cb1ce4a2b71eef$exports = {};
6
+
7
+ $parcel$export($83cb1ce4a2b71eef$exports, "entities", () => $83cb1ce4a2b71eef$export$5345fd892b40e1f6, (v) => $83cb1ce4a2b71eef$export$5345fd892b40e1f6 = v);
8
+ $parcel$export($83cb1ce4a2b71eef$exports, "entity", () => $83cb1ce4a2b71eef$export$4b5127edceda427d, (v) => $83cb1ce4a2b71eef$export$4b5127edceda427d = v);
9
+ var $83cb1ce4a2b71eef$export$5345fd892b40e1f6;
10
+ var $83cb1ce4a2b71eef$export$4b5127edceda427d;
11
+ $83cb1ce4a2b71eef$export$5345fd892b40e1f6 = `UZr1Jq_entities`;
12
+ $83cb1ce4a2b71eef$export$4b5127edceda427d = `UZr1Jq_entity`;
13
+
14
+
15
+ export {$83cb1ce4a2b71eef$exports as default};
16
+ //# sourceMappingURL=main.module.ca3db294.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;;;;;;AAAA,IAAA;AACA,IAAA;AADA,4CAA6B,CAAC,eAAe,CAAC;AAC9C,4CAA2B,CAAC,aAAa,CAAC","sources":["packages/feedback-components/src/extractions/main.module.sass"],"sourcesContent":[".entities\n list-style: none\n padding-left: 0\n\n .entities ul\n list-style: none\n\n.entity\n margin: 0.2em 0 0.5em\n padding-right: 3px\n"],"names":[],"version":3,"file":"main.module.ca3db294.js.map"}
@@ -0,0 +1,14 @@
1
+ .UZr1Jq_entities {
2
+ padding-left: 0;
3
+ list-style: none;
4
+ }
5
+
6
+ .UZr1Jq_entities .UZr1Jq_entities ul {
7
+ list-style: none;
8
+ }
9
+
10
+ .UZr1Jq_entity {
11
+ margin: .2em 0 .5em;
12
+ padding-right: 3px;
13
+ }
14
+ /*# sourceMappingURL=main.module.d6508c0e.css.map */
@@ -0,0 +1 @@
1
+ {"mappings":"AAAA;;;;;AAIE;;;;AAGF","sources":["packages/feedback-components/src/extractions/main.module.sass"],"sourcesContent":[".entities\n list-style: none\n padding-left: 0\n\n .entities ul\n list-style: none\n\n.entity\n margin: 0.2em 0 0.5em\n padding-right: 3px\n"],"names":[],"version":3,"file":"main.module.d6508c0e.css.map"}
@@ -0,0 +1,17 @@
1
+ .WofFTa_item-container {
2
+ align-items: center;
3
+ gap: 1em;
4
+ min-height: 40px;
5
+ display: flex;
6
+ }
7
+
8
+ .WofFTa_item-container.WofFTa_selected {
9
+ background-color: var(--panel-background-color);
10
+ }
11
+
12
+ .WofFTa_swatch {
13
+ border-radius: 4px;
14
+ width: 30px;
15
+ height: 30px;
16
+ }
17
+ /*# sourceMappingURL=main.module.f9f92ece.css.map */
@@ -0,0 +1 @@
1
+ {"mappings":"AAAA;;;;;;;AAME;;;;AAGF","sources":["packages/feedback-components/src/feedback/type-selector/main.module.sass"],"sourcesContent":[".item-container\n min-height: 40px\n display: flex\n align-items: center\n gap: 1em\n\n &.selected\n background-color: var(--panel-background-color)\n\n.swatch\n width: 30px\n height: 30px\n border-radius: 4px\n"],"names":[],"version":3,"file":"main.module.f9f92ece.css.map"}
@@ -0,0 +1,59 @@
1
+ import {EntityTag as $03d8811e9c9b360d$export$117e56c71b172cde} from "./extractions.54be85f8.js";
2
+ import {useTreeDispatch as $b79bf29960412ca7$export$e1068f2d1c68f87e} from "./edit-state.e8edb13a.js";
3
+ import "./feedback.module.7e16830e.css";
4
+ import $6psMB$feedbackmodulec28cbac7js from "./feedback.module.c28cbac7.js";
5
+ import $6psMB$macrostrathyper from "@macrostrat/hyper";
6
+
7
+
8
+ function $parcel$interopDefault(a) {
9
+ return a && a.__esModule ? a.default : a;
10
+ }
11
+
12
+
13
+
14
+
15
+ const $b6b9741bf83336eb$var$h = (0, $6psMB$macrostrathyper).styled((0, ($parcel$interopDefault($6psMB$feedbackmodulec28cbac7js))));
16
+ function $b6b9741bf83336eb$var$isSelected(searchNode, treeNode) {
17
+ return searchNode.id == treeNode.id;
18
+ // We could also select children of the search node here if we wanted to
19
+ }
20
+ function $b6b9741bf83336eb$var$isNodeHighlighted(node, tree) {
21
+ // We treat no selection as all nodes being active. We may add some nuance later
22
+ if (tree.selectedNodes.length == 0) return true;
23
+ for (const selectedNode of tree.selectedNodes){
24
+ if ($b6b9741bf83336eb$var$isSelected(node.data, selectedNode.data)) return true;
25
+ }
26
+ // Check if the parent node is highlighted
27
+ if (node.parent != null && $b6b9741bf83336eb$var$isNodeHighlighted(node.parent, tree)) return true;
28
+ return false;
29
+ }
30
+ function $b6b9741bf83336eb$var$isNodeActive(node, tree) {
31
+ for (const selectedNode of tree.selectedNodes){
32
+ if ($b6b9741bf83336eb$var$isSelected(node.data, selectedNode.data)) return true;
33
+ }
34
+ return false;
35
+ }
36
+ function $b6b9741bf83336eb$var$Node({ node: node, style: style, dragHandle: dragHandle, tree: tree, matchComponent: matchComponent }) {
37
+ let highlighted = $b6b9741bf83336eb$var$isNodeHighlighted(node, tree);
38
+ let active = $b6b9741bf83336eb$var$isNodeActive(node, tree);
39
+ const dispatch = (0, $b79bf29960412ca7$export$e1068f2d1c68f87e)();
40
+ return $b6b9741bf83336eb$var$h("div.node", {
41
+ style: style,
42
+ ref: dragHandle
43
+ }, $b6b9741bf83336eb$var$h((0, $03d8811e9c9b360d$export$117e56c71b172cde), {
44
+ data: node.data,
45
+ active: active,
46
+ highlighted: highlighted,
47
+ matchComponent: matchComponent,
48
+ onClickType () {
49
+ dispatch({
50
+ type: "toggle-entity-type-selector"
51
+ });
52
+ }
53
+ }));
54
+ }
55
+ var $b6b9741bf83336eb$export$2e2bcd8739ae039 = $b6b9741bf83336eb$var$Node;
56
+
57
+
58
+ export {$b6b9741bf83336eb$export$2e2bcd8739ae039 as default};
59
+ //# sourceMappingURL=node.30d0b8c3.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;;;;;;;;;;;;AAOA,MAAM,0BAAI,CAAA,GAAA,sBAAI,EAAE,MAAM,CAAC,CAAA,GAAA,0EAAK;AAE5B,SAAS,iCAAW,UAAoB,EAAE,QAAkB;IAC1D,OAAO,WAAW,EAAE,IAAI,SAAS,EAAE;AACnC,wEAAwE;AAC1E;AAEA,SAAS,wCAAkB,IAAuB,EAAE,IAAuB;IACzE,gFAAgF;IAChF,IAAI,KAAK,aAAa,CAAC,MAAM,IAAI,GAC/B,OAAO;IAGT,KAAK,MAAM,gBAAgB,KAAK,aAAa,CAAE;QAC7C,IAAI,iCAAW,KAAK,IAAI,EAAE,aAAa,IAAI,GACzC,OAAO;IAEX;IAEA,0CAA0C;IAC1C,IAAI,KAAK,MAAM,IAAI,QAAQ,wCAAkB,KAAK,MAAM,EAAE,OACxD,OAAO;IAGT,OAAO;AACT;AAEA,SAAS,mCAAa,IAAuB,EAAE,IAAuB;IACpE,KAAK,MAAM,gBAAgB,KAAK,aAAa,CAAE;QAC7C,IAAI,iCAAW,KAAK,IAAI,EAAE,aAAa,IAAI,GACzC,OAAO;IAEX;IACA,OAAO;AACT;AAEA,SAAS,2BAAK,QAAE,IAAI,SAAE,KAAK,cAAE,UAAU,QAAE,IAAI,kBAAE,cAAc,EAAO;IAClE,IAAI,cAAuB,wCAAkB,MAAM;IACnD,IAAI,SAAkB,mCAAa,MAAM;IAEzC,MAAM,WAAW,CAAA,GAAA,yCAAc;IAE/B,OAAO,wBACL,YACA;eAAE;QAAO,KAAK;IAAW,GACzB,wBAAE,CAAA,GAAA,yCAAQ,GAAG;QACX,MAAM,KAAK,IAAI;gBACf;qBACA;wBACA;QACA;YACE,SAAS;gBAAE,MAAM;YAA8B;QACjD;IACF;AAEJ;IAEA,2CAAe","sources":["packages/feedback-components/src/feedback/node.ts"],"sourcesContent":["import { NodeApi, TreeApi } from \"react-arborist\";\nimport { TreeData } from \"./types\";\nimport { EntityTag } from \"../extractions\";\nimport { useTreeDispatch } from \"./edit-state\";\nimport styles from \"./feedback.module.sass\";\nimport hyper from \"@macrostrat/hyper\";\n\nconst h = hyper.styled(styles);\n\nfunction isSelected(searchNode: TreeData, treeNode: TreeData) {\n return searchNode.id == treeNode.id;\n // We could also select children of the search node here if we wanted to\n}\n\nfunction isNodeHighlighted(node: NodeApi<TreeData>, tree: TreeApi<TreeData>) {\n // We treat no selection as all nodes being active. We may add some nuance later\n if (tree.selectedNodes.length == 0) {\n return true;\n }\n\n for (const selectedNode of tree.selectedNodes) {\n if (isSelected(node.data, selectedNode.data)) {\n return true;\n }\n }\n\n // Check if the parent node is highlighted\n if (node.parent != null && isNodeHighlighted(node.parent, tree)) {\n return true;\n }\n\n return false;\n}\n\nfunction isNodeActive(node: NodeApi<TreeData>, tree: TreeApi<TreeData>) {\n for (const selectedNode of tree.selectedNodes) {\n if (isSelected(node.data, selectedNode.data)) {\n return true;\n }\n }\n return false;\n}\n\nfunction Node({ node, style, dragHandle, tree, matchComponent }: any) {\n let highlighted: boolean = isNodeHighlighted(node, tree);\n let active: boolean = isNodeActive(node, tree);\n\n const dispatch = useTreeDispatch();\n\n return h(\n \"div.node\",\n { style, ref: dragHandle },\n h(EntityTag, {\n data: node.data,\n active,\n highlighted,\n matchComponent,\n onClickType() {\n dispatch({ type: \"toggle-entity-type-selector\" });\n },\n })\n );\n}\n\nexport default Node;\n"],"names":[],"version":3,"file":"node.30d0b8c3.js.map"}
@@ -0,0 +1,101 @@
1
+ import "./feedback.module.7e16830e.css";
2
+ import $85rLh$feedbackmodulec28cbac7js from "./feedback.module.c28cbac7.js";
3
+ import {getTagStyle as $03d8811e9c9b360d$export$35baa338324d8550, buildHighlights as $03d8811e9c9b360d$export$c4b91360064ad200} from "./extractions.54be85f8.js";
4
+ import {TextAnnotateBlend as $85rLh$TextAnnotateBlend} from "react-text-annotate-blend";
5
+ import $85rLh$macrostrathyper from "@macrostrat/hyper";
6
+ import {useCallback as $85rLh$useCallback} from "react";
7
+
8
+
9
+ function $parcel$interopDefault(a) {
10
+ return a && a.__esModule ? a.default : a;
11
+ }
12
+
13
+
14
+
15
+
16
+
17
+ const $156a3efbc315814c$var$h = (0, $85rLh$macrostrathyper).styled((0, ($parcel$interopDefault($85rLh$feedbackmodulec28cbac7js))));
18
+ function $156a3efbc315814c$var$buildTags(highlights, selectedNodes) {
19
+ let tags = [];
20
+ // If entity ID has already been seen, don't add it again
21
+ const entities = new Set();
22
+ for (const highlight of highlights){
23
+ // Don't add multiply-linked entities multiple times
24
+ if (entities.has(highlight.id)) continue;
25
+ const highlighted = $156a3efbc315814c$var$isHighlighted(highlight, selectedNodes);
26
+ const active = $156a3efbc315814c$var$isActive(highlight, selectedNodes);
27
+ tags.push({
28
+ markStyle: {
29
+ ...(0, $03d8811e9c9b360d$export$35baa338324d8550)(highlight.backgroundColor, {
30
+ highlighted: highlighted,
31
+ active: active
32
+ }),
33
+ borderRadius: "0.2em",
34
+ padding: "0.1em",
35
+ borderWidth: "1.5px",
36
+ cursor: "pointer"
37
+ },
38
+ tagStyle: {
39
+ display: "none"
40
+ },
41
+ ...highlight
42
+ });
43
+ entities.add(highlight.id);
44
+ }
45
+ return tags;
46
+ }
47
+ function $156a3efbc315814c$var$isActive(tag, selectedNodes) {
48
+ return selectedNodes.includes(tag.id);
49
+ }
50
+ function $156a3efbc315814c$var$isHighlighted(tag, selectedNodes) {
51
+ if (selectedNodes.length === 0) return true;
52
+ return (selectedNodes.includes(tag.id) || tag.parents?.some((d)=>selectedNodes.includes(d))) ?? false;
53
+ }
54
+ function $156a3efbc315814c$export$6e107db9091b8219(props) {
55
+ // Convert input to tags
56
+ const { text: text, selectedNodes: selectedNodes, nodes: nodes, dispatch: dispatch } = props;
57
+ let allTags = $156a3efbc315814c$var$buildTags((0, $03d8811e9c9b360d$export$c4b91360064ad200)(nodes, null), selectedNodes);
58
+ const onChange = (0, $85rLh$useCallback)((tags)=>{
59
+ // New tags
60
+ console.log(tags);
61
+ const newTags = tags.filter((d)=>!("id" in d));
62
+ if (newTags.length > 0) {
63
+ const { start: start, end: end } = newTags[0];
64
+ const payload = {
65
+ start: start,
66
+ end: end,
67
+ text: text.slice(start, end)
68
+ };
69
+ dispatch({
70
+ type: "create-node",
71
+ payload: payload
72
+ });
73
+ return;
74
+ }
75
+ const tagIDs = new Set(tags.map((d)=>d.id));
76
+ const removedIds = allTags.map((d)=>d.id).filter((d)=>!tagIDs.has(d));
77
+ /* Find the id that was removed: that is the one that will be selected
78
+ (we are hijacking the 'click to delete' functionality to select instead) */ if (removedIds.length > 0) dispatch({
79
+ type: "toggle-node-selected",
80
+ payload: {
81
+ ids: removedIds
82
+ }
83
+ });
84
+ }, [
85
+ allTags,
86
+ text
87
+ ]);
88
+ return $156a3efbc315814c$var$h((0, $85rLh$TextAnnotateBlend), {
89
+ style: {
90
+ fontSize: "1.2em"
91
+ },
92
+ className: "feedback-text",
93
+ content: text,
94
+ onChange: onChange,
95
+ value: allTags
96
+ });
97
+ }
98
+
99
+
100
+ export {$156a3efbc315814c$export$6e107db9091b8219 as FeedbackText};
101
+ //# sourceMappingURL=text-visualizer.77af0d24.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;;;;;;;;;;;;;;AASA,MAAM,0BAAI,CAAA,GAAA,sBAAI,EAAE,MAAM,CAAC,CAAA,GAAA,0EAAK;AAU5B,SAAS,gCACP,UAAuB,EACvB,aAAuB;IAEvB,IAAI,OAA2B,EAAE;IAEjC,yDAAyD;IACzD,MAAM,WAAW,IAAI;IAErB,KAAK,MAAM,aAAa,WAAY;QAClC,oDAAoD;QACpD,IAAI,SAAS,GAAG,CAAC,UAAU,EAAE,GAAG;QAEhC,MAAM,cAAc,oCAAc,WAAW;QAC7C,MAAM,SAAS,+BAAS,WAAW;QAEnC,KAAK,IAAI,CAAC;YACR,WAAW;gBACT,GAAG,CAAA,GAAA,yCAAU,EAAE,UAAU,eAAe,EAAE;iCACxC;4BACA;gBACF,EAAE;gBACF,cAAc;gBACd,SAAS;gBACT,aAAa;gBACb,QAAQ;YACV;YACA,UAAU;gBACR,SAAS;YACX;YACA,GAAG,SAAS;QACd;QAEA,SAAS,GAAG,CAAC,UAAU,EAAE;IAC3B;IAEA,OAAO;AACT;AAEA,SAAS,+BAAS,GAAc,EAAE,aAAuB;IACvD,OAAO,cAAc,QAAQ,CAAC,IAAI,EAAE;AACtC;AAEA,SAAS,oCAAc,GAAc,EAAE,aAAuB;IAC5D,IAAI,cAAc,MAAM,KAAK,GAAG,OAAO;IACvC,OACE,AAAC,CAAA,cAAc,QAAQ,CAAC,IAAI,EAAE,KAC5B,IAAI,OAAO,EAAE,KAAK,CAAC,IAAM,cAAc,QAAQ,CAAC,GAAE,KACpD;AAEJ;AAEO,SAAS,0CAAa,KAAwB;IACnD,wBAAwB;IACxB,MAAM,QAAE,IAAI,iBAAE,aAAa,SAAE,KAAK,YAAE,QAAQ,EAAE,GAAG;IACjD,IAAI,UAA8B,gCAChC,CAAA,GAAA,yCAAc,EAAE,OAAO,OACvB;IAGF,MAAM,WAAW,CAAA,GAAA,kBAAU,EACzB,CAAC;QACC,WAAW;QACX,QAAQ,GAAG,CAAC;QACZ,MAAM,UAAU,KAAK,MAAM,CAAC,CAAC,IAAM,CAAE,CAAA,QAAQ,CAAA;QAC7C,IAAI,QAAQ,MAAM,GAAG,GAAG;YACtB,MAAM,SAAE,KAAK,OAAE,GAAG,EAAE,GAAG,OAAO,CAAC,EAAE;YACjC,MAAM,UAAU;uBAAE;qBAAO;gBAAK,MAAM,KAAK,KAAK,CAAC,OAAO;YAAK;YAC3D,SAAS;gBAAE,MAAM;yBAAe;YAAQ;YACxC;QACF;QAEA,MAAM,SAAS,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,IAAM,EAAE,EAAE;QAC3C,MAAM,aAAa,QAAQ,GAAG,CAAC,CAAC,IAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,IAAM,CAAC,OAAO,GAAG,CAAC;QAEtE;gFAC0E,GAC1E,IAAI,WAAW,MAAM,GAAG,GACtB,SAAS;YACP,MAAM;YACN,SAAS;gBAAE,KAAK;YAAW;QAC7B;IAEJ,GACA;QAAC;QAAS;KAAK;IAGjB,OAAO,wBAAE,CAAA,GAAA,wBAAgB,GAAG;QAC1B,OAAO;YACL,UAAU;QACZ;QACA,WAAW;QACX,SAAS;kBACT;QACA,OAAO;IACT;AACF","sources":["packages/feedback-components/src/feedback/text-visualizer.ts"],"sourcesContent":["import { AnnotateBlendTag, TextAnnotateBlend } from \"react-text-annotate-blend\";\nimport { InternalEntity } from \"./types\";\nimport { TreeDispatch } from \"./edit-state\";\nimport styles from \"./feedback.module.sass\";\nimport hyper from \"@macrostrat/hyper\";\nimport { buildHighlights, getTagStyle } from \"../extractions\";\nimport { Highlight } from \"../extractions/types\";\nimport { useCallback } from \"react\";\n\nconst h = hyper.styled(styles);\n\nexport interface FeedbackTextProps {\n text: string;\n selectedNodes: number[];\n nodes: InternalEntity[];\n updateNodes: (nodes: string[]) => void;\n dispatch: TreeDispatch;\n}\n\nfunction buildTags(\n highlights: Highlight[],\n selectedNodes: number[]\n): AnnotateBlendTag[] {\n let tags: AnnotateBlendTag[] = [];\n\n // If entity ID has already been seen, don't add it again\n const entities = new Set<number>();\n\n for (const highlight of highlights) {\n // Don't add multiply-linked entities multiple times\n if (entities.has(highlight.id)) continue;\n\n const highlighted = isHighlighted(highlight, selectedNodes);\n const active = isActive(highlight, selectedNodes);\n\n tags.push({\n markStyle: {\n ...getTagStyle(highlight.backgroundColor, {\n highlighted,\n active,\n }),\n borderRadius: \"0.2em\",\n padding: \"0.1em\",\n borderWidth: \"1.5px\",\n cursor: \"pointer\",\n },\n tagStyle: {\n display: \"none\",\n },\n ...highlight,\n });\n\n entities.add(highlight.id);\n }\n\n return tags;\n}\n\nfunction isActive(tag: Highlight, selectedNodes: number[]) {\n return selectedNodes.includes(tag.id);\n}\n\nfunction isHighlighted(tag: Highlight, selectedNodes: number[]) {\n if (selectedNodes.length === 0) return true;\n return (\n (selectedNodes.includes(tag.id) ||\n tag.parents?.some((d) => selectedNodes.includes(d))) ??\n false\n );\n}\n\nexport function FeedbackText(props: FeedbackTextProps) {\n // Convert input to tags\n const { text, selectedNodes, nodes, dispatch } = props;\n let allTags: AnnotateBlendTag[] = buildTags(\n buildHighlights(nodes, null),\n selectedNodes\n );\n\n const onChange = useCallback(\n (tags) => {\n // New tags\n console.log(tags);\n const newTags = tags.filter((d) => !(\"id\" in d));\n if (newTags.length > 0) {\n const { start, end } = newTags[0];\n const payload = { start, end, text: text.slice(start, end) };\n dispatch({ type: \"create-node\", payload });\n return;\n }\n\n const tagIDs = new Set(tags.map((d) => d.id));\n const removedIds = allTags.map((d) => d.id).filter((d) => !tagIDs.has(d));\n\n /* Find the id that was removed: that is the one that will be selected\n (we are hijacking the 'click to delete' functionality to select instead) */\n if (removedIds.length > 0) {\n dispatch({\n type: \"toggle-node-selected\",\n payload: { ids: removedIds },\n });\n }\n },\n [allTags, text]\n );\n\n return h(TextAnnotateBlend, {\n style: {\n fontSize: \"1.2em\",\n },\n className: \"feedback-text\",\n content: text,\n onChange,\n value: allTags,\n });\n}\n"],"names":[],"version":3,"file":"text-visualizer.77af0d24.js.map"}
@@ -0,0 +1,62 @@
1
+ import "./main.module.f9f92ece.css";
2
+ import $4V3RG$mainmodule21bbfaf4js from "./main.module.21bbfaf4.js";
3
+ import $4V3RG$macrostrathyper from "@macrostrat/hyper";
4
+ import $4V3RG$classnames from "classnames";
5
+ import {Omnibar as $4V3RG$Omnibar} from "@blueprintjs/select";
6
+ import "@blueprintjs/select/lib/css/blueprint-select.css";
7
+
8
+
9
+ function $parcel$interopDefault(a) {
10
+ return a && a.__esModule ? a.default : a;
11
+ }
12
+ /**
13
+ * Entity type selector
14
+ */
15
+
16
+
17
+
18
+
19
+ const $fda9ef5406c1cfb4$var$h = (0, $4V3RG$macrostrathyper).styled((0, ($parcel$interopDefault($4V3RG$mainmodule21bbfaf4js))));
20
+ const $fda9ef5406c1cfb4$var$TagListItem = (props)=>{
21
+ /** Render a tag for the omnibox list */ let { active: active, selected: selected, className: className, onSelect: onSelect, item: item, children: children } = props;
22
+ className = (0, $4V3RG$classnames)({
23
+ active: active,
24
+ selected: selected
25
+ }, className);
26
+ const onClick = ()=>onSelect(item);
27
+ return $fda9ef5406c1cfb4$var$h("div.item-container", {
28
+ key: item.id,
29
+ className: className,
30
+ onClick: onClick
31
+ }, [
32
+ $fda9ef5406c1cfb4$var$h("div.swatch", {
33
+ style: {
34
+ backgroundColor: item.color
35
+ }
36
+ }),
37
+ $fda9ef5406c1cfb4$var$h("div.item", {}, item.name)
38
+ ]);
39
+ };
40
+ function $fda9ef5406c1cfb4$export$d8660660a589068c(props) {
41
+ /** A general omnibox for annotation types */ const { onSelectItem: onSelectItem, items: items, isOpen: isOpen, onClose: onClose } = props;
42
+ return $fda9ef5406c1cfb4$var$h((0, $4V3RG$Omnibar), {
43
+ onItemSelect: onSelectItem,
44
+ items: items,
45
+ resetOnSelect: false,
46
+ isOpen: isOpen,
47
+ onClose: onClose,
48
+ itemRenderer (item, { handleClick: handleClick, modifiers: modifiers }) {
49
+ return $fda9ef5406c1cfb4$var$h($fda9ef5406c1cfb4$var$TagListItem, {
50
+ key: item.id,
51
+ item: item,
52
+ onSelect: handleClick,
53
+ active: modifiers.active,
54
+ selected: modifiers.active
55
+ });
56
+ }
57
+ });
58
+ }
59
+
60
+
61
+ export {$fda9ef5406c1cfb4$export$d8660660a589068c as OmniboxSelector};
62
+ //# sourceMappingURL=type-selector.e75dd247.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;;;;;;;;;AAAA;;CAEC;;;;;AAUD,MAAM,0BAAI,CAAA,GAAA,sBAAI,EAAE,MAAM,CAAC,CAAA,GAAA,sEAAK;AAa5B,MAAM,oCAAoD,CAAC;IACzD,sCAAsC,GACtC,IAAI,UAAE,MAAM,YAAE,QAAQ,aAAE,SAAS,YAAE,QAAQ,QAAE,IAAI,YAAE,QAAQ,EAAE,GAAG;IAChE,YAAY,CAAA,GAAA,iBAAS,EAAE;gBAAE;kBAAQ;IAAS,GAAG;IAC7C,MAAM,UAAU,IAAM,SAAS;IAE/B,OAAO,wBACL,sBACA;QACE,KAAK,KAAK,EAAE;mBACZ;iBACA;IACF,GACA;QACE,wBAAE,cAAc;YAAE,OAAO;gBAAE,iBAAiB,KAAK,KAAK;YAAC;QAAE;QACzD,wBAAE,YAAY,CAAC,GAAG,KAAK,IAAI;KAC5B;AAEL;AAWO,SAAS,0CAAmB,KAAsB;IACvD,2CAA2C,GAC3C,MAAM,gBAAE,YAAY,SAAE,KAAK,UAAE,MAAM,WAAE,OAAO,EAAE,GAAG;IAEjD,OAAO,wBAAE,CAAA,GAAA,cAAM,GAAG;QAChB,cAAc;eACd;QACA,eAAe;gBACf;iBACA;QACA,cAAa,IAAO,EAAE,eAAE,WAAW,aAAE,SAAS,EAAE;YAC9C,OAAO,wBAAE,mCAAa;gBACpB,KAAK,KAAK,EAAE;sBACZ;gBACA,UAAU;gBACV,QAAQ,UAAU,MAAM;gBACxB,UAAU,UAAU,MAAM;YAC5B;QACF;IACF;AACF","sources":["packages/feedback-components/src/feedback/type-selector/index.ts"],"sourcesContent":["/**\n * Entity type selector\n */\n\nimport styles from \"./main.module.sass\";\nimport hyper from \"@macrostrat/hyper\";\n\nimport classNames from \"classnames\";\nimport React from \"react\";\nimport { Omnibar, OmnibarProps } from \"@blueprintjs/select\";\nimport \"@blueprintjs/select/lib/css/blueprint-select.css\";\n\nconst h = hyper.styled(styles);\n\ninterface TagItemProps<T> {\n selected: boolean;\n active: boolean;\n className?: string;\n item: T;\n\n onSelect(t: T): void;\n\n children?: React.ReactElement;\n}\n\nconst TagListItem: React.ComponentType<TagItemProps<T>> = (props) => {\n /** Render a tag for the omnibox list */\n let { active, selected, className, onSelect, item, children } = props;\n className = classNames({ active, selected }, className);\n const onClick = () => onSelect(item);\n\n return h(\n \"div.item-container\",\n {\n key: item.id,\n className,\n onClick,\n },\n [\n h(\"div.swatch\", { style: { backgroundColor: item.color } }),\n h(\"div.item\", {}, item.name),\n ]\n );\n};\n\ntype BoxLifecycleProps<T> = Pick<OmnibarProps<T>, \"onClose\" | \"isOpen\">;\n\ninterface OmniboxProps<T> extends BoxLifecycleProps<T> {\n items: T[];\n selectedItem: T;\n onSelectItem: (t: T) => void;\n listItemComponent?: React.ComponentType<TagItemProps<T>>;\n}\n\nexport function OmniboxSelector<T>(props: OmniboxProps<T>) {\n /** A general omnibox for annotation types */\n const { onSelectItem, items, isOpen, onClose } = props;\n\n return h(Omnibar, {\n onItemSelect: onSelectItem,\n items,\n resetOnSelect: false,\n isOpen,\n onClose,\n itemRenderer(item: T, { handleClick, modifiers }) {\n return h(TagListItem, {\n key: item.id,\n item,\n onSelect: handleClick,\n active: modifiers.active,\n selected: modifiers.active,\n });\n },\n });\n}\n"],"names":[],"version":3,"file":"type-selector.e75dd247.js.map"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@macrostrat/feedback-components",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "source": "src/index.ts",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "scripts": {
10
+ "build": "rm -rf dist && parcel build"
11
+ },
12
+ "author": "Daven Quinn",
13
+ "license": "ISC",
14
+ "dependencies": {
15
+ "@babel/preset-react": "^7.18.6",
16
+ "@blueprintjs/core": "^5.10.2",
17
+ "@blueprintjs/select": "^5.3.10",
18
+ "@macrostrat/color-utils": "^1.0.1",
19
+ "@macrostrat/hyper": "^3.0.6",
20
+ "@macrostrat/ui-components": "^4.1.0",
21
+ "@xyflow/react": "^12.3.6",
22
+ "classnames": "^2.5.1",
23
+ "d3-force": "^3.0.0",
24
+ "immutability-helper": "^3.1.1",
25
+ "react-arborist": "^3.4.0",
26
+ "react-text-annotate-blend": "^1.2.0",
27
+ "use-element-dimensions": "^2.1.3"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "source": "./src/index.ts",
32
+ "import": "./dist/index.js",
33
+ "types": "./dist/index.d.ts"
34
+ }
35
+ },
36
+ "peerDependencies": {
37
+ "react": "^17.0.2||^18",
38
+ "react-dom": "^17.0.2||^18"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/UW-Macrostrat/web-components.git",
43
+ "directory": "packages/feedback-components"
44
+ },
45
+ "devDependencies": {
46
+ "parcel": "^2.13.3"
47
+ }
48
+ }
@@ -0,0 +1,219 @@
1
+ import styles from "./main.module.sass";
2
+ import classNames from "classnames";
3
+ import { Tag } from "@blueprintjs/core";
4
+ import type { Entity, EntityExt, Highlight, EntityType } from "./types";
5
+ import { CSSProperties } from "react";
6
+ import { asChromaColor } from "@macrostrat/color-utils";
7
+ import hyper from "@macrostrat/hyper";
8
+
9
+ export type { Entity, EntityExt };
10
+
11
+ const h = hyper.styled(styles);
12
+
13
+ export function buildHighlights(
14
+ entities: EntityExt[],
15
+ parent: EntityExt | null
16
+ ): Highlight[] {
17
+ let highlights = [];
18
+ let parents = [];
19
+ if (parent != null) {
20
+ parents = [parent.id, ...(parent.parents ?? [])];
21
+ }
22
+
23
+ for (const entity of entities) {
24
+ highlights.push({
25
+ start: entity.indices[0],
26
+ end: entity.indices[1],
27
+ text: entity.name,
28
+ backgroundColor: entity.type.color ?? "#ddd",
29
+ tag: entity.type.name,
30
+ id: entity.id,
31
+ parents,
32
+ });
33
+ highlights.push(...buildHighlights(entity.children ?? [], entity));
34
+ }
35
+ return highlights;
36
+ }
37
+
38
+ export function enhanceData(extractionData, models, entityTypes) {
39
+ return {
40
+ ...extractionData,
41
+ model: models.get(extractionData.model_id),
42
+ entities: extractionData.entities?.map((d) =>
43
+ enhanceEntity(d, entityTypes)
44
+ ),
45
+ };
46
+ }
47
+
48
+ export function getTagStyle(
49
+ baseColor: string,
50
+ options: { highlighted?: boolean; inDarkMode?: boolean; active?: boolean }
51
+ ): CSSProperties {
52
+ const _baseColor = asChromaColor(baseColor ?? "#ddd");
53
+ const { highlighted = true, inDarkMode = false, active = false } = options;
54
+
55
+ let mixAmount = highlighted ? 0.8 : 0.5;
56
+ let backgroundAlpha = highlighted ? 0.8 : 0.2;
57
+
58
+ if (active) {
59
+ mixAmount = 1;
60
+ backgroundAlpha = 1;
61
+ }
62
+
63
+ const mixTarget = inDarkMode ? "white" : "black";
64
+
65
+ const color = _baseColor.mix(mixTarget, mixAmount).css();
66
+ const borderColor = highlighted
67
+ ? _baseColor.mix(mixTarget, mixAmount / 2).css()
68
+ : "transparent";
69
+
70
+ return {
71
+ color,
72
+ backgroundColor: _baseColor.alpha(backgroundAlpha).css(),
73
+ boxSizing: "border-box",
74
+ borderStyle: "solid",
75
+ borderColor,
76
+ borderWidth: "1px",
77
+ fontWeight: active ? "bold" : "normal",
78
+ };
79
+ }
80
+
81
+ function enhanceEntity(
82
+ entity: Entity,
83
+ entityTypes: Map<number, EntityType>
84
+ ): EntityExt {
85
+ return {
86
+ ...entity,
87
+ type: addColor(entityTypes.get(entity.type), entity.match != null),
88
+ children: entity.children?.map((d) => enhanceEntity(d, entityTypes)),
89
+ };
90
+ }
91
+
92
+ function addColor(entityType: EntityType, match = false) {
93
+ const color = asChromaColor(entityType.color ?? "#ddd").brighten(
94
+ match ? 1 : 2
95
+ );
96
+
97
+ return { ...entityType, color: color.css() };
98
+ }
99
+
100
+ export function ExtractionContext({
101
+ data,
102
+ entityTypes,
103
+ matchComponent,
104
+ }: {
105
+ data: any;
106
+ entityTypes: Map<number, EntityType>;
107
+ matchComponent: MatchComponent;
108
+ }) {
109
+ const highlights = buildHighlights(data.entities, null);
110
+
111
+ return h("div", [
112
+ h("p", h(HighlightedText, { text: data.paragraph_text, highlights })),
113
+ h(ModelInfo, { data: data.model }),
114
+ h(
115
+ "ul.entities",
116
+ data.entities.map((d) => h(ExtractionInfo, { data: d, matchComponent }))
117
+ ),
118
+ ]);
119
+ }
120
+
121
+ export function ModelInfo({ data }) {
122
+ return h("p.model-name", ["Model: ", h("code.bp5-code", data.name)]);
123
+ }
124
+
125
+ export type MatchComponent = (props: { data: any }) => any;
126
+
127
+ type EntityTagProps = {
128
+ data: EntityExt;
129
+ highlighted?: boolean;
130
+ active?: boolean;
131
+ onClickType?: (type: EntityType) => void;
132
+ matchComponent?: MatchComponent;
133
+ };
134
+
135
+ export function EntityTag({
136
+ data,
137
+ highlighted = true,
138
+ active = false,
139
+ onClickType,
140
+ matchComponent = null,
141
+ }: EntityTagProps) {
142
+ const { name, type, match } = data;
143
+ const className = classNames(
144
+ {
145
+ matched: match != null,
146
+ type: data.type.name,
147
+ },
148
+ "entity"
149
+ );
150
+
151
+ const style = getTagStyle(type.color ?? "#aaaaaa", { highlighted, active });
152
+
153
+ let _matchLink = null;
154
+ if (match != null && matchComponent != null) {
155
+ _matchLink = h(matchComponent, { data: match });
156
+ }
157
+
158
+ return h(Tag, { style, className }, [
159
+ h("span.entity-name", name),
160
+ " ",
161
+ h(
162
+ "code.entity-type.bp5-code",
163
+ {
164
+ onClick(evt) {
165
+ if (active && onClickType != null) {
166
+ onClickType(type);
167
+ evt.stopPropagation();
168
+ }
169
+ },
170
+ },
171
+ [type.name, _matchLink]
172
+ ),
173
+ ]);
174
+ }
175
+
176
+ function ExtractionInfo({
177
+ data,
178
+ matchComponent = null,
179
+ }: {
180
+ data: EntityExt;
181
+ matchComponent: MatchComponent;
182
+ }) {
183
+ const children = data.children ?? [];
184
+
185
+ return h("li.entity-row", [
186
+ h(EntityTag, { data, matchComponent }),
187
+ h.if(children.length > 0)([
188
+ h(
189
+ "ul.children",
190
+ children.map((d) => h(ExtractionInfo, { data: d, matchComponent }))
191
+ ),
192
+ ]),
193
+ ]);
194
+ }
195
+
196
+ function HighlightedText(props: { text: string; highlights: Highlight[] }) {
197
+ const { text, highlights = [] } = props;
198
+ const parts = [];
199
+ let start = 0;
200
+
201
+ const sortedHighlights = highlights.sort((a, b) => a.start - b.start);
202
+ const deconflictedHighlights = sortedHighlights.map((highlight, i) => {
203
+ if (i === 0) return highlight;
204
+ const prev = sortedHighlights[i - 1];
205
+ if (highlight.start < prev.end) {
206
+ highlight.start = prev.end;
207
+ }
208
+ return highlight;
209
+ });
210
+
211
+ for (const highlight of deconflictedHighlights) {
212
+ const { start: s, end, ...rest } = highlight;
213
+ parts.push(text.slice(start, s));
214
+ parts.push(h("span.highlight", { style: rest }, text.slice(s, end)));
215
+ start = end;
216
+ }
217
+ parts.push(text.slice(start));
218
+ return h("span", parts);
219
+ }
@@ -0,0 +1,10 @@
1
+ .entities
2
+ list-style: none
3
+ padding-left: 0
4
+
5
+ .entities ul
6
+ list-style: none
7
+
8
+ .entity
9
+ margin: 0.2em 0 0.5em
10
+ padding-right: 3px
@@ -0,0 +1,30 @@
1
+ type EntityType = { name: string; color: string; id: number };
2
+ type Match = any;
3
+
4
+ export interface Entity {
5
+ id: number;
6
+ name: string;
7
+ type?: number;
8
+ indices: [number, number];
9
+ children: Entity[];
10
+ match?: Match;
11
+ }
12
+
13
+ export { EntityType };
14
+
15
+ export type Highlight = {
16
+ start: number;
17
+ end: number;
18
+ tag?: string;
19
+ text?: string;
20
+ backgroundColor?: string;
21
+ borderColor?: string;
22
+ id: number;
23
+ parents?: number[];
24
+ };
25
+
26
+ export interface EntityExt extends Omit<Entity, "type" | "children"> {
27
+ type: EntityType;
28
+ children: EntityExt[];
29
+ parents?: number[];
30
+ }