@manuscripts/body-editor 3.16.3 → 3.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/cjs/lib/context-menu.js +28 -11
  2. package/dist/cjs/lib/popper.js +18 -3
  3. package/dist/cjs/lib/position-menu.js +175 -100
  4. package/dist/cjs/lib/view.js +10 -1
  5. package/dist/cjs/plugins/detect-inconsistency/index.js +6 -4
  6. package/dist/cjs/plugins/persist.js +3 -0
  7. package/dist/cjs/versions.js +1 -1
  8. package/dist/cjs/views/box_element.js +23 -1
  9. package/dist/cjs/views/caption_title.js +1 -1
  10. package/dist/cjs/views/footnote.js +43 -18
  11. package/dist/cjs/views/general_table_footnote.js +31 -21
  12. package/dist/cjs/views/image_element.js +7 -62
  13. package/dist/cjs/views/pullquote_element.js +23 -1
  14. package/dist/cjs/views/table_element.js +23 -1
  15. package/dist/es/lib/context-menu.js +29 -12
  16. package/dist/es/lib/popper.js +18 -3
  17. package/dist/es/lib/position-menu.js +173 -91
  18. package/dist/es/lib/view.js +8 -0
  19. package/dist/es/plugins/detect-inconsistency/index.js +6 -4
  20. package/dist/es/plugins/persist.js +3 -0
  21. package/dist/es/versions.js +1 -1
  22. package/dist/es/views/box_element.js +23 -1
  23. package/dist/es/views/caption_title.js +1 -1
  24. package/dist/es/views/footnote.js +43 -18
  25. package/dist/es/views/general_table_footnote.js +31 -21
  26. package/dist/es/views/image_element.js +7 -62
  27. package/dist/es/views/pullquote_element.js +23 -1
  28. package/dist/es/views/table_element.js +23 -1
  29. package/dist/types/lib/popper.d.ts +4 -2
  30. package/dist/types/lib/position-menu.d.ts +36 -11
  31. package/dist/types/lib/view.d.ts +3 -1
  32. package/dist/types/versions.d.ts +1 -1
  33. package/dist/types/views/box_element.d.ts +6 -0
  34. package/dist/types/views/footnote.d.ts +5 -2
  35. package/dist/types/views/general_table_footnote.d.ts +2 -2
  36. package/dist/types/views/image_element.d.ts +2 -9
  37. package/dist/types/views/pullquote_element.d.ts +6 -0
  38. package/dist/types/views/table_element.d.ts +6 -0
  39. package/package.json +2 -2
  40. package/src/lib/context-menu.ts +42 -21
  41. package/src/lib/popper.ts +23 -4
  42. package/src/lib/position-menu.ts +243 -137
  43. package/src/lib/view.ts +17 -0
  44. package/src/plugins/detect-inconsistency/index.ts +6 -4
  45. package/src/plugins/persist.ts +3 -0
  46. package/src/versions.ts +1 -1
  47. package/src/views/box_element.ts +34 -1
  48. package/src/views/caption_title.ts +1 -1
  49. package/src/views/footnote.ts +69 -33
  50. package/src/views/general_table_footnote.ts +43 -20
  51. package/src/views/image_element.ts +12 -89
  52. package/src/views/pullquote_element.ts +33 -1
  53. package/src/views/table_element.ts +34 -1
  54. package/styles/AdvancedEditor.css +5 -8
  55. package/styles/Editor.css +9 -5
package/src/lib/view.ts CHANGED
@@ -27,6 +27,7 @@ import * as utils from 'prosemirror-utils'
27
27
 
28
28
  import { isHidden } from './track-changes-utils'
29
29
  import { sanitizeAttrsChange } from '@manuscripts/track-changes-plugin'
30
+ import { EditorView } from 'prosemirror-view'
30
31
 
31
32
  const metaNodeTypes = [
32
33
  schema.nodes.bibliography_item,
@@ -163,3 +164,19 @@ export const saveBibliographyItem = (
163
164
 
164
165
  insertBibliographyItems(view, attrs)
165
166
  }
167
+
168
+ export const isSelectionInsideNode = (
169
+ view: EditorView,
170
+ node: ManuscriptNode,
171
+ pos: number
172
+ ) => {
173
+ const { from, to, empty } = view.state.selection
174
+ const end = pos + node.nodeSize
175
+
176
+ // Treat a cursor as "inside" only when it is strictly within the node range.
177
+ if (empty) {
178
+ return from > pos && from < end
179
+ }
180
+
181
+ return from >= pos && to <= end
182
+ }
@@ -35,11 +35,13 @@ export default (props: EditorProps) => {
35
35
  key: detectInconsistencyKey,
36
36
  state: {
37
37
  init: (_, state) => buildPluginState(state, props, false),
38
- apply: (tr, value, newState) => {
38
+ apply: (tr, value, _, newState) => {
39
+ const metaValue = tr.getMeta(detectInconsistencyKey)
39
40
  const showDecorations =
40
- tr.getMeta(detectInconsistencyKey) !== undefined
41
- ? tr.getMeta(detectInconsistencyKey)
42
- : value.showDecorations
41
+ metaValue !== undefined ? metaValue : value.showDecorations
42
+ if (!tr.docChanged && metaValue === undefined) {
43
+ return value
44
+ }
43
45
  return buildPluginState(newState, props, showDecorations)
44
46
  },
45
47
  },
@@ -56,6 +56,9 @@ export default () => {
56
56
  }
57
57
  ids.add(id)
58
58
  })
59
+ if (!tr.steps.length) {
60
+ return null
61
+ }
59
62
  skipTracking(tr)
60
63
  tr.setMeta('origin', 'persist')
61
64
  return tr
package/src/versions.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  //THIS FILE WAS GENERATED BY versions.mjs
2
- export const VERSION = '3.16.3';
2
+ export const VERSION = '3.17.1';
3
3
  export const MATHJAX_VERSION = '3.2.2';
@@ -18,15 +18,48 @@ import { BoxElementNode } from '@manuscripts/transform'
18
18
 
19
19
  import BlockView from './block_view'
20
20
  import { createNodeView } from './creators'
21
+ import { HorizontalPositionMenu } from '../lib/position-menu'
21
22
 
22
23
  export class BoxElementView extends BlockView<BoxElementNode> {
23
24
  public elementType = 'div'
25
+ container: HTMLDivElement
24
26
 
25
27
  public createElement = () => {
26
28
  this.contentDOM = document.createElement(this.elementType)
27
29
  this.contentDOM.className = 'block box-element'
28
30
  this.contentDOM.setAttribute('id', this.node.attrs.id)
29
- this.dom.appendChild(this.contentDOM)
31
+ this.container = document.createElement('div')
32
+ this.container.classList.add('container')
33
+ this.container.appendChild(this.contentDOM)
34
+ this.dom.appendChild(this.container)
35
+ }
36
+
37
+ public updateContents() {
38
+ super.updateContents()
39
+ this.addTools()
40
+ }
41
+
42
+ protected addTools() {
43
+ this.addPositionMenu()
44
+ }
45
+
46
+ private positionMenu: HorizontalPositionMenu
47
+
48
+ protected addPositionMenu() {
49
+ if (!this.positionMenu) {
50
+ this.positionMenu = new HorizontalPositionMenu(
51
+ this,
52
+ this.updateHorizontalPosition,
53
+ this.container
54
+ )
55
+ }
56
+ this.positionMenu.create()
57
+ }
58
+
59
+ private updateHorizontalPosition(horizontalPosition: string) {
60
+ const tr = this.view.state.tr
61
+ tr.setNodeAttribute(this.getPos(), 'type', horizontalPosition)
62
+ this.view.dispatch(tr)
30
63
  }
31
64
  }
32
65
 
@@ -26,7 +26,7 @@ export class CaptionTitleView extends BaseNodeView<CaptionTitleNode> {
26
26
  }
27
27
 
28
28
  protected createDOM() {
29
- this.dom = document.createElement('label')
29
+ this.dom = document.createElement('div')
30
30
  this.dom.className = 'caption-title placeholder'
31
31
  this.contentDOM = this.dom
32
32
  }
@@ -19,6 +19,7 @@ import { isDeleted, isPendingInsert } from '@manuscripts/track-changes-plugin'
19
19
  import { FootnoteNode, ManuscriptNode, schema } from '@manuscripts/transform'
20
20
 
21
21
  import isEqual from 'lodash/isEqual'
22
+ import xor from 'lodash/xor'
22
23
  import { NodeSelection, Transaction } from 'prosemirror-state'
23
24
  import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils'
24
25
 
@@ -33,10 +34,13 @@ import { BaseNodeView } from './base_node_view'
33
34
  import { createNodeView } from './creators'
34
35
  import ReactSubView from './ReactSubView'
35
36
  import { handleEnterKey } from '../lib/navigation-utils'
37
+ import { isSelectionInsideNode } from '../lib/view'
36
38
 
37
39
  export class FootnoteView extends BaseNodeView<Trackable<FootnoteNode>> {
38
40
  dialog: HTMLElement
39
41
  contextMenu: HTMLDivElement
42
+ isMenuShown: boolean = false
43
+ previousActionsLabels: string[] = []
40
44
 
41
45
  public initialise = () => {
42
46
  this.dom = document.createElement('div')
@@ -44,10 +48,24 @@ export class FootnoteView extends BaseNodeView<Trackable<FootnoteNode>> {
44
48
  this.dom.tabIndex = 0
45
49
  this.contentDOM = document.createElement('div')
46
50
  this.contentDOM.classList.add('footnote-text')
47
- this.dom.addEventListener('mousedown', (e) => this.handleClick(e, false))
51
+ if (!this.view.editable) {
52
+ // when the editor is in the viewing mode we can't depend on the selection to show context-menu so we use mousedown
53
+ // as in that case will not have the same listener from prosemirror-view to interfere with it
54
+ const actions = this.getContextMenuActions()
55
+ if (actions.length > 0) {
56
+ this.dom.addEventListener('mousedown', () =>
57
+ this.showContextMenu(actions, true)
58
+ )
59
+ }
60
+ }
48
61
  this.dom.addEventListener(
49
62
  'keydown',
50
- handleEnterKey((e) => this.handleClick(e, true))
63
+ handleEnterKey(() => {
64
+ const actions = this.getContextMenuActions()
65
+ if (actions.length > 0) {
66
+ this.showContextMenu(actions, true)
67
+ }
68
+ })
51
69
  )
52
70
  this.updateContents()
53
71
  }
@@ -73,6 +91,29 @@ export class FootnoteView extends BaseNodeView<Trackable<FootnoteNode>> {
73
91
  this.dom.innerHTML = ''
74
92
  this.dom.appendChild(marker)
75
93
  this.contentDOM && this.dom.appendChild(this.contentDOM)
94
+
95
+ const selectionInsideNode = isSelectionInsideNode(
96
+ this.view,
97
+ this.node,
98
+ this.getPos()
99
+ )
100
+
101
+ const actions = this.getContextMenuActions()
102
+ const hasActions = actions.length > 0
103
+ const currentLabels = actions.map(({ label }) => label)
104
+ const changedLabels = xor(currentLabels, this.previousActionsLabels)
105
+ if (
106
+ selectionInsideNode &&
107
+ hasActions &&
108
+ (!this.isMenuShown || changedLabels.length > 0)
109
+ ) {
110
+ this.showContextMenu(actions, false)
111
+ this.previousActionsLabels = actions.map(({ label }) => label)
112
+ } else if ((!hasActions || !selectionInsideNode) && this.isMenuShown) {
113
+ this.props.popper.destroy()
114
+ this.isMenuShown = false
115
+ this.previousActionsLabels = []
116
+ }
76
117
  }
77
118
 
78
119
  getFootnoteState() {
@@ -81,55 +122,50 @@ export class FootnoteView extends BaseNodeView<Trackable<FootnoteNode>> {
81
122
  return { id, fn }
82
123
  }
83
124
 
84
- showContextMenu(element: HTMLElement, autoFocus: boolean) {
125
+ showContextMenu(actions: ContextMenuProps['actions'], autoFocus: boolean) {
126
+ this.isMenuShown = true
85
127
  this.props.popper.destroy()
86
-
87
- const can = this.props.getCapabilities()
88
- const { id, fn } = this.getFootnoteState()
89
-
90
- const componentProps: ContextMenuProps = {
91
- actions: [],
92
- }
93
- if (!fn?.unusedFootnoteIDs.has(id)) {
94
- componentProps.actions.push({
95
- label: 'Go to footnote Refernce',
96
- action: () => this.handleMarkerClick(),
97
- icon: 'Scroll',
98
- })
99
- }
100
- if (can.editArticle && !isDeleted(this.node)) {
101
- componentProps.actions.push({
102
- label: 'Delete',
103
- action: () => this.handleDeleteClick(),
104
- icon: 'Delete',
105
- })
106
- }
107
-
108
128
  this.contextMenu = ReactSubView(
109
129
  this.props,
110
130
  ContextMenu,
111
- componentProps,
131
+ { actions },
112
132
  this.node,
113
133
  this.getPos,
114
134
  this.view,
115
135
  ['context-menu', 'footnote-context-menu']
116
136
  )
117
137
  this.props.popper.show(
118
- element,
138
+ this.dom,
119
139
  this.contextMenu,
120
140
  'right-start',
121
141
  true,
122
142
  [],
123
- autoFocus
143
+ autoFocus,
144
+ false
124
145
  )
125
146
  }
126
147
 
127
- handleClick = (event: Event, fromKeyboard = false) => {
128
- const element = event.target as HTMLElement
129
- const item = element.closest('.footnote')
130
- if (item) {
131
- this.showContextMenu(item as HTMLElement, fromKeyboard)
148
+ getContextMenuActions() {
149
+ const can = this.props.getCapabilities()
150
+ const { id, fn } = this.getFootnoteState()
151
+ const actions: ContextMenuProps['actions'] = []
152
+
153
+ if (!fn?.unusedFootnoteIDs.has(id)) {
154
+ actions.push({
155
+ label: 'Go to footnote Reference',
156
+ action: () => this.handleMarkerClick(),
157
+ icon: 'Scroll',
158
+ })
159
+ }
160
+ if (can.editArticle && !isDeleted(this.node)) {
161
+ actions.push({
162
+ label: 'Delete',
163
+ action: () => this.handleDeleteClick(),
164
+ icon: 'Delete',
165
+ })
132
166
  }
167
+
168
+ return actions
133
169
  }
134
170
 
135
171
  handleMarkerClick = (e?: Event) => {
@@ -30,17 +30,30 @@ import { BaseNodeView } from './base_node_view'
30
30
  import { createNodeView } from './creators'
31
31
  import ReactSubView from './ReactSubView'
32
32
  import { isDeleted, isPendingInsert } from '@manuscripts/track-changes-plugin'
33
+ import { handleEnterKey } from '../lib/navigation-utils'
34
+ import { isSelectionInsideNode } from '../lib/view'
33
35
 
34
36
  export class GeneralTableFootnoteView extends BaseNodeView<
35
37
  Trackable<GeneralTableFootnoteNode>
36
38
  > {
37
39
  dialog: HTMLElement
38
40
  contextMenu: HTMLDivElement
41
+ isMenuShown: boolean = false
39
42
 
40
43
  public initialise = () => {
41
44
  this.dom = document.createElement('div')
42
45
  this.dom.classList.add('footnote', 'general-table-footnote')
43
- this.dom.addEventListener('mousedown', this.handleClick)
46
+ this.dom.tabIndex = 0
47
+ this.dom.addEventListener(
48
+ 'keydown',
49
+ handleEnterKey(() => {
50
+ const can = this.props.getCapabilities()
51
+ const canShowMenu = can.editArticle && !isDeleted(this.node)
52
+ if (canShowMenu) {
53
+ this.showContextMenu(true)
54
+ }
55
+ })
56
+ )
44
57
  this.contentDOM = document.createElement('div')
45
58
  this.contentDOM.classList.add('footnote-text')
46
59
  this.updateContents()
@@ -50,32 +63,34 @@ export class GeneralTableFootnoteView extends BaseNodeView<
50
63
  super.updateContents()
51
64
  this.dom.innerHTML = ''
52
65
  this.contentDOM && this.dom.appendChild(this.contentDOM)
53
- }
54
66
 
55
- handleClick = (e: Event) => {
56
- const element = e.target as HTMLElement
67
+ const selectionInsideNode = isSelectionInsideNode(
68
+ this.view,
69
+ this.node,
70
+ this.getPos()
71
+ )
57
72
  const can = this.props.getCapabilities()
58
-
59
- if (can.editArticle) {
60
- const item = element.closest('.general-table-footnote')
61
- if (item) {
62
- this.showContextMenu(item as HTMLElement)
63
- }
73
+ const canShowMenu = can.editArticle && !isDeleted(this.node)
74
+ if (canShowMenu && selectionInsideNode && !this.isMenuShown) {
75
+ this.showContextMenu(false)
76
+ } else if ((!canShowMenu || !selectionInsideNode) && this.isMenuShown) {
77
+ this.props.popper.destroy()
78
+ this.isMenuShown = false
64
79
  }
65
80
  }
66
81
 
67
- showContextMenu(element: HTMLElement) {
82
+ showContextMenu(autoFocus = false) {
83
+ this.isMenuShown = true
68
84
  this.props.popper.destroy()
69
85
 
70
86
  const componentProps: ContextMenuProps = {
71
- actions: [],
72
- }
73
- if (!isDeleted(this.node)) {
74
- componentProps.actions.push({
75
- label: 'Delete',
76
- action: () => this.handleDeleteClick(),
77
- icon: 'Delete',
78
- })
87
+ actions: [
88
+ {
89
+ label: 'Delete',
90
+ action: () => this.handleDeleteClick(),
91
+ icon: 'Delete',
92
+ },
93
+ ],
79
94
  }
80
95
 
81
96
  this.contextMenu = ReactSubView(
@@ -87,7 +102,15 @@ export class GeneralTableFootnoteView extends BaseNodeView<
87
102
  this.view,
88
103
  ['context-menu', 'footnote-context-menu']
89
104
  )
90
- this.props.popper.show(element, this.contextMenu, 'right-start')
105
+ this.props.popper.show(
106
+ this.dom,
107
+ this.contextMenu,
108
+ 'right-start',
109
+ false,
110
+ [],
111
+ autoFocus,
112
+ false
113
+ )
91
114
  }
92
115
 
93
116
  handleDeleteClick = () => {
@@ -14,7 +14,6 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { ContextMenu } from '@manuscripts/style-guide'
18
17
  import { FigureNode, ImageElementNode, schema } from '@manuscripts/transform'
19
18
 
20
19
  import { deleteIcon, linkIcon } from '../icons'
@@ -24,24 +23,16 @@ import {
24
23
  MediaType,
25
24
  } from '../lib/media'
26
25
  import { handleEnterKey } from '../lib/navigation-utils'
27
- import { createPositionMenuWrapper } from '../lib/position-menu'
26
+ import { HorizontalPositionMenu } from '../lib/position-menu'
28
27
  import { Trackable } from '../types'
29
28
  import BlockView from './block_view'
30
29
  import { createNodeView } from './creators'
31
- import ReactSubView from './ReactSubView'
32
-
33
- export enum figurePositions {
34
- left = 'half-left',
35
- right = 'half-right',
36
- default = '',
37
- }
38
30
 
39
31
  export class ImageElementView extends BlockView<Trackable<ImageElementNode>> {
40
32
  public container: HTMLElement
41
33
  public subcontainer: HTMLElement
42
34
  public extLinkEditorContainer: HTMLDivElement
43
- private positionMenuWrapper: HTMLDivElement
44
- private figurePosition: string
35
+ private positionMenu: HorizontalPositionMenu
45
36
  private isEditingExtLink = false
46
37
 
47
38
  public ignoreMutation = () => true
@@ -93,33 +84,12 @@ export class ImageElementView extends BlockView<Trackable<ImageElementNode>> {
93
84
  this.addPositionMenu()
94
85
  }
95
86
 
96
- protected addPositionMenu() {
97
- if (this.props.getCapabilities()?.editArticle) {
98
- // Remove existing position menu if it exists
99
- const existingMenu = this.container.querySelector('.position-menu')
100
- if (existingMenu) {
101
- existingMenu.remove()
102
- }
103
-
104
- const firstFigure = this.getFirstFigure()
105
- this.figurePosition = firstFigure?.attrs.type || figurePositions.default
106
-
107
- this.positionMenuWrapper = createPositionMenuWrapper(
108
- this.figurePosition,
109
- this.showPositionMenu,
110
- this.props
111
- )
112
-
113
- this.container.prepend(this.positionMenuWrapper)
114
- }
115
- }
116
-
117
87
  /**
118
88
  * Gets the first figure node from the element.
119
89
  * Optimized for image_element (direct access) vs figure_element (loop through all).
120
90
  * @returns The first figure node or null if no figure exists
121
91
  */
122
- private getFirstFigure(): FigureNode | null {
92
+ private getFirstFigure() {
123
93
  // Optimize for image_element which only has one figure
124
94
  if (this.node.type === schema.nodes.image_element) {
125
95
  // Direct access to the single figure in image_element
@@ -166,62 +136,16 @@ export class ImageElementView extends BlockView<Trackable<ImageElementNode>> {
166
136
  return figures
167
137
  }
168
138
 
169
- private showPositionMenu = () => {
170
- const firstFigure = this.getFirstFigure()
171
- if (!firstFigure) {
172
- return
173
- }
174
-
175
- this.props.popper.destroy()
176
-
177
- const options = [
178
- {
179
- label: 'Left',
180
- action: () => {
181
- this.props.popper.destroy()
182
- this.updateAllFiguresPosition(figurePositions.left)
183
- },
184
- icon: 'ImageLeft',
185
- selected: this.figurePosition === figurePositions.left,
186
- },
187
- {
188
- label: 'Default',
189
- action: () => {
190
- this.props.popper.destroy()
191
- this.updateAllFiguresPosition(figurePositions.default)
192
- },
193
- icon: 'ImageDefault',
194
- selected: !this.figurePosition,
195
- },
196
- {
197
- label: 'Right',
198
- action: () => {
199
- this.props.popper.destroy()
200
- this.updateAllFiguresPosition(figurePositions.right)
201
- },
202
- icon: 'ImageRight',
203
- selected: this.figurePosition === figurePositions.right,
204
- },
205
- ]
206
-
207
- const componentProps = {
208
- actions: options,
139
+ protected addPositionMenu() {
140
+ if (!this.positionMenu) {
141
+ this.positionMenu = new HorizontalPositionMenu(
142
+ this,
143
+ this.updateAllFiguresPosition.bind(this),
144
+ this.container,
145
+ () => this.getFirstFigure()
146
+ )
209
147
  }
210
-
211
- this.props.popper.show(
212
- this.positionMenuWrapper,
213
- ReactSubView(
214
- this.props,
215
- ContextMenu,
216
- componentProps,
217
- this.node,
218
- this.getPos,
219
- this.view,
220
- ['context-menu', 'position-menu']
221
- ),
222
- 'left',
223
- false
224
- )
148
+ this.positionMenu.create()
225
149
  }
226
150
 
227
151
  /**
@@ -241,7 +165,6 @@ export class ImageElementView extends BlockView<Trackable<ImageElementNode>> {
241
165
  })
242
166
 
243
167
  this.view.dispatch(tr)
244
- this.figurePosition = position
245
168
  }
246
169
 
247
170
  // external link editor
@@ -24,10 +24,13 @@ import { setCommentSelection } from '../plugins/comments'
24
24
  import BlockView from './block_view'
25
25
  import { createNodeOrElementView } from './creators'
26
26
  import ReactSubView from './ReactSubView'
27
+ import { HorizontalPositionMenu } from '../lib/position-menu'
27
28
 
28
29
  export class PullquoteElementView extends BlockView<PullquoteElementNode> {
29
30
  public elementType = 'aside'
30
31
  contextMenu: HTMLElement
32
+ container: HTMLDivElement
33
+ private positionMenu: HorizontalPositionMenu
31
34
 
32
35
  public ignoreMutation = () => true
33
36
  public stopEvent = () => true
@@ -51,7 +54,10 @@ export class PullquoteElementView extends BlockView<PullquoteElementNode> {
51
54
  this.contentDOM.className = 'block'
52
55
  this.contentDOM.classList.add('pullquote')
53
56
 
54
- this.dom.appendChild(this.contentDOM)
57
+ this.container = document.createElement('div')
58
+ this.container.classList.add('container')
59
+ this.container.appendChild(this.contentDOM)
60
+ this.dom.appendChild(this.container)
55
61
 
56
62
  // Add click event listener to handle comment marker clicks
57
63
  this.dom.addEventListener('click', this.handleClick)
@@ -96,11 +102,37 @@ export class PullquoteElementView extends BlockView<PullquoteElementNode> {
96
102
  return undefined
97
103
  }
98
104
 
105
+ public updateContents() {
106
+ super.updateContents()
107
+ this.addTools()
108
+ }
109
+
99
110
  public destroy = () => {
100
111
  // Clean up event listener
101
112
  this.dom.removeEventListener('click', this.handleClick)
102
113
  super.destroy()
103
114
  }
115
+
116
+ protected addTools() {
117
+ this.addPositionMenu()
118
+ }
119
+
120
+ protected addPositionMenu() {
121
+ if (!this.positionMenu) {
122
+ this.positionMenu = new HorizontalPositionMenu(
123
+ this,
124
+ this.updateHorizontalPosition,
125
+ this.container
126
+ )
127
+ }
128
+ this.positionMenu.create()
129
+ }
130
+
131
+ private updateHorizontalPosition(horizontalPosition: string) {
132
+ const tr = this.view.state.tr
133
+ tr.setNodeAttribute(this.getPos(), 'type', horizontalPosition)
134
+ this.view.dispatch(tr)
135
+ }
104
136
  }
105
137
 
106
138
  export default createNodeOrElementView(PullquoteElementView, 'aside')
@@ -18,15 +18,48 @@ import { TableElementNode } from '@manuscripts/transform'
18
18
 
19
19
  import BlockView from './block_view'
20
20
  import { createNodeView } from './creators'
21
+ import { HorizontalPositionMenu } from '../lib/position-menu'
21
22
 
22
23
  export class TableElementView extends BlockView<TableElementNode> {
23
24
  public elementType = 'figure'
25
+ container: HTMLDivElement
26
+ private positionMenu: HorizontalPositionMenu
24
27
 
25
28
  public createElement = () => {
26
29
  this.contentDOM = document.createElement('figure')
27
30
  this.contentDOM.classList.add('block')
28
31
  this.contentDOM.setAttribute('id', this.node.attrs.id)
29
- this.dom.appendChild(this.contentDOM)
32
+
33
+ this.container = document.createElement('div')
34
+ this.container.classList.add('container')
35
+ this.container.appendChild(this.contentDOM)
36
+ this.dom.appendChild(this.container)
37
+ }
38
+
39
+ public updateContents() {
40
+ super.updateContents()
41
+ this.addTools()
42
+ }
43
+
44
+ protected addTools() {
45
+ this.addPositionMenu()
46
+ }
47
+
48
+ protected addPositionMenu() {
49
+ if (!this.positionMenu) {
50
+ this.positionMenu = new HorizontalPositionMenu(
51
+ this,
52
+ this.updateHorizontalPosition,
53
+ this.container
54
+ )
55
+ }
56
+ this.positionMenu.create()
57
+ }
58
+
59
+ private updateHorizontalPosition(horizontalPosition: string) {
60
+ const tr = this.view.state.tr
61
+ tr.setNodeAttribute(this.getPos(), 'type', horizontalPosition)
62
+ this.view.dispatch(tr)
30
63
  }
31
64
  }
32
65