@manuscripts/body-editor 3.13.10 → 3.13.11

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 (52) hide show
  1. package/dist/cjs/commands.js +21 -2
  2. package/dist/cjs/configs/editor-views.js +6 -0
  3. package/dist/cjs/icons.js +2 -1
  4. package/dist/cjs/lib/files.js +6 -1
  5. package/dist/cjs/menus.js +8 -0
  6. package/dist/cjs/node-type-icons.js +4 -0
  7. package/dist/cjs/plugins/accessibility_element.js +2 -1
  8. package/dist/cjs/versions.js +1 -1
  9. package/dist/cjs/views/accessibility_element.js +7 -0
  10. package/dist/cjs/views/headshot_element.js +74 -0
  11. package/dist/cjs/views/headshot_element_editable.js +20 -0
  12. package/dist/cjs/views/headshot_grid.js +82 -0
  13. package/dist/cjs/views/headshot_grid_editable.js +21 -0
  14. package/dist/cjs/views/headshot_image_editable.js +79 -0
  15. package/dist/es/commands.js +18 -0
  16. package/dist/es/configs/editor-views.js +6 -0
  17. package/dist/es/icons.js +2 -1
  18. package/dist/es/lib/files.js +6 -1
  19. package/dist/es/menus.js +9 -1
  20. package/dist/es/node-type-icons.js +5 -1
  21. package/dist/es/plugins/accessibility_element.js +2 -1
  22. package/dist/es/versions.js +1 -1
  23. package/dist/es/views/accessibility_element.js +7 -0
  24. package/dist/es/views/headshot_element.js +70 -0
  25. package/dist/es/views/headshot_element_editable.js +18 -0
  26. package/dist/es/views/headshot_grid.js +75 -0
  27. package/dist/es/views/headshot_grid_editable.js +19 -0
  28. package/dist/es/views/headshot_image_editable.js +75 -0
  29. package/dist/types/commands.d.ts +1 -0
  30. package/dist/types/icons.d.ts +1 -0
  31. package/dist/types/versions.d.ts +1 -1
  32. package/dist/types/views/headshot_element.d.ts +29 -0
  33. package/dist/types/views/headshot_element_editable.d.ts +18 -0
  34. package/dist/types/views/headshot_grid.d.ts +28 -0
  35. package/dist/types/views/headshot_grid_editable.d.ts +44 -0
  36. package/dist/types/views/headshot_image_editable.d.ts +28 -0
  37. package/package.json +2 -2
  38. package/src/commands.ts +27 -0
  39. package/src/configs/editor-views.ts +6 -0
  40. package/src/icons.ts +2 -0
  41. package/src/lib/files.ts +7 -1
  42. package/src/menus.tsx +10 -0
  43. package/src/node-type-icons.tsx +12 -0
  44. package/src/plugins/accessibility_element.ts +4 -1
  45. package/src/versions.ts +1 -1
  46. package/src/views/accessibility_element.ts +7 -0
  47. package/src/views/headshot_element.ts +98 -0
  48. package/src/views/headshot_element_editable.ts +20 -0
  49. package/src/views/headshot_grid.ts +98 -0
  50. package/src/views/headshot_grid_editable.ts +21 -0
  51. package/src/views/headshot_image_editable.ts +93 -0
  52. package/styles/AdvancedEditor.css +169 -1
@@ -0,0 +1,98 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { HeadshotElementNode } from '@manuscripts/transform'
18
+ import { TextSelection } from 'prosemirror-state'
19
+
20
+ import { plusIcon } from '../icons'
21
+ import { handleEnterKey } from '../lib/navigation-utils'
22
+ import { Trackable } from '../types'
23
+ import { BaseNodeView } from './base_node_view'
24
+ import { createNodeView } from './creators'
25
+
26
+ export class HeadshotElement extends BaseNodeView<
27
+ Trackable<HeadshotElementNode>
28
+ > {
29
+ public elementType = 'div'
30
+ private container: HTMLElement
31
+ private deleteHeadshotButton: HTMLElement
32
+
33
+ public initialise() {
34
+ this.createElement()
35
+ this.updateContents()
36
+ }
37
+
38
+ public createElement = () => {
39
+ this.dom = document.createElement('div')
40
+ this.dom.classList.add('headshot-element')
41
+ this.dom.tabIndex = 0
42
+ this.dom.setAttribute('id', this.node.attrs.id)
43
+
44
+ this.container = document.createElement('div')
45
+ this.container.classList.add('block')
46
+ this.dom.appendChild(this.container)
47
+
48
+ this.contentDOM = document.createElement('div')
49
+ this.contentDOM.classList.add('block-container', 'headshot-element-block')
50
+ this.container.appendChild(this.contentDOM)
51
+
52
+ const can = this.props.getCapabilities()
53
+
54
+ if (can.editArticle) {
55
+ this.deleteHeadshotButton = document.createElement('button')
56
+ this.deleteHeadshotButton.innerHTML = plusIcon
57
+ this.deleteHeadshotButton.classList.add('headshot-remove-button')
58
+ this.deleteHeadshotButton.setAttribute('data-cy', 'headshot-delete')
59
+ this.deleteHeadshotButton.contentEditable = 'false'
60
+ this.deleteHeadshotButton.addEventListener(
61
+ 'click',
62
+ this.handleDeleteHeadshot
63
+ )
64
+ this.deleteHeadshotButton.addEventListener(
65
+ 'keydown',
66
+ handleEnterKey(this.handleDeleteHeadshot)
67
+ )
68
+ this.container.appendChild(this.deleteHeadshotButton)
69
+ }
70
+ }
71
+
72
+ private handleDeleteHeadshot = () => {
73
+ const { state, dispatch } = this.view
74
+ const { tr } = state
75
+ const pos = this.getPos()
76
+
77
+ tr.delete(pos, pos + this.node.nodeSize)
78
+ this.view.focus()
79
+ const $pos = tr.doc.resolve(pos)
80
+ if ($pos.node().nodeSize > 2) {
81
+ // we set selection to the start to prevent it from selecting alt_text & long_desc as we render them for now with empty dom node
82
+ tr.setSelection(
83
+ TextSelection.near(tr.doc.resolve($pos.start()))
84
+ ).scrollIntoView()
85
+ }
86
+ dispatch(tr)
87
+ }
88
+
89
+ public destroy() {
90
+ this.deleteHeadshotButton?.removeEventListener(
91
+ 'click',
92
+ this.handleDeleteHeadshot
93
+ )
94
+ super.destroy()
95
+ }
96
+ }
97
+
98
+ export default createNodeView(HeadshotElement)
@@ -0,0 +1,20 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { createEditableNodeView } from './creators'
18
+ import { HeadshotElement } from './headshot_element'
19
+
20
+ export default createEditableNodeView(HeadshotElement)
@@ -0,0 +1,98 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ HeadshotElementNode,
19
+ HeadshotGridNode,
20
+ schema,
21
+ } from '@manuscripts/transform'
22
+ import { TextSelection } from 'prosemirror-state'
23
+
24
+ import { addAuthorIcon } from '../icons'
25
+ import {
26
+ createKeyboardInteraction,
27
+ handleEnterKey,
28
+ } from '../lib/navigation-utils'
29
+ import BlockView from './block_view'
30
+ import { createNodeView } from './creators'
31
+
32
+ export class HeadshotGridView extends BlockView<HeadshotGridNode> {
33
+ public elementType = 'div'
34
+ private container: HTMLElement
35
+ private addHeadshotButton: HTMLElement
36
+ private removeKeydownListener?: () => void
37
+
38
+ public createElement = () => {
39
+ this.container = document.createElement('div')
40
+ this.container.classList.add('block', 'headshot-grid-container')
41
+ this.dom.appendChild(this.container)
42
+
43
+ this.contentDOM = document.createElement('div')
44
+ this.contentDOM.classList.add('headshot-grid-block')
45
+ this.contentDOM.setAttribute('id', this.node.attrs.id)
46
+ this.removeKeydownListener = createKeyboardInteraction({
47
+ container: this.contentDOM,
48
+ navigation: {
49
+ arrowKeys: { forward: 'ArrowDown', backward: 'ArrowUp' },
50
+ getItems: () => [...(this.contentDOM?.children || [])] as HTMLElement[],
51
+ },
52
+ })
53
+ this.container.appendChild(this.contentDOM)
54
+
55
+ const can = this.props.getCapabilities()
56
+
57
+ if (can.editArticle) {
58
+ this.addHeadshotButton = document.createElement('button')
59
+ this.addHeadshotButton.classList.add('add-headshot-element-button')
60
+ this.addHeadshotButton.setAttribute('data-cy', 'headshot-add')
61
+ this.addHeadshotButton.contentEditable = 'false'
62
+ this.addHeadshotButton.innerHTML = addAuthorIcon
63
+ const buttonText = document.createElement('span')
64
+ buttonText.classList.add('add-headshot-element-text')
65
+ buttonText.innerText = 'Add Headshot'
66
+ this.addHeadshotButton.appendChild(buttonText)
67
+ this.addHeadshotButton.addEventListener('click', this.handleAddHeadshot)
68
+ this.addHeadshotButton.addEventListener(
69
+ 'keydown',
70
+ handleEnterKey(this.handleAddHeadshot)
71
+ )
72
+ this.container.appendChild(this.addHeadshotButton)
73
+ }
74
+
75
+ this.dom.appendChild(this.container)
76
+ }
77
+
78
+ private handleAddHeadshot = () => {
79
+ const { state, dispatch } = this.view
80
+ const { tr } = state
81
+
82
+ const headshotElementNode =
83
+ schema.nodes.headshot_element.createAndFill() as HeadshotElementNode
84
+ const insertPos = this.getPos() + 1
85
+ tr.insert(insertPos, headshotElementNode)
86
+ this.view.focus()
87
+ tr.setSelection(TextSelection.create(tr.doc, insertPos))
88
+ dispatch(tr.scrollIntoView())
89
+ }
90
+
91
+ public destroy() {
92
+ this.addHeadshotButton?.removeEventListener('click', this.handleAddHeadshot)
93
+ this.removeKeydownListener?.()
94
+ super.destroy()
95
+ }
96
+ }
97
+
98
+ export default createNodeView(HeadshotGridView)
@@ -0,0 +1,21 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { createEditableNodeView } from './creators'
18
+ import { EditableBlock } from './editable_block'
19
+ import { HeadshotGridView } from './headshot_grid'
20
+
21
+ export default createEditableNodeView(EditableBlock(HeadshotGridView))
@@ -0,0 +1,93 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ addTrackChangesAttributes,
19
+ addTrackChangesClassNames,
20
+ } from '@manuscripts/track-changes-plugin'
21
+
22
+ import { cameraIcon, plusIcon } from '../icons'
23
+ import { handleEnterKey } from '../lib/navigation-utils'
24
+ import { createEditableNodeView } from './creators'
25
+ import { FigureEditableView } from './figure_editable'
26
+
27
+ export class HeadshotImageEditableView extends FigureEditableView {
28
+ private detachImageButton: HTMLButtonElement
29
+
30
+ public ignoreMutation = () => true
31
+
32
+ override createDOM() {
33
+ this.dom = document.createElement('figure')
34
+ this.container = document.createElement('div')
35
+ this.container.className = 'headshot-figure'
36
+ this.container.contentEditable = 'false'
37
+ this.dom.appendChild(this.container)
38
+ }
39
+
40
+ override addTools() {
41
+ const src = this.node.attrs.src
42
+ const files = this.props.getFiles()
43
+ const file = src && files.filter((f) => f.id === src)[0]
44
+ const link = file && this.props.fileManagement.previewLink(file)
45
+ const can = this.props.getCapabilities()
46
+ if (can.detachFile && link) {
47
+ this.detachImageButton = document.createElement('button')
48
+ this.detachImageButton.innerHTML = plusIcon
49
+ this.detachImageButton.classList.add('headshot-remove-button')
50
+ this.detachImageButton.setAttribute('data-cy', 'headshot-image-detach')
51
+ this.detachImageButton.contentEditable = 'false'
52
+ this.detachImageButton.addEventListener('click', this.handleDetachImage)
53
+ this.detachImageButton.addEventListener(
54
+ 'keydown',
55
+ handleEnterKey(this.handleDetachImage)
56
+ )
57
+ this.container.appendChild(this.detachImageButton)
58
+ }
59
+ }
60
+
61
+ public override updateContents() {
62
+ super.updateContents()
63
+ addTrackChangesAttributes(this.node.attrs, this.dom)
64
+ addTrackChangesClassNames(this.node.attrs, this.dom)
65
+ }
66
+
67
+ protected override createPlaceholder = () => {
68
+ const element = document.createElement('div')
69
+ element.classList.add('figure', 'placeholder')
70
+ element.tabIndex = 0
71
+
72
+ const instructions = document.createElement('div')
73
+ instructions.classList.add('instructions')
74
+ instructions.innerHTML = `${cameraIcon}<div>Upload<br>photo</div>`
75
+
76
+ element.appendChild(instructions)
77
+
78
+ return element
79
+ }
80
+
81
+ private handleDetachImage = () => {
82
+ const { state, dispatch } = this.view
83
+ const pos = this.getPos()
84
+ dispatch(state.tr.delete(pos, pos + this.node.nodeSize))
85
+ }
86
+
87
+ public destroy() {
88
+ this.detachImageButton?.removeEventListener('click', this.handleDetachImage)
89
+ super.destroy()
90
+ }
91
+ }
92
+
93
+ export default createEditableNodeView(HeadshotImageEditableView)
@@ -1052,7 +1052,7 @@ figure.block:has(.equation.selected-suggestion) {
1052
1052
  .ProseMirror
1053
1053
  [data-track-op='set_attrs']
1054
1054
  .block:not(.trans-abstract):not(.trans-graphical-abstract),
1055
- .tracking-visible .block:has(figure[data-track-op='set_attrs']),
1055
+ .tracking-visible .block:has(figure[data-track-op='set_attrs']):not(.headshot-grid-container),
1056
1056
  .tracking-visible figure.block:has(.equation.set_attrs),
1057
1057
  .tracking-visible
1058
1058
  .ProseMirror
@@ -1761,6 +1761,174 @@ th:hover > .table-context-menu-button,
1761
1761
  .hero-image-container .position-menu {
1762
1762
  display: none !important;
1763
1763
  }
1764
+
1765
+ /* Headshot Grid Container */
1766
+
1767
+ .ProseMirror .block-headshot_grid > .block {
1768
+ display: grid;
1769
+ gap: 16px;
1770
+ margin: 0 12px;
1771
+ padding: 8px 10px 16px 10px;
1772
+ border-radius: 4px;
1773
+ border: 1px solid #F2F2F2;
1774
+ }
1775
+
1776
+ .ProseMirror .headshot-grid-block {
1777
+ display: grid;
1778
+ gap: 16px;
1779
+ }
1780
+
1781
+ .ProseMirror .headshot-grid-container .headshot-grid-block .headshot-element {
1782
+ border-radius: 4px;
1783
+ border: 1px solid #E2E2E2 !important;
1784
+ background: #FFF;
1785
+ position: relative;
1786
+ }
1787
+
1788
+
1789
+ /* Headshot Element Block */
1790
+
1791
+ .ProseMirror .headshot-element-block {
1792
+ display: grid;
1793
+ grid-template-columns: auto 1fr;
1794
+ grid-template-rows: auto 1fr;
1795
+ column-gap: 16px;
1796
+ row-gap: 8px;
1797
+ padding: 16px 24px;
1798
+ }
1799
+
1800
+ .ProseMirror .headshot-element-block figure {
1801
+ grid-row: 1 / span 2;
1802
+ margin: 0;
1803
+ }
1804
+
1805
+ /* Headshot Image */
1806
+
1807
+ .ProseMirror .headshot-figure {
1808
+ width: 84px;
1809
+ height: 84px;
1810
+ position: relative;
1811
+ }
1812
+
1813
+ .ProseMirror .headshot-figure .figure-image {
1814
+ border-radius: 8px;
1815
+ }
1816
+
1817
+ .ProseMirror .headshot-element-block .figure.placeholder {
1818
+ min-width: 84px;
1819
+ min-height: 84px;
1820
+ padding: 0;
1821
+ border-radius: 8px;
1822
+ background: #f2f2f2;
1823
+ white-space: pre-line;
1824
+ }
1825
+
1826
+ /* Headshot Captions */
1827
+
1828
+ .ProseMirror .headshot-element-block .caption-title,
1829
+ .ProseMirror .headshot-element-block .caption-description {
1830
+ text-align: start;
1831
+ font-size: 12px;
1832
+ font-style: normal;
1833
+ line-height: 16px;
1834
+ }
1835
+
1836
+ .ProseMirror .headshot-element-block .caption-title {
1837
+ font-weight: 700;
1838
+ padding-top: 6px;
1839
+ }
1840
+
1841
+ .ProseMirror .headshot-element-block .caption-description,
1842
+ .ProseMirror .headshot-element-block .caption-title .placeholder-text {
1843
+ font-weight: 400;
1844
+ }
1845
+
1846
+ .ProseMirror .headshot-element-block .caption-title .placeholder-text::before {
1847
+ content: 'Name...';
1848
+ color: #c9c9c9;
1849
+ }
1850
+
1851
+ .ProseMirror .headshot-element-block .caption-description .placeholder-text::before {
1852
+ content: 'Summary...';
1853
+ color: #c9c9c9;
1854
+ }
1855
+
1856
+ /* Add Headshot Button */
1857
+
1858
+ .ProseMirror .add-headshot-element-button {
1859
+ border-radius: 4px;
1860
+ border: 1px dashed #E2E2E2;
1861
+ background: #FAFAFA;
1862
+ padding: 8px 24px;
1863
+ display: inline-flex;
1864
+ justify-content: center;
1865
+ align-items: center;
1866
+ gap: 8px;
1867
+ cursor: pointer;
1868
+ }
1869
+
1870
+ .ProseMirror .add-headshot-element-text {
1871
+ color: #353535;
1872
+ text-align: center;
1873
+ font-size: 10px;
1874
+ font-style: normal;
1875
+ font-weight: 400;
1876
+ line-height: 16px;
1877
+ }
1878
+
1879
+ .ProseMirror .add-headshot-element-button svg rect {
1880
+ fill: #6e6e6e;
1881
+ }
1882
+
1883
+ /* Headshot Remove Buttons */
1884
+
1885
+ .ProseMirror .headshot-element .headshot-remove-button {
1886
+ background: #ffffff;
1887
+ padding: 4px;
1888
+ margin: 8px;
1889
+ cursor: pointer;
1890
+ border: 1px solid #e2e2e2;
1891
+ border-radius: 50%;
1892
+ box-sizing: border-box;
1893
+ display: flex;
1894
+ align-items: center;
1895
+ justify-content: center;
1896
+ position: absolute;
1897
+ top: 0;
1898
+ right: 0;
1899
+ visibility: hidden;
1900
+ z-index: 10;
1901
+ }
1902
+
1903
+ .ProseMirror .headshot-figure .headshot-remove-button {
1904
+ margin: 0;
1905
+ top: -6%;
1906
+ right: -4%;
1907
+ }
1908
+
1909
+ .ProseMirror .headshot-element .headshot-remove-button svg {
1910
+ width: 12px;
1911
+ height: 12px;
1912
+ transform: rotate(45deg);
1913
+ }
1914
+
1915
+ .ProseMirror .headshot-figure .headshot-remove-button svg {
1916
+ width: 8px;
1917
+ height: 8px;
1918
+ }
1919
+
1920
+ .ProseMirror .headshot-element:hover .block > .headshot-remove-button,
1921
+ .ProseMirror .headshot-figure:hover .headshot-remove-button,
1922
+ .ProseMirror .headshot-element:focus-within .headshot-remove-button {
1923
+ visibility: visible;
1924
+ }
1925
+
1926
+ .ProseMirror .headshot-element:focus-visible,
1927
+ .ProseMirror .add-headshot-element-button:focus-visible,
1928
+ .ProseMirror .headshot-remove-button:focus-visible {
1929
+ outline: 3px solid var(--focus-outline-color);
1930
+ }
1931
+
1764
1932
  .toggle-btn {
1765
1933
  cursor: pointer;
1766
1934
  }