add-comment.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import {
  2. Decoration,
  3. DecorationSet,
  4. EditorView,
  5. showTooltip,
  6. Tooltip,
  7. } from '@codemirror/view'
  8. import {
  9. Extension,
  10. StateField,
  11. StateEffect,
  12. Range,
  13. SelectionRange,
  14. } from '@codemirror/state'
  15. import { isSplitTestEnabled } from '@/utils/splitTestUtils'
  16. import { v4 as uuid } from 'uuid'
  17. import { textSelected, textSelectedEffect } from './text-selected'
  18. export const addNewCommentRangeEffect = StateEffect.define<Range<Decoration>>()
  19. export const removeNewCommentRangeEffect = StateEffect.define<Decoration>()
  20. export const buildAddNewCommentRangeEffect = (range: SelectionRange) => {
  21. return addNewCommentRangeEffect.of(
  22. Decoration.mark({
  23. tagName: 'span',
  24. class: `ol-cm-change ol-cm-change-c`,
  25. opType: 'c',
  26. id: uuid(),
  27. }).range(range.from, range.to)
  28. )
  29. }
  30. export const addComment = (): Extension => {
  31. if (!isSplitTestEnabled('review-panel-redesign')) {
  32. return []
  33. }
  34. return [addCommentTheme, addCommentStateField, textSelected]
  35. }
  36. export const addCommentStateField = StateField.define<{
  37. tooltip: Tooltip | null
  38. ranges: DecorationSet
  39. }>({
  40. create() {
  41. return { tooltip: null, ranges: Decoration.none }
  42. },
  43. update(field, tr) {
  44. let { tooltip, ranges } = field
  45. ranges = ranges.map(tr.changes)
  46. for (const effect of tr.effects) {
  47. if (effect.is(removeNewCommentRangeEffect)) {
  48. const rangeToRemove = effect.value
  49. ranges = ranges.update({
  50. // eslint-disable-next-line no-unused-vars
  51. filter: (from, to, value) => {
  52. return value.spec.id !== rangeToRemove.spec.id
  53. },
  54. })
  55. }
  56. if (effect.is(addNewCommentRangeEffect)) {
  57. const rangeToAdd = effect.value
  58. ranges = ranges.update({
  59. add: [rangeToAdd],
  60. })
  61. }
  62. if (effect.is(textSelectedEffect)) {
  63. tooltip = buildTooltip(effect.value)
  64. }
  65. if (tooltip && tr.state.selection.main.empty) {
  66. tooltip = null
  67. }
  68. }
  69. return { tooltip, ranges }
  70. },
  71. provide: field => [
  72. EditorView.decorations.from(field, field => field.ranges),
  73. showTooltip.compute([field], state => state.field(field).tooltip),
  74. ],
  75. })
  76. function buildTooltip(range: SelectionRange): Tooltip | null {
  77. if (range.empty) {
  78. return null
  79. }
  80. return {
  81. pos: range.assoc < 0 ? range.to : range.from,
  82. above: true,
  83. strictSide: true,
  84. arrow: false,
  85. create() {
  86. const dom = document.createElement('div')
  87. dom.className = 'review-panel-add-comment-tooltip-container'
  88. return { dom, overlap: true, offset: { x: 0, y: 8 } }
  89. },
  90. }
  91. }
  92. /**
  93. * Styles for the tooltip
  94. */
  95. const addCommentTheme = EditorView.baseTheme({
  96. '.review-panel-add-comment-tooltip-container.cm-tooltip': {
  97. backgroundColor: 'transparent',
  98. border: 'none',
  99. },
  100. '&light': {
  101. '& .review-panel-add-comment-tooltip': {
  102. backgroundColor: 'white',
  103. border: '1px solid #e7e9ee',
  104. '&:hover': {
  105. backgroundColor: '#e7e9ee',
  106. },
  107. },
  108. },
  109. '&dark': {
  110. '& .review-panel-add-comment-tooltip': {
  111. backgroundColor: '#1b222c',
  112. border: '1px solid #2f3a4c',
  113. '&:hover': {
  114. backgroundColor: '#2f3a4c',
  115. },
  116. },
  117. },
  118. })