Skip to content

Commit e1ddf61

Browse files
committed
relationship annotator checkpoint
1 parent cae6db2 commit e1ddf61

File tree

7 files changed

+254
-48
lines changed

7 files changed

+254
-48
lines changed

src/components/Document/index.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,16 @@ export default function Document({
6868
sequence,
6969
relationships,
7070
onHighlightedChanged = () => null,
71+
onLastPairClickedChanged = () => null,
7172
onSequenceChange = () => null,
7273
onRelationshipsChange = () => null,
7374
nothingHighlighted = false,
75+
createRelationshipsMode = false,
7476
colorLabelMap = {}
7577
}: Props) {
7678
const sequenceItemPositionsRef = useRef({})
7779
const [mouseDown, changeMouseDown] = useState()
78-
const [timeoutCalled, cancelTimeout, resetTimeout] = useTimeout(100) // Force rerender after mounting
80+
const [timeoutCalled, cancelTimeout, resetTimeout] = useTimeout(10) // Force rerender after mounting
7981
const windowSize = useWindowSize()
8082
useEffect(() => {
8183
resetTimeout()
@@ -84,7 +86,11 @@ export default function Document({
8486
[firstSelected, lastSelected],
8587
changeHighlightedRangeState
8688
] = useState([null, null])
89+
8790
const changeHighlightedRange = ([first, last]) => {
91+
if (first !== firstSelected && first !== null && firstSelected !== null) {
92+
onLastPairClickedChanged([firstSelected, first])
93+
}
8894
changeHighlightedRangeState([first, last])
8995
const highlightedItems = []
9096
for (let i = Math.min(first, last); i <= Math.max(first, last); i++)
@@ -122,24 +128,30 @@ export default function Document({
122128
}
123129
}}
124130
relationshipsOn={Boolean(relationships)}
131+
onClick={e => e.stopPropagation()}
125132
onMouseDown={() => {
126-
if (seq.label) return
133+
if (seq.label && !createRelationshipsMode) return
127134
changeHighlightedRange([i, i])
128135
}}
129136
onMouseMove={() => {
130-
if (seq.label) return
131-
if (mouseDown && i !== lastSelected) {
132-
changeHighlightedRange([
133-
firstSelected === null ? i : firstSelected,
134-
i
135-
])
137+
if (!mouseDown) return
138+
if (!createRelationshipsMode) {
139+
if (seq.label) return
140+
if (i !== lastSelected) {
141+
changeHighlightedRange([
142+
firstSelected === null ? i : firstSelected,
143+
i
144+
])
145+
}
136146
}
137147
}}
138148
className={seq.label ? "label" : "unlabeled"}
139149
color={
140150
seq.label
141151
? seq.color || colorLabelMap[seq.label] || "#333"
142-
: seq.text !== " " && highlightedItems.includes(i)
152+
: !createRelationshipsMode &&
153+
seq.text !== " " &&
154+
highlightedItems.includes(i)
143155
? "#ccc"
144156
: "inherit"
145157
}
@@ -154,7 +166,8 @@ export default function Document({
154166
)}
155167
{seq.label && (
156168
<LabeledText
157-
onClick={() => {
169+
onClick={e => {
170+
e.stopPropagation()
158171
onSequenceChange(
159172
sequence
160173
.flatMap(s => (s !== seq ? s : stringToSequence(s.text)))

src/components/Document/index.story.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ storiesOf("Document", module)
3131
))
3232
.add("Relationships", () => (
3333
<Document
34+
createRelationshipsMode
3435
onSequenceChange={action("onSequenceChange")}
3536
onRelationshipsChange={action("onRelationshipsChange")}
3637
onHighlightedChanged={action("onHighlightedChanged")}

src/components/RelationshipAnnotator/index.js

Lines changed: 87 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,38 @@
22

33
import React, { useState, useMemo } from "react"
44

5-
import type { SequenceAnnotatorProps } from "../../types"
5+
import type { RelationshipAnnotatorProps } from "../../types"
66
import Document from "../Document"
77
import LabelSelector from "../LabelSelector"
88
import stringToSequence from "../../string-to-sequence.js"
99
import mergeSequence from "../../merge-sequence.js"
1010
import colors from "../../colors"
1111

12-
export default function RelationshipAnnotator(props: SequenceAnnotatorProps) {
12+
const withId = entity =>
13+
entity.textId
14+
? entity
15+
: {
16+
...entity,
17+
textId: Math.random()
18+
.toString(36)
19+
.slice(-8)
20+
}
21+
22+
export default function RelationshipAnnotator(
23+
props: RelationshipAnnotatorProps
24+
) {
1325
const [highlightedItems, changeHighlightedItems] = useState([])
26+
const [relationships, setRelationships] = useState(props.relationships || [])
27+
const [activePair, setActivePair] = useState(null)
28+
const [creatingRelationships, setCreatingRelationships] = useState(true)
1429
const [sequence, changeSequence] = useState(() =>
1530
props.initialSequence
1631
? props.initialSequence.flatMap(entity =>
1732
entity.label
18-
? [entity]
19-
: stringToSequence(entity.text, props.separatorRegex)
33+
? [withId(entity)]
34+
: stringToSequence(entity.text, props.separatorRegex).map(withId)
2035
)
21-
: stringToSequence(props.document)
36+
: stringToSequence(props.document).map(withId)
2237
)
2338
const colorLabelMap = useMemo(
2439
() =>
@@ -30,57 +45,96 @@ export default function RelationshipAnnotator(props: SequenceAnnotatorProps) {
3045
)
3146

3247
return (
33-
<div>
48+
<div
49+
onMouseMove={e => {
50+
lastMousePosition.current = { x: e.target.clientX, y: e.target.clientX }
51+
}}
52+
>
3453
<div>
3554
<LabelSelector
3655
hotkeysEnabled={props.hotkeysEnabled}
3756
labels={props.labels}
3857
onSelectLabel={(label: string) => {
39-
const { color } = props.labels.find(({ id }) => label === id) || {}
40-
let buildText = ""
41-
const newSequence = []
42-
for (let itemIndex = 0; itemIndex < sequence.length; itemIndex++) {
43-
const item = sequence[itemIndex]
44-
if (!highlightedItems.includes(itemIndex) || item.label) {
45-
if (buildText.length > 0) {
46-
newSequence.push({
47-
text: buildText,
48-
color,
49-
label
50-
})
51-
buildText = ""
58+
if (!creatingRelationships) {
59+
const { color } =
60+
props.labels.find(({ id }) => label === id) || {}
61+
let buildText = ""
62+
const newSequence = []
63+
for (
64+
let itemIndex = 0;
65+
itemIndex < sequence.length;
66+
itemIndex++
67+
) {
68+
const item = sequence[itemIndex]
69+
if (!highlightedItems.includes(itemIndex) || item.label) {
70+
if (buildText.length > 0) {
71+
newSequence.push({
72+
text: buildText,
73+
color,
74+
label
75+
})
76+
buildText = ""
77+
}
78+
newSequence.push(item)
79+
} else {
80+
buildText += item.text
5281
}
53-
newSequence.push(item)
54-
} else {
55-
buildText += item.text
5682
}
57-
}
58-
if (buildText.length > 0) {
59-
newSequence.push({
60-
text: buildText,
61-
color,
62-
label
83+
if (buildText.length > 0) {
84+
newSequence.push({
85+
text: buildText,
86+
color,
87+
label
88+
})
89+
}
90+
91+
changeSequence(newSequence)
92+
props.onChange({
93+
sequence: mergeSequence(newSequence),
94+
relationships
95+
})
96+
changeHighlightedItems([])
97+
} else {
98+
setActivePair(null)
99+
const newRelationships = relationships.concat([
100+
{ ...activePair, label: label, color: colorLabelMap[label] }
101+
])
102+
setRelationships(newRelationships)
103+
props.onChange({
104+
sequence,
105+
relationships: newRelationships
63106
})
64107
}
65-
66-
changeSequence(newSequence)
67-
props.onChange(mergeSequence(newSequence))
68-
changeHighlightedItems([])
69108
}}
70109
/>
71110
</div>
72111
<div style={{ borderTop: "1px solid #ccc", marginTop: 8, paddingTop: 5 }}>
73112
<Document
74113
colorLabelMap={colorLabelMap}
75114
nothingHighlighted={highlightedItems.length === 0}
115+
onLastPairClickedChanged={([first, second]) => {
116+
setActivePair({
117+
from: sequence[first].textId,
118+
to: sequence[second].textId,
119+
label: "???",
120+
color: "#333"
121+
})
122+
}}
123+
onRelationshipsChange={relationships =>
124+
setRelationships(relationships)
125+
}
76126
onHighlightedChanged={highlightedItems =>
77127
changeHighlightedItems(highlightedItems)
78128
}
79129
onSequenceChange={sequence => {
80130
changeSequence(sequence)
81-
props.onChange(mergeSequence(sequence))
131+
props.onChange({ sequence: mergeSequence(sequence) })
82132
}}
83133
sequence={sequence}
134+
relationships={relationships.concat(
135+
activePair !== null ? [activePair] : []
136+
)}
137+
createRelationshipsMode={creatingRelationships}
84138
/>
85139
</div>
86140
</div>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// @flow
2+
3+
import React from "react"
4+
5+
import { storiesOf } from "@storybook/react"
6+
import { action } from "@storybook/addon-actions"
7+
import colors from "../../colors"
8+
9+
import RelationshipAnnotator from "./"
10+
11+
storiesOf("RelationshipAnnotator", module)
12+
.add("Basic", () => (
13+
<RelationshipAnnotator
14+
type="label-relationships"
15+
document={`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pharetra ipsum tristique ligula venenatis placerat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Fusce mollis velit nec tellus sollicitudin aliquam. In velit erat, iaculis id consectetur et, tincidunt sit amet mauris. Quisque ultricies, purus eleifend congue malesuada, ipsum erat molestie dolor, in pellentesque lacus purus vel nisl. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla sed vestibulum magna. Quisque ut lorem imperdiet, aliquam velit nec, dictum felis.`}
16+
labels={[
17+
{
18+
color: colors[0],
19+
id: "noun",
20+
displayName: "Noun"
21+
},
22+
{
23+
color: colors[1],
24+
id: "proper-noun",
25+
displayName: "Proper Noun"
26+
}
27+
]}
28+
onChange={action("onChange")}
29+
/>
30+
))
31+
.add("Initial Content", () => (
32+
<RelationshipAnnotator
33+
type="label-relationships"
34+
document={`Lorem ipsum dolor.`}
35+
initialSequence={[
36+
{ label: "noun", text: "Lorem" },
37+
{ text: " ipsum " },
38+
{ label: "proper-noun", text: "dolor." },
39+
{ text: " this should be broken up into words." }
40+
]}
41+
labels={[
42+
{
43+
color: colors[0],
44+
id: "noun",
45+
displayName: "Noun"
46+
},
47+
{
48+
color: colors[1],
49+
id: "proper-noun",
50+
displayName: "Proper Noun"
51+
}
52+
]}
53+
onChange={action("onChange")}
54+
/>
55+
))
56+
.add("Basic Nested Labels", () => (
57+
<RelationshipAnnotator
58+
type="label-relationships"
59+
document={`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pharetra ipsum tristique ligula venenatis placerat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Fusce mollis velit nec tellus sollicitudin aliquam. In velit erat, iaculis id consectetur et, tincidunt sit amet mauris. Quisque ultricies, purus eleifend congue malesuada, ipsum erat molestie dolor, in pellentesque lacus purus vel nisl. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla sed vestibulum magna. Quisque ut lorem imperdiet, aliquam velit nec, dictum felis.`}
60+
labels={[
61+
{
62+
color: colors[0],
63+
id: "intent",
64+
displayName: "Intent"
65+
},
66+
{
67+
color: colors[1],
68+
id: "subject",
69+
displayName: "Subject"
70+
},
71+
{
72+
color: colors[6],
73+
id: "desire",
74+
displayName: "Desire",
75+
parent: "intent"
76+
},
77+
{
78+
color: colors[7],
79+
id: "informative-desire",
80+
displayName: "Informative Desire",
81+
parent: "desire"
82+
},
83+
{
84+
color: colors[2],
85+
id: "wants-information",
86+
displayName: "Wants More Information",
87+
parent: "informative-desire"
88+
},
89+
{
90+
color: colors[3],
91+
id: "cancel",
92+
displayName: "Wants To Cancel",
93+
parent: "desire"
94+
},
95+
{
96+
color: colors[4],
97+
id: "continue",
98+
displayName: "Wants To Continue",
99+
parent: "desire"
100+
},
101+
{
102+
color: colors[5],
103+
id: "person",
104+
displayName: "Person",
105+
parent: "subject"
106+
},
107+
{
108+
color: colors[6],
109+
id: "object",
110+
displayName: "Object",
111+
parent: "subject"
112+
}
113+
]}
114+
onChange={action("onChange")}
115+
/>
116+
))
117+
.add("Accented Characters", () => (
118+
<RelationshipAnnotator
119+
type="label-relationships"
120+
document={`This should appear as a single word: aùbûcàdâeçfégèhêiëjïkîlôm (and not multiple letters)`}
121+
labels={[
122+
{
123+
color: colors[0],
124+
id: "test",
125+
displayName: "test"
126+
}
127+
]}
128+
onChange={action("onChange")}
129+
/>
130+
))

src/components/RelationshipArrows/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,10 @@ export const RelationshipArrows = ({
418418
</div>
419419
{arrows.map((arrow, i) => (
420420
<ArrowLabel
421-
onClick={() => onClickArrow(arrow)}
421+
onClick={e => {
422+
e.stopPropagation()
423+
onClickArrow(arrow)
424+
}}
422425
style={{
423426
left: labelPositions[i][0],
424427
top: labelPositions[i][1] - 9,

0 commit comments

Comments
 (0)