You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
4.7 KiB

  1. <template lang="pug">
  2. div(@click="setSelectedRange")
  3. span.text-sequence(
  4. v-for="r in chunksWithLabel"
  5. v-bind:class="{ tag: id2label[r.label].text_color }"
  6. v-bind:style="{ \
  7. color: id2label[r.label].text_color, \
  8. backgroundColor: id2label[r.label].background_color \
  9. }"
  10. ) {{ textPart(r) }}
  11. button.delete.is-small(v-if="id2label[r.label].text_color", v-on:click="removeLabel(r)")
  12. </template>
  13. <script>
  14. export default {
  15. props: {
  16. labels: {
  17. type: Array, // [{id: Integer, color: String, text: String}]
  18. default: () => [],
  19. },
  20. text: {
  21. type: String,
  22. default: '',
  23. },
  24. entityPositions: {
  25. type: Array, // [{'startOffset': 10, 'endOffset': 15, 'label_id': 1}]
  26. default: () => [],
  27. },
  28. },
  29. data: () => ({
  30. startOffset: 0,
  31. endOffset: 0,
  32. }),
  33. computed: {
  34. sortedEntityPositions() {
  35. /* eslint-disable vue/no-side-effects-in-computed-properties */
  36. this.entityPositions = this.entityPositions.sort((a, b) => a.start_offset - b.start_offset);
  37. return this.entityPositions;
  38. /* eslint-enable vue/no-side-effects-in-computed-properties */
  39. },
  40. chunks() {
  41. const res = [];
  42. let left = 0;
  43. for (let i = 0; i < this.sortedEntityPositions.length; i++) {
  44. const e = this.sortedEntityPositions[i];
  45. const l = this.makeLabel(left, e.start_offset);
  46. res.push(l);
  47. res.push(e);
  48. left = e.end_offset;
  49. }
  50. const l = this.makeLabel(left, this.text.length);
  51. res.push(l);
  52. return res;
  53. },
  54. chunksWithLabel() {
  55. return this.chunks.filter(r => this.id2label[r.label]);
  56. },
  57. id2label() {
  58. const id2label = {};
  59. // default value;
  60. id2label[-1] = {
  61. text_color: '',
  62. background_color: '',
  63. };
  64. for (let i = 0; i < this.labels.length; i++) {
  65. const label = this.labels[i];
  66. id2label[label.id] = label;
  67. }
  68. return id2label;
  69. },
  70. },
  71. watch: {
  72. entityPositions() {
  73. this.resetRange();
  74. },
  75. },
  76. methods: {
  77. setSelectedRange() {
  78. let start;
  79. let end;
  80. if (window.getSelection) {
  81. const range = window.getSelection().getRangeAt(0);
  82. const preSelectionRange = range.cloneRange();
  83. preSelectionRange.selectNodeContents(this.$el);
  84. preSelectionRange.setEnd(range.startContainer, range.startOffset);
  85. start = [...preSelectionRange.toString()].length;
  86. end = start + [...range.toString()].length;
  87. } else if (document.selection && document.selection.type !== 'Control') {
  88. const selectedTextRange = document.selection.createRange();
  89. const preSelectionTextRange = document.body.createTextRange();
  90. preSelectionTextRange.moveToElementText(this.$el);
  91. preSelectionTextRange.setEndPoint('EndToStart', selectedTextRange);
  92. start = [...preSelectionTextRange.text].length;
  93. end = start + [...selectedTextRange.text].length;
  94. }
  95. this.startOffset = start;
  96. this.endOffset = end;
  97. console.log(start, end); // eslint-disable-line no-console
  98. },
  99. validRange() {
  100. if (this.startOffset === this.endOffset) {
  101. return false;
  102. }
  103. if (this.startOffset > this.text.length || this.endOffset > this.text.length) {
  104. return false;
  105. }
  106. if (this.startOffset < 0 || this.endOffset < 0) {
  107. return false;
  108. }
  109. for (let i = 0; i < this.entityPositions.length; i++) {
  110. const e = this.entityPositions[i];
  111. if ((e.start_offset <= this.startOffset) && (this.startOffset < e.end_offset)) {
  112. return false;
  113. }
  114. if ((e.start_offset < this.endOffset) && (this.endOffset < e.end_offset)) {
  115. return false;
  116. }
  117. if ((this.startOffset < e.start_offset) && (e.start_offset < this.endOffset)) {
  118. return false;
  119. }
  120. if ((this.startOffset < e.end_offset) && (e.end_offset < this.endOffset)) {
  121. return false;
  122. }
  123. }
  124. return true;
  125. },
  126. resetRange() {
  127. this.startOffset = 0;
  128. this.endOffset = 0;
  129. },
  130. textPart(r) {
  131. return [...this.text].slice(r.start_offset, r.end_offset).join('');
  132. },
  133. addLabel(labelId) {
  134. if (this.validRange()) {
  135. const label = {
  136. start_offset: this.startOffset,
  137. end_offset: this.endOffset,
  138. label: labelId,
  139. };
  140. this.$emit('add-label', label);
  141. }
  142. },
  143. removeLabel(index) {
  144. this.$emit('remove-label', index);
  145. },
  146. makeLabel(startOffset, endOffset) {
  147. const label = {
  148. id: 0,
  149. label: -1,
  150. start_offset: startOffset,
  151. end_offset: endOffset,
  152. };
  153. return label;
  154. },
  155. },
  156. };
  157. </script>