Skip to content

Commit

Permalink
Refactor to remove Django template inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
c-w committed Apr 13, 2019
1 parent 21471bd commit d5bc9e4
Show file tree
Hide file tree
Showing 27 changed files with 1,694 additions and 1,112 deletions.
452 changes: 452 additions & 0 deletions app/server/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions app/server/package.json
Expand Up @@ -28,6 +28,8 @@
"eslint-config-airbnb-base": "^13.0.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-vue": "^5.2.2",
"pug": "^2.0.3",
"pug-plain-loader": "^1.0.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.12.0",
"webpack-bundle-tracker": "^0.4.2-beta",
Expand Down
119 changes: 119 additions & 0 deletions app/server/static/js/annotation.pug
@@ -0,0 +1,119 @@
div.columns(v-cloak="")
aside.column.is-3.aside.hero.is-fullheight
div

div.main.pr20.pl20
div.field.has-addons
div.control.is-expanded
input.input(
type="text",
placeholder="Search document",
v-model="searchQuery",
@keyup.enter="submit",
style="border-right: none; box-shadow: none; -webkit-box-shadow: none;"
)

div.control
div.dropdown.is-hoverable.is-right
div.dropdown-trigger
button.button(
aria-haspopup="true",
aria-controls="dropdown-menu",
style="border-left: none"
)
span.icon.has-text-grey.pr0
i.fas.fa-angle-down(aria-hidden="true")

div#dropdown-menu.dropdown-menu.pt0(role="menu")
div.dropdown-content
a.dropdown-item
label.radio
input(
type="radio",
value="all",
v-model="picked",
checked=""
)
| All
a.dropdown-item
label.radio
input(
type="radio",
value="active",
v-model="picked"
)
| Active
a.dropdown-item
label.radio
input(
type="radio",
value="completed",
v-model="picked"
)
| Completed

div.main.pt0.pb0.pr20.pl20
span About {{ count }} results

div.main
a.item(
href="#",
v-for="(doc, index) in docs",
:class="{ active: index == pageNumber }",
@click="pageNumber = index",
:data-preview-id="index"
)
span.icon
i.fa.fa-check(v-show="annotations[index] && annotations[index].length")
span.name {{ doc.text.slice(0, 60) }}...

div#message-pane.column.is-7.is-offset-1.message.hero.is-fullheight

div.modal(:class="{ 'is-active': isActive }")
div.modal-background
div.modal-card
header.modal-card-head
p.modal-card-title Annotation Guideline
button.delete(aria-label="close", @click="isActive = !isActive")
section.modal-card-body.modal-card-body-footer.content(
style="line-height:150%",
v-html="compiledMarkdown"
)

div.columns.is-multiline.is-gapless.is-mobile.is-vertical-center
div.column.is-3
progress.progress.is-inline-block(
:class="progressColor",
:value="achievement",
max="100"
) 30%
div.column.is-8
span.ml10
strong {{ total - remaining }}
| /
span {{ total }}
div.column.is-1.has-text-right
a.button(@click="isActive =! isActive")
span.icon
i.fas.fa-book

block annotation-area

div.level.mt30
a.button.level-left(
@click="prevPage",
v-shortkey="{prev1: ['ctrl', 'p'], prev2: ['arrowup'], prev3: ['arrowleft']}",
@shortkey="prevPage"
)
span.icon
i.fas.fa-chevron-left
span Prev

a.button.level-right(
@click="nextPage",
v-shortkey="{next1: ['ctrl', 'n'], next2: ['arrowdown'], next3: ['arrowright']}",
@shortkey="nextPage"
)
span Next
span.icon
i.fas.fa-chevron-right
168 changes: 168 additions & 0 deletions app/server/static/js/annotator.vue
@@ -0,0 +1,168 @@
<template lang="pug">
div(@click="setSelectedRange")
span.text-sequence(
v-for="r in chunksWithLabel",
:class="{ tag: id2label[r.label].text_color }",
:style="{ \
color: id2label[r.label].text_color, \
backgroundColor: id2label[r.label].background_color \
}",
) {{ [...text].slice(r.start_offset, r.end_offset).join('') }}
button.delete.is-small(v-if="id2label[r.label].text_color", @click="removeLabel(r)")
</template>

<script>
export default {
props: {
labels: {
type: Array, // [{id: Integer, color: String, text: String}]
default: () => [],
},
text: {
type: String,
default: '',
},
entityPositions: {
type: Array, // [{'startOffset': 10, 'endOffset': 15, 'label_id': 1}]
default: () => [],
},
},
data: () => ({
startOffset: 0,
endOffset: 0,
}),
computed: {
sortedEntityPositions() {
/* eslint-disable vue/no-side-effects-in-computed-properties */
this.entityPositions = this.entityPositions.sort((a, b) => a.start_offset - b.start_offset);
return this.entityPositions;
/* eslint-enable vue/no-side-effects-in-computed-properties */
},
chunks() {
const res = [];
let left = 0;
for (let i = 0; i < this.sortedEntityPositions.length; i++) {
const e = this.sortedEntityPositions[i];
const l = this.makeLabel(left, e.start_offset);
res.push(l);
res.push(e);
left = e.end_offset;
}
const l = this.makeLabel(left, this.text.length);
res.push(l);
return res;
},
chunksWithLabel() {
return this.chunks().filter(r => this.id2label[r.label]);
},
id2label() {
const id2label = {};
// default value;
id2label[-1] = {
text_color: '',
background_color: '',
};
for (let i = 0; i < this.labels.length; i++) {
const label = this.labels[i];
id2label[label.id] = label;
}
return id2label;
},
},
watch: {
entityPositions() {
this.resetRange();
},
},
methods: {
setSelectedRange() {
let start;
let end;
if (window.getSelection) {
const range = window.getSelection().getRangeAt(0);
const preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(this.$el);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
start = [...preSelectionRange.toString()].length;
end = start + [...range.toString()].length;
} else if (document.selection && document.selection.type !== 'Control') {
const selectedTextRange = document.selection.createRange();
const preSelectionTextRange = document.body.createTextRange();
preSelectionTextRange.moveToElementText(this.$el);
preSelectionTextRange.setEndPoint('EndToStart', selectedTextRange);
start = [...preSelectionTextRange.text].length;
end = start + [...selectedTextRange.text].length;
}
this.startOffset = start;
this.endOffset = end;
console.log(start, end); // eslint-disable-line no-console
},
validRange() {
if (this.startOffset === this.endOffset) {
return false;
}
if (this.startOffset > this.text.length || this.endOffset > this.text.length) {
return false;
}
if (this.startOffset < 0 || this.endOffset < 0) {
return false;
}
for (let i = 0; i < this.entityPositions.length; i++) {
const e = this.entityPositions[i];
if ((e.start_offset <= this.startOffset) && (this.startOffset < e.end_offset)) {
return false;
}
if ((e.start_offset < this.endOffset) && (this.endOffset < e.end_offset)) {
return false;
}
if ((this.startOffset < e.start_offset) && (e.start_offset < this.endOffset)) {
return false;
}
if ((this.startOffset < e.end_offset) && (e.end_offset < this.endOffset)) {
return false;
}
}
return true;
},
resetRange() {
this.startOffset = 0;
this.endOffset = 0;
},
addLabel(labelId) {
if (this.validRange()) {
const label = {
start_offset: this.startOffset,
end_offset: this.endOffset,
label: labelId,
};
this.$emit('add-label', label);
}
},
removeLabel(index) {
this.$emit('remove-label', index);
},
makeLabel(startOffset, endOffset) {
const label = {
id: 0,
label: -1,
start_offset: startOffset,
end_offset: endOffset,
};
return label;
},
},
};
</script>

0 comments on commit d5bc9e4

Please sign in to comment.