@manuscripts/body-editor 3.13.17 → 3.13.19

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.
package/dist/cjs/icons.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ORCIDIcon = exports.tickIcon = exports.fileMainDocumentIcon = exports.linkIcon = exports.translateIcon = exports.draggableIcon = exports.fileCorruptedIcon = exports.imageDefaultIcon = exports.imageLeftIcon = exports.imageRightIcon = exports.plusIcon = exports.lockIcon = exports.scrollIcon = exports.sectionCategoryIcon = exports.editIcon = exports.deleteIcon = exports.cameraIcon = exports.alertIcon = exports.arrowUp = exports.arrowDown = exports.addAuthorIcon = void 0;
3
+ exports.ORCIDIcon = exports.tickIcon = exports.fileMainDocumentIcon = exports.leadingHalfLeftIcon = exports.leadingWallpaperIcon = exports.leadingLargeFloatIcon = exports.leadingSmallFloatIcon = exports.leadingHeroImageIcon = exports.linkIcon = exports.translateIcon = exports.draggableIcon = exports.fileCorruptedIcon = exports.imageDefaultIcon = exports.imageLeftIcon = exports.imageRightIcon = exports.plusIcon = exports.lockIcon = exports.scrollIcon = exports.sectionCategoryIcon = exports.editIcon = exports.deleteIcon = exports.cameraIcon = exports.alertIcon = exports.arrowUp = exports.arrowDown = exports.addAuthorIcon = void 0;
4
4
  const style_guide_1 = require("@manuscripts/style-guide");
5
5
  const react_1 = require("react");
6
6
  const server_1 = require("react-dom/server");
@@ -23,6 +23,11 @@ exports.fileCorruptedIcon = renderIcon(style_guide_1.FileCorruptedIcon);
23
23
  exports.draggableIcon = renderIcon(style_guide_1.DraggableIcon);
24
24
  exports.translateIcon = renderIcon(style_guide_1.TranslateIcon);
25
25
  exports.linkIcon = renderIcon(style_guide_1.LinkIcon);
26
+ exports.leadingHeroImageIcon = renderIcon(style_guide_1.LeadingHeroImageIcon);
27
+ exports.leadingSmallFloatIcon = renderIcon(style_guide_1.LeadingSmallFloatIcon);
28
+ exports.leadingLargeFloatIcon = renderIcon(style_guide_1.LeadingLargeFloatIcon);
29
+ exports.leadingWallpaperIcon = renderIcon(style_guide_1.LeadingWallpaperIcon);
30
+ exports.leadingHalfLeftIcon = renderIcon(style_guide_1.LeadingHalfLeftIcon);
26
31
  exports.fileMainDocumentIcon = renderIcon(style_guide_1.FileMainDocumentIcon);
27
32
  exports.tickIcon = renderIcon(style_guide_1.TickIcon);
28
33
  exports.ORCIDIcon = renderIcon(style_guide_1.ORCIDIcon);
@@ -207,6 +207,7 @@ const getDecorationPos = (node, pos) => {
207
207
  case transform_1.schema.nodes.contributors:
208
208
  case transform_1.schema.nodes.hero_image:
209
209
  case transform_1.schema.nodes.supplements:
210
+ case transform_1.schema.nodes.headshot_grid:
210
211
  return pos;
211
212
  case transform_1.schema.nodes.keywords:
212
213
  return pos + 2;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MATHJAX_VERSION = exports.VERSION = void 0;
4
- exports.VERSION = '3.13.17';
4
+ exports.VERSION = '3.13.19';
5
5
  exports.MATHJAX_VERSION = '3.2.2';
@@ -23,21 +23,67 @@ const icons_1 = require("../icons");
23
23
  const navigation_utils_1 = require("../lib/navigation-utils");
24
24
  const block_view_1 = __importDefault(require("./block_view"));
25
25
  const creators_1 = require("./creators");
26
+ const LAYOUT_OPTIONS_CONFIG = [
27
+ {
28
+ label: 'Hero Image',
29
+ icon: icons_1.leadingHeroImageIcon,
30
+ attr: 'leading',
31
+ },
32
+ {
33
+ label: 'Float Image - Small',
34
+ icon: icons_1.leadingSmallFloatIcon,
35
+ attr: 'leading-small-float',
36
+ },
37
+ {
38
+ label: 'Float Image - Large',
39
+ icon: icons_1.leadingLargeFloatIcon,
40
+ attr: 'leading-large-float',
41
+ },
42
+ {
43
+ label: 'Wallpaper Image',
44
+ icon: icons_1.leadingWallpaperIcon,
45
+ attr: 'leading-wallpaper',
46
+ },
47
+ {
48
+ label: 'Half Image',
49
+ icon: icons_1.leadingHalfLeftIcon,
50
+ attr: 'leading-half-left',
51
+ },
52
+ ];
26
53
  class HeroImageView extends block_view_1.default {
27
54
  constructor() {
28
55
  super(...arguments);
29
56
  this.collapsed = false;
57
+ this.layoutPanelCollapsed = false;
30
58
  this.ignoreMutation = () => true;
31
59
  this.createElement = () => {
32
60
  this.container = document.createElement('div');
33
61
  this.container.classList.add('block', 'hero-image-container');
34
62
  this.dom.appendChild(this.container);
63
+ this.abortController = new AbortController();
35
64
  this.container.appendChild(this.createPanel());
65
+ this.container.appendChild(this.createLayoutPanel());
66
+ this.layoutOptions = this.createLayoutOptions();
67
+ this.layoutOptions.addEventListener('change', this.onSelectOption, {
68
+ signal: this.abortController.signal,
69
+ });
70
+ this.container.appendChild(this.layoutOptions);
36
71
  this.contentDOM = document.createElement('figure');
37
72
  this.contentDOM.classList.add('figure-block', 'hero-image-figure');
38
73
  this.contentDOM.setAttribute('id', this.node.attrs.id);
39
74
  this.container.appendChild(this.contentDOM);
40
75
  };
76
+ this.onSelectOption = (e) => {
77
+ const target = e.target;
78
+ if (target && target.type === 'radio' && target.name === 'layout-option') {
79
+ const { state, dispatch } = this.view;
80
+ const tr = state.tr.setNodeMarkup(this.getPos(), undefined, {
81
+ ...this.node.attrs,
82
+ type: target.value,
83
+ });
84
+ dispatch(tr);
85
+ }
86
+ };
41
87
  }
42
88
  createPanel() {
43
89
  const panel = document.createElement('div');
@@ -57,11 +103,72 @@ class HeroImageView extends block_view_1.default {
57
103
  heroImageToggleBtn.classList.toggle('collapsed', this.collapsed);
58
104
  };
59
105
  heroImageToggleBtn.onclick = handleToggle;
60
- heroImageToggleBtn.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)(handleToggle));
106
+ heroImageToggleBtn.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)(handleToggle), {
107
+ signal: this.abortController.signal,
108
+ });
61
109
  panel.appendChild(label);
62
110
  panel.appendChild(heroImageToggleBtn);
63
111
  return panel;
64
112
  }
113
+ createLayoutPanel() {
114
+ const panel = document.createElement('div');
115
+ panel.classList.add('panel-header', 'layout-header');
116
+ const label = document.createElement('span');
117
+ label.textContent = 'Layout';
118
+ label.contentEditable = 'false';
119
+ const toggleBtn = document.createElement('button');
120
+ toggleBtn.classList.add('toggle-btn', 'button-reset');
121
+ toggleBtn.innerHTML = icons_1.arrowUp;
122
+ toggleBtn.classList.toggle('collapsed', this.layoutPanelCollapsed);
123
+ const handleToggle = () => {
124
+ this.layoutPanelCollapsed = !this.layoutPanelCollapsed;
125
+ if (this.layoutOptions) {
126
+ this.layoutOptions.style.display = this.layoutPanelCollapsed
127
+ ? 'none'
128
+ : '';
129
+ }
130
+ toggleBtn.classList.toggle('collapsed', this.layoutPanelCollapsed);
131
+ };
132
+ toggleBtn.onclick = handleToggle;
133
+ toggleBtn.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)(handleToggle), {
134
+ signal: this.abortController.signal,
135
+ });
136
+ panel.appendChild(label);
137
+ panel.appendChild(toggleBtn);
138
+ return panel;
139
+ }
140
+ createLayoutOptions() {
141
+ const can = this.props.getCapabilities();
142
+ const optionsGroup = document.createElement('div');
143
+ optionsGroup.className = 'layout-options-group';
144
+ LAYOUT_OPTIONS_CONFIG.forEach(({ label, icon, attr }) => {
145
+ const image = document.createElement('span');
146
+ image.className = 'layout-option-image';
147
+ image.innerHTML = icon;
148
+ const input = document.createElement('input');
149
+ input.type = 'radio';
150
+ input.name = 'layout-option';
151
+ input.value = attr;
152
+ input.disabled = !can.editMetadata;
153
+ input.checked = attr === this.node.attrs.type;
154
+ const inputLabel = document.createElement('span');
155
+ inputLabel.className = 'layout-option-label';
156
+ inputLabel.textContent = label;
157
+ const wrapper = document.createElement('span');
158
+ wrapper.className = 'layout-option-label-wrapper';
159
+ wrapper.append(input, inputLabel);
160
+ const container = document.createElement('div');
161
+ container.className = 'layout-option';
162
+ container.append(image, wrapper);
163
+ optionsGroup.appendChild(container);
164
+ });
165
+ optionsGroup.contentEditable = 'false';
166
+ return optionsGroup;
167
+ }
168
+ destroy() {
169
+ this.abortController?.abort();
170
+ super.destroy();
171
+ }
65
172
  }
66
173
  exports.HeroImageView = HeroImageView;
67
174
  exports.default = (0, creators_1.createNodeView)(HeroImageView);
package/dist/es/icons.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AddAuthorIcon, AlertIcon, ArrowDownCircleIcon, ArrowUpIcon, CameraIcon, DeleteIcon, DraggableIcon, EditIcon, FileCorruptedIcon, FileMainDocumentIcon, ImageDefaultIcon, ImageLeftIcon, ImageRightIcon, LinkIcon, LockIcon, ORCIDIcon as ORCID, PlusIcon, ScrollIcon, SectionCategoryIcon, TickIcon, TranslateIcon, } from '@manuscripts/style-guide';
1
+ import { AddAuthorIcon, AlertIcon, ArrowDownCircleIcon, ArrowUpIcon, CameraIcon, DeleteIcon, DraggableIcon, EditIcon, FileCorruptedIcon, FileMainDocumentIcon, ImageDefaultIcon, ImageLeftIcon, ImageRightIcon, LinkIcon, LockIcon, LeadingHalfLeftIcon, LeadingHeroImageIcon, LeadingLargeFloatIcon, LeadingSmallFloatIcon, LeadingWallpaperIcon, ORCIDIcon as ORCID, PlusIcon, ScrollIcon, SectionCategoryIcon, TickIcon, TranslateIcon, } from '@manuscripts/style-guide';
2
2
  import { createElement } from 'react';
3
3
  import { renderToStaticMarkup } from 'react-dom/server';
4
4
  const renderIcon = (c) => renderToStaticMarkup(createElement(c));
@@ -20,6 +20,11 @@ export const fileCorruptedIcon = renderIcon(FileCorruptedIcon);
20
20
  export const draggableIcon = renderIcon(DraggableIcon);
21
21
  export const translateIcon = renderIcon(TranslateIcon);
22
22
  export const linkIcon = renderIcon(LinkIcon);
23
+ export const leadingHeroImageIcon = renderIcon(LeadingHeroImageIcon);
24
+ export const leadingSmallFloatIcon = renderIcon(LeadingSmallFloatIcon);
25
+ export const leadingLargeFloatIcon = renderIcon(LeadingLargeFloatIcon);
26
+ export const leadingWallpaperIcon = renderIcon(LeadingWallpaperIcon);
27
+ export const leadingHalfLeftIcon = renderIcon(LeadingHalfLeftIcon);
23
28
  export const fileMainDocumentIcon = renderIcon(FileMainDocumentIcon);
24
29
  export const tickIcon = renderIcon(TickIcon);
25
30
  export const ORCIDIcon = renderIcon(ORCID);
@@ -202,6 +202,7 @@ const getDecorationPos = (node, pos) => {
202
202
  case schema.nodes.contributors:
203
203
  case schema.nodes.hero_image:
204
204
  case schema.nodes.supplements:
205
+ case schema.nodes.headshot_grid:
205
206
  return pos;
206
207
  case schema.nodes.keywords:
207
208
  return pos + 2;
@@ -1,2 +1,2 @@
1
- export const VERSION = '3.13.17';
1
+ export const VERSION = '3.13.19';
2
2
  export const MATHJAX_VERSION = '3.2.2';
@@ -13,25 +13,71 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { arrowUp } from '../icons';
16
+ import { arrowUp, leadingHalfLeftIcon, leadingHeroImageIcon, leadingLargeFloatIcon, leadingSmallFloatIcon, leadingWallpaperIcon, } from '../icons';
17
17
  import { handleEnterKey } from '../lib/navigation-utils';
18
18
  import BlockView from './block_view';
19
19
  import { createNodeView } from './creators';
20
+ const LAYOUT_OPTIONS_CONFIG = [
21
+ {
22
+ label: 'Hero Image',
23
+ icon: leadingHeroImageIcon,
24
+ attr: 'leading',
25
+ },
26
+ {
27
+ label: 'Float Image - Small',
28
+ icon: leadingSmallFloatIcon,
29
+ attr: 'leading-small-float',
30
+ },
31
+ {
32
+ label: 'Float Image - Large',
33
+ icon: leadingLargeFloatIcon,
34
+ attr: 'leading-large-float',
35
+ },
36
+ {
37
+ label: 'Wallpaper Image',
38
+ icon: leadingWallpaperIcon,
39
+ attr: 'leading-wallpaper',
40
+ },
41
+ {
42
+ label: 'Half Image',
43
+ icon: leadingHalfLeftIcon,
44
+ attr: 'leading-half-left',
45
+ },
46
+ ];
20
47
  export class HeroImageView extends BlockView {
21
48
  constructor() {
22
49
  super(...arguments);
23
50
  this.collapsed = false;
51
+ this.layoutPanelCollapsed = false;
24
52
  this.ignoreMutation = () => true;
25
53
  this.createElement = () => {
26
54
  this.container = document.createElement('div');
27
55
  this.container.classList.add('block', 'hero-image-container');
28
56
  this.dom.appendChild(this.container);
57
+ this.abortController = new AbortController();
29
58
  this.container.appendChild(this.createPanel());
59
+ this.container.appendChild(this.createLayoutPanel());
60
+ this.layoutOptions = this.createLayoutOptions();
61
+ this.layoutOptions.addEventListener('change', this.onSelectOption, {
62
+ signal: this.abortController.signal,
63
+ });
64
+ this.container.appendChild(this.layoutOptions);
30
65
  this.contentDOM = document.createElement('figure');
31
66
  this.contentDOM.classList.add('figure-block', 'hero-image-figure');
32
67
  this.contentDOM.setAttribute('id', this.node.attrs.id);
33
68
  this.container.appendChild(this.contentDOM);
34
69
  };
70
+ this.onSelectOption = (e) => {
71
+ const target = e.target;
72
+ if (target && target.type === 'radio' && target.name === 'layout-option') {
73
+ const { state, dispatch } = this.view;
74
+ const tr = state.tr.setNodeMarkup(this.getPos(), undefined, {
75
+ ...this.node.attrs,
76
+ type: target.value,
77
+ });
78
+ dispatch(tr);
79
+ }
80
+ };
35
81
  }
36
82
  createPanel() {
37
83
  const panel = document.createElement('div');
@@ -51,10 +97,71 @@ export class HeroImageView extends BlockView {
51
97
  heroImageToggleBtn.classList.toggle('collapsed', this.collapsed);
52
98
  };
53
99
  heroImageToggleBtn.onclick = handleToggle;
54
- heroImageToggleBtn.addEventListener('keydown', handleEnterKey(handleToggle));
100
+ heroImageToggleBtn.addEventListener('keydown', handleEnterKey(handleToggle), {
101
+ signal: this.abortController.signal,
102
+ });
55
103
  panel.appendChild(label);
56
104
  panel.appendChild(heroImageToggleBtn);
57
105
  return panel;
58
106
  }
107
+ createLayoutPanel() {
108
+ const panel = document.createElement('div');
109
+ panel.classList.add('panel-header', 'layout-header');
110
+ const label = document.createElement('span');
111
+ label.textContent = 'Layout';
112
+ label.contentEditable = 'false';
113
+ const toggleBtn = document.createElement('button');
114
+ toggleBtn.classList.add('toggle-btn', 'button-reset');
115
+ toggleBtn.innerHTML = arrowUp;
116
+ toggleBtn.classList.toggle('collapsed', this.layoutPanelCollapsed);
117
+ const handleToggle = () => {
118
+ this.layoutPanelCollapsed = !this.layoutPanelCollapsed;
119
+ if (this.layoutOptions) {
120
+ this.layoutOptions.style.display = this.layoutPanelCollapsed
121
+ ? 'none'
122
+ : '';
123
+ }
124
+ toggleBtn.classList.toggle('collapsed', this.layoutPanelCollapsed);
125
+ };
126
+ toggleBtn.onclick = handleToggle;
127
+ toggleBtn.addEventListener('keydown', handleEnterKey(handleToggle), {
128
+ signal: this.abortController.signal,
129
+ });
130
+ panel.appendChild(label);
131
+ panel.appendChild(toggleBtn);
132
+ return panel;
133
+ }
134
+ createLayoutOptions() {
135
+ const can = this.props.getCapabilities();
136
+ const optionsGroup = document.createElement('div');
137
+ optionsGroup.className = 'layout-options-group';
138
+ LAYOUT_OPTIONS_CONFIG.forEach(({ label, icon, attr }) => {
139
+ const image = document.createElement('span');
140
+ image.className = 'layout-option-image';
141
+ image.innerHTML = icon;
142
+ const input = document.createElement('input');
143
+ input.type = 'radio';
144
+ input.name = 'layout-option';
145
+ input.value = attr;
146
+ input.disabled = !can.editMetadata;
147
+ input.checked = attr === this.node.attrs.type;
148
+ const inputLabel = document.createElement('span');
149
+ inputLabel.className = 'layout-option-label';
150
+ inputLabel.textContent = label;
151
+ const wrapper = document.createElement('span');
152
+ wrapper.className = 'layout-option-label-wrapper';
153
+ wrapper.append(input, inputLabel);
154
+ const container = document.createElement('div');
155
+ container.className = 'layout-option';
156
+ container.append(image, wrapper);
157
+ optionsGroup.appendChild(container);
158
+ });
159
+ optionsGroup.contentEditable = 'false';
160
+ return optionsGroup;
161
+ }
162
+ destroy() {
163
+ this.abortController?.abort();
164
+ super.destroy();
165
+ }
59
166
  }
60
167
  export default createNodeView(HeroImageView);
@@ -16,6 +16,11 @@ export declare const fileCorruptedIcon: string;
16
16
  export declare const draggableIcon: string;
17
17
  export declare const translateIcon: string;
18
18
  export declare const linkIcon: string;
19
+ export declare const leadingHeroImageIcon: string;
20
+ export declare const leadingSmallFloatIcon: string;
21
+ export declare const leadingLargeFloatIcon: string;
22
+ export declare const leadingWallpaperIcon: string;
23
+ export declare const leadingHalfLeftIcon: string;
19
24
  export declare const fileMainDocumentIcon: string;
20
25
  export declare const tickIcon: string;
21
26
  export declare const ORCIDIcon: string;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.13.17";
1
+ export declare const VERSION = "3.13.19";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";
@@ -13,15 +13,22 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { FigureElementNode } from '@manuscripts/transform';
16
+ import { HeroImageNode } from '@manuscripts/transform';
17
17
  import { Trackable } from '../types';
18
18
  import BlockView from './block_view';
19
- export declare class HeroImageView extends BlockView<Trackable<FigureElementNode>> {
19
+ export declare class HeroImageView extends BlockView<Trackable<HeroImageNode>> {
20
20
  private container;
21
+ private layoutOptions;
22
+ private abortController;
21
23
  private collapsed;
24
+ private layoutPanelCollapsed;
22
25
  ignoreMutation: () => boolean;
23
26
  createElement: () => void;
24
27
  createPanel(): HTMLDivElement;
28
+ createLayoutPanel(): HTMLDivElement;
29
+ createLayoutOptions(): HTMLDivElement;
30
+ private onSelectOption;
31
+ destroy(): void;
25
32
  }
26
33
  declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<HeroImageView>;
27
34
  export default _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/body-editor",
3
3
  "description": "Prosemirror components for editing and viewing manuscripts",
4
- "version": "3.13.17",
4
+ "version": "3.13.19",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -41,7 +41,7 @@
41
41
  "@citation-js/plugin-pubmed": "0.3.0",
42
42
  "@citation-js/plugin-ris": "0.7.18",
43
43
  "@iarna/word-count": "1.1.2",
44
- "@manuscripts/style-guide": "3.6.5",
44
+ "@manuscripts/style-guide": "3.6.6",
45
45
  "@manuscripts/track-changes-plugin": "2.4.0",
46
46
  "@manuscripts/transform": "4.4.6",
47
47
  "@popperjs/core": "2.11.8",
package/src/icons.ts CHANGED
@@ -29,6 +29,11 @@ import {
29
29
  ImageRightIcon,
30
30
  LinkIcon,
31
31
  LockIcon,
32
+ LeadingHalfLeftIcon,
33
+ LeadingHeroImageIcon,
34
+ LeadingLargeFloatIcon,
35
+ LeadingSmallFloatIcon,
36
+ LeadingWallpaperIcon,
32
37
  ORCIDIcon as ORCID,
33
38
  PlusIcon,
34
39
  ScrollIcon,
@@ -59,6 +64,11 @@ export const fileCorruptedIcon = renderIcon(FileCorruptedIcon)
59
64
  export const draggableIcon = renderIcon(DraggableIcon)
60
65
  export const translateIcon = renderIcon(TranslateIcon)
61
66
  export const linkIcon = renderIcon(LinkIcon)
67
+ export const leadingHeroImageIcon = renderIcon(LeadingHeroImageIcon)
68
+ export const leadingSmallFloatIcon = renderIcon(LeadingSmallFloatIcon)
69
+ export const leadingLargeFloatIcon = renderIcon(LeadingLargeFloatIcon)
70
+ export const leadingWallpaperIcon = renderIcon(LeadingWallpaperIcon)
71
+ export const leadingHalfLeftIcon = renderIcon(LeadingHalfLeftIcon)
62
72
  export const fileMainDocumentIcon = renderIcon(FileMainDocumentIcon)
63
73
  export const tickIcon = renderIcon(TickIcon)
64
74
  export const ORCIDIcon = renderIcon(ORCID)
@@ -283,6 +283,7 @@ const getDecorationPos = (node: ManuscriptNode, pos: number) => {
283
283
  case schema.nodes.contributors:
284
284
  case schema.nodes.hero_image:
285
285
  case schema.nodes.supplements:
286
+ case schema.nodes.headshot_grid:
286
287
  return pos
287
288
  case schema.nodes.keywords:
288
289
  return pos + 2
package/src/versions.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  //THIS FILE WAS GENERATED BY versions.mjs
2
- export const VERSION = '3.13.17';
2
+ export const VERSION = '3.13.19';
3
3
  export const MATHJAX_VERSION = '3.2.2';
@@ -14,17 +14,55 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { FigureElementNode } from '@manuscripts/transform'
18
-
19
- import { arrowUp } from '../icons'
17
+ import { HeroImageNode } from '@manuscripts/transform'
18
+
19
+ import {
20
+ arrowUp,
21
+ leadingHalfLeftIcon,
22
+ leadingHeroImageIcon,
23
+ leadingLargeFloatIcon,
24
+ leadingSmallFloatIcon,
25
+ leadingWallpaperIcon,
26
+ } from '../icons'
20
27
  import { handleEnterKey } from '../lib/navigation-utils'
21
28
  import { Trackable } from '../types'
22
29
  import BlockView from './block_view'
23
30
  import { createNodeView } from './creators'
24
31
 
25
- export class HeroImageView extends BlockView<Trackable<FigureElementNode>> {
32
+ const LAYOUT_OPTIONS_CONFIG = [
33
+ {
34
+ label: 'Hero Image',
35
+ icon: leadingHeroImageIcon,
36
+ attr: 'leading',
37
+ },
38
+ {
39
+ label: 'Float Image - Small',
40
+ icon: leadingSmallFloatIcon,
41
+ attr: 'leading-small-float',
42
+ },
43
+ {
44
+ label: 'Float Image - Large',
45
+ icon: leadingLargeFloatIcon,
46
+ attr: 'leading-large-float',
47
+ },
48
+ {
49
+ label: 'Wallpaper Image',
50
+ icon: leadingWallpaperIcon,
51
+ attr: 'leading-wallpaper',
52
+ },
53
+ {
54
+ label: 'Half Image',
55
+ icon: leadingHalfLeftIcon,
56
+ attr: 'leading-half-left',
57
+ },
58
+ ]
59
+
60
+ export class HeroImageView extends BlockView<Trackable<HeroImageNode>> {
26
61
  private container: HTMLElement
62
+ private layoutOptions: HTMLElement
63
+ private abortController: AbortController
27
64
  private collapsed = false
65
+ private layoutPanelCollapsed = false
28
66
 
29
67
  public ignoreMutation = () => true
30
68
 
@@ -33,7 +71,17 @@ export class HeroImageView extends BlockView<Trackable<FigureElementNode>> {
33
71
  this.container.classList.add('block', 'hero-image-container')
34
72
  this.dom.appendChild(this.container)
35
73
 
74
+ this.abortController = new AbortController()
75
+
36
76
  this.container.appendChild(this.createPanel())
77
+ this.container.appendChild(this.createLayoutPanel())
78
+
79
+ this.layoutOptions = this.createLayoutOptions()
80
+ this.layoutOptions.addEventListener('change', this.onSelectOption, {
81
+ signal: this.abortController.signal,
82
+ })
83
+
84
+ this.container.appendChild(this.layoutOptions)
37
85
 
38
86
  this.contentDOM = document.createElement('figure')
39
87
  this.contentDOM.classList.add('figure-block', 'hero-image-figure')
@@ -65,12 +113,103 @@ export class HeroImageView extends BlockView<Trackable<FigureElementNode>> {
65
113
  }
66
114
 
67
115
  heroImageToggleBtn.onclick = handleToggle
68
- heroImageToggleBtn.addEventListener('keydown', handleEnterKey(handleToggle))
116
+ heroImageToggleBtn.addEventListener(
117
+ 'keydown',
118
+ handleEnterKey(handleToggle),
119
+ {
120
+ signal: this.abortController.signal,
121
+ }
122
+ )
69
123
 
70
124
  panel.appendChild(label)
71
125
  panel.appendChild(heroImageToggleBtn)
72
126
  return panel
73
127
  }
128
+
129
+ createLayoutPanel() {
130
+ const panel = document.createElement('div')
131
+ panel.classList.add('panel-header', 'layout-header')
132
+
133
+ const label = document.createElement('span')
134
+ label.textContent = 'Layout'
135
+ label.contentEditable = 'false'
136
+
137
+ const toggleBtn = document.createElement('button')
138
+ toggleBtn.classList.add('toggle-btn', 'button-reset')
139
+
140
+ toggleBtn.innerHTML = arrowUp
141
+ toggleBtn.classList.toggle('collapsed', this.layoutPanelCollapsed)
142
+
143
+ const handleToggle = () => {
144
+ this.layoutPanelCollapsed = !this.layoutPanelCollapsed
145
+ if (this.layoutOptions) {
146
+ this.layoutOptions.style.display = this.layoutPanelCollapsed
147
+ ? 'none'
148
+ : ''
149
+ }
150
+ toggleBtn.classList.toggle('collapsed', this.layoutPanelCollapsed)
151
+ }
152
+
153
+ toggleBtn.onclick = handleToggle
154
+ toggleBtn.addEventListener('keydown', handleEnterKey(handleToggle), {
155
+ signal: this.abortController.signal,
156
+ })
157
+
158
+ panel.appendChild(label)
159
+ panel.appendChild(toggleBtn)
160
+ return panel
161
+ }
162
+
163
+ createLayoutOptions() {
164
+ const can = this.props.getCapabilities()
165
+ const optionsGroup = document.createElement('div')
166
+ optionsGroup.className = 'layout-options-group'
167
+
168
+ LAYOUT_OPTIONS_CONFIG.forEach(({ label, icon, attr }) => {
169
+ const image = document.createElement('span')
170
+ image.className = 'layout-option-image'
171
+ image.innerHTML = icon
172
+
173
+ const input = document.createElement('input')
174
+ input.type = 'radio'
175
+ input.name = 'layout-option'
176
+ input.value = attr
177
+ input.disabled = !can.editMetadata
178
+ input.checked = attr === this.node.attrs.type
179
+
180
+ const inputLabel = document.createElement('span')
181
+ inputLabel.className = 'layout-option-label'
182
+ inputLabel.textContent = label
183
+
184
+ const wrapper = document.createElement('span')
185
+ wrapper.className = 'layout-option-label-wrapper'
186
+ wrapper.append(input, inputLabel)
187
+
188
+ const container = document.createElement('div')
189
+ container.className = 'layout-option'
190
+ container.append(image, wrapper)
191
+ optionsGroup.appendChild(container)
192
+ })
193
+ optionsGroup.contentEditable = 'false'
194
+ return optionsGroup
195
+ }
196
+
197
+ private onSelectOption = (e: Event) => {
198
+ const target = e.target as HTMLInputElement
199
+ if (target && target.type === 'radio' && target.name === 'layout-option') {
200
+ const { state, dispatch } = this.view
201
+ const tr = state.tr.setNodeMarkup(this.getPos(), undefined, {
202
+ ...this.node.attrs,
203
+ type: target.value,
204
+ })
205
+ dispatch(tr)
206
+ }
207
+ }
208
+
209
+ destroy() {
210
+ this.abortController?.abort()
211
+ super.destroy()
212
+ }
74
213
  }
75
214
 
76
215
  export default createNodeView(HeroImageView)
@@ -498,6 +498,10 @@ ProseMirror .block-embed .position-menu {
498
498
  right: 26px;
499
499
  }
500
500
 
501
+ .comment-marker:has(+ .block-headshot_grid) {
502
+ top: 0;
503
+ }
504
+
501
505
  .block-subtitles .comment-marker {
502
506
  top: 0 !important;
503
507
  left: 0 !important;
@@ -1750,6 +1754,63 @@ th:hover > .table-context-menu-button,
1750
1754
  border-bottom: 1px solid #c9c9c9;
1751
1755
  }
1752
1756
 
1757
+ .ProseMirror .layout-header {
1758
+ font-size: 18px;
1759
+ font-weight: 400;
1760
+ padding: 4px 3px 4px 0;
1761
+ margin: 8px;
1762
+ background: unset;
1763
+ border: none;
1764
+ }
1765
+
1766
+ .ProseMirror .layout-options-group {
1767
+ display: flex;
1768
+ justify-content: center;
1769
+ gap: 16px;
1770
+ padding: 16px 0;
1771
+ margin: 0 8px;
1772
+ border-bottom: 1px solid #e2e2e2;
1773
+ }
1774
+
1775
+ .ProseMirror .layout-option-image {
1776
+ width: 120px;
1777
+ height: 114px;
1778
+ margin-bottom: 8px;
1779
+ border-radius: 5px 5px 9px 9px;
1780
+ border: 1px solid #e2e2e2;
1781
+ background: #fafafa;
1782
+ display: flex;
1783
+ align-items: center;
1784
+ justify-content: center;
1785
+ }
1786
+
1787
+ .ProseMirror .layout-options-group .layout-option-label-wrapper {
1788
+ display: flex;
1789
+ width: 120px;
1790
+ align-items: flex-start;
1791
+ gap: 6px;
1792
+ }
1793
+
1794
+ .ProseMirror .layout-options-group input[type="radio"] {
1795
+ accent-color: #1a9bc7;
1796
+ width: 16px;
1797
+ height: 16px;
1798
+ }
1799
+
1800
+ .ProseMirror .layout-options-group input[type="radio"]:focus-visible {
1801
+ outline: 1px solid var(--focus-outline-color);
1802
+ outline-offset: 2px;
1803
+ }
1804
+
1805
+ .ProseMirror .layout-option-label {
1806
+ color: #353535;
1807
+ font-size: 14px;
1808
+ font-style: normal;
1809
+ font-weight: 400;
1810
+ line-height: 24px;
1811
+ user-select: none;
1812
+ }
1813
+
1753
1814
  .hero-image-container .figure-block {
1754
1815
  border: none !important;
1755
1816
  padding: 8px;