add-comment.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import {
  2. Decoration,
  3. DecorationSet,
  4. EditorView,
  5. showTooltip,
  6. Tooltip,
  7. } from '@codemirror/view'
  8. import {
  9. EditorState,
  10. Extension,
  11. StateField,
  12. StateEffect,
  13. Range,
  14. SelectionRange,
  15. } from '@codemirror/state'
  16. import { isSplitTestEnabled } from '@/utils/splitTestUtils'
  17. import { v4 as uuid } from 'uuid'
  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]
  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. }
  63. if (tr.docChanged || tr.selection) {
  64. tooltip = buildTooltip(tr.state)
  65. }
  66. return { tooltip, ranges }
  67. },
  68. provide: field => [
  69. EditorView.decorations.from(field, field => field.ranges),
  70. showTooltip.compute([field], state => state.field(field).tooltip),
  71. ],
  72. })
  73. function buildTooltip(state: EditorState): Tooltip | null {
  74. const range = state.selection.main
  75. if (range.empty) {
  76. return null
  77. }
  78. return {
  79. pos: range.assoc < 0 ? range.to : range.from,
  80. above: true,
  81. strictSide: true,
  82. arrow: false,
  83. create() {
  84. const dom = document.createElement('div')
  85. dom.className = 'review-panel-add-comment-tooltip-container'
  86. return { dom, overlap: true, offset: { x: 0, y: 8 } }
  87. },
  88. }
  89. }
  90. /**
  91. * Styles for the tooltip
  92. */
  93. const addCommentTheme = EditorView.baseTheme({
  94. '.review-panel-add-comment-tooltip-container.cm-tooltip': {
  95. backgroundColor: 'transparent',
  96. border: 'none',
  97. },
  98. '&light': {
  99. '& .review-panel-add-comment-tooltip': {
  100. backgroundColor: 'white',
  101. border: '1px solid #e7e9ee',
  102. '&:hover': {
  103. backgroundColor: '#e7e9ee',
  104. },
  105. },
  106. },
  107. '&dark': {
  108. '& .review-panel-add-comment-tooltip': {
  109. backgroundColor: '#1b222c',
  110. border: '1px solid #2f3a4c',
  111. '&:hover': {
  112. backgroundColor: '#2f3a4c',
  113. },
  114. },
  115. },
  116. })