tiptap-extension-izzy-resizable-image 0.1.2 → 0.1.4

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 (3) hide show
  1. package/README.md +25 -12
  2. package/npm/index.js +73 -11
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Resizable inline image node for TipTap v2 with eight resize handles, optional alignment menu, drag-and-drop, and sensible defaults via `.extend()`.
4
4
 
5
- Demo: https://bernanr.github.io/TipTap-Extension-Izzy-Resizable-Image
5
+ Demo: https://startinn.github.io/TipTap-Extension-Izzy-Resizable-Image/
6
6
 
7
7
  ## Features
8
8
 
@@ -44,14 +44,14 @@ editor.commands.setResizableImageAlignment('center');
44
44
 
45
45
  ### Using from npm (bundler)
46
46
 
47
- ```bash
48
- npm install tiptap-extension-izzy-resizable-image @tiptap/core@^3 prosemirror-state
49
- ```
47
+ ```bash
48
+ npm install tiptap-extension-izzy-resizable-image @tiptap/core@^3 prosemirror-state
49
+ ```
50
50
 
51
51
  ```js
52
52
  import { Editor } from '@tiptap/core';
53
53
  import StarterKit from '@tiptap/starter-kit';
54
- import { TiptapIzzyExtensionResizableImage } from 'tiptap-extension-izzy-resizable-image';
54
+ import { TiptapIzzyExtensionResizableImage } from 'tiptap-extension-izzy-resizable-image';
55
55
 
56
56
  const editor = new Editor({
57
57
  element: document.querySelector('#editor'),
@@ -74,12 +74,23 @@ const editor = new Editor({
74
74
  height: 200,
75
75
  showAlignMenu: true,
76
76
  alignMenuPosition: 'below',
77
- alignMenuIcons: {
78
- left: '<i class="bi bi-text-left"></i>',
79
- center: '<i class="bi bi-text-center"></i>',
80
- right: '<i class="bi bi-text-right"></i>',
81
- clear: 'x',
82
- },
77
+ alignMenuIcons: {
78
+ left: '⟸',
79
+ center: '⇔',
80
+ right: '⟹',
81
+ clear: 'x',
82
+ preview: '🔍',
83
+ },
84
+ alignMenuButtonsHide: {
85
+ // hide specific buttons if true
86
+ // left: true,
87
+ // right: true,
88
+ // center: true,
89
+ // clear: true,
90
+ // preview: true,
91
+ // size50: true,
92
+ // size100: true,
93
+ },
83
94
  };
84
95
  },
85
96
  }),
@@ -95,7 +106,8 @@ editor.commands.insertResizableImage({ src: 'https://picsum.photos/300/200', wid
95
106
  - `height: number | null` — default height in pixels when node `height` attr is missing.
96
107
  - `showAlignMenu: boolean` — whether the inline alignment menu is shown by default.
97
108
  - `alignMenuPosition: 'above' | 'below'` — where the menu appears relative to the image.
98
- - `alignMenuIcons: { left: string, center: string, right: string, clear: string }` — HTML strings for menu buttons.
109
+ - `alignMenuIcons: { left: string, center: string, right: string, clear: string, preview: string }` — HTML strings for menu buttons.
110
+ - `alignMenuButtonsHide: Record<string, boolean>` — hide specific buttons when `true` (supports `left`, `center`, `right`, `clear`, `preview`, `size50`, `size100`).
99
111
 
100
112
  Reference:
101
113
  - Defined in `resizable-image.js:387-394`
@@ -118,6 +130,7 @@ These can be set per-node, overriding options.
118
130
  - `iconCenter: string | null`
119
131
  - `iconRight: string | null`
120
132
  - `iconClear: string | null`
133
+ - `iconView: string | null` — HTML or text for the preview (lightbox) button.
121
134
 
122
135
  Reference: `resizable-image.js:358-372`
123
136
 
package/npm/index.js CHANGED
@@ -84,21 +84,34 @@ class ResizableImageView {
84
84
  this.btnCenter = document.createElement('button')
85
85
  this.btnRight = document.createElement('button')
86
86
  this.btnClear = document.createElement('button')
87
+ this.btnView = document.createElement('button')
88
+ this.btnSize50 = document.createElement('button')
89
+ this.btnSize100 = document.createElement('button')
87
90
 
88
91
  this.btnLeft.innerHTML = (node.attrs.iconLeft != null ? node.attrs.iconLeft : (this.options.alignMenuIcons?.left ?? '⟸'))
89
92
  this.btnCenter.innerHTML = (node.attrs.iconCenter != null ? node.attrs.iconCenter : (this.options.alignMenuIcons?.center ?? '⇔'))
90
93
  this.btnRight.innerHTML = (node.attrs.iconRight != null ? node.attrs.iconRight : (this.options.alignMenuIcons?.right ?? '⟹'))
91
94
  this.btnClear.innerHTML = (node.attrs.iconClear != null ? node.attrs.iconClear : (this.options.alignMenuIcons?.clear ?? 'x'))
95
+ this.btnView.innerHTML = (this.options.alignMenuIcons?.preview ?? '🔍')
96
+ this.btnSize50.textContent = '50%'
97
+ this.btnSize100.textContent = '100%'
92
98
 
93
99
  this.btnLeft.addEventListener('click', () => this.applyAlignAttr('left'))
94
100
  this.btnCenter.addEventListener('click', () => this.applyAlignAttr('center'))
95
101
  this.btnRight.addEventListener('click', () => this.applyAlignAttr('right'))
96
102
  this.btnClear.addEventListener('click', () => this.applyAlignAttr(null))
97
-
98
- this.menu.appendChild(this.btnLeft)
99
- this.menu.appendChild(this.btnCenter)
100
- this.menu.appendChild(this.btnRight)
101
- this.menu.appendChild(this.btnClear)
103
+ this.btnView.addEventListener('click', () => this.openModal())
104
+ this.btnSize50.addEventListener('click', () => this.applyResizePercent(0.5))
105
+ this.btnSize100.addEventListener('click', () => this.applyResizePercent(1))
106
+
107
+ const hidden = this.options.alignMenuButtonsHide || {}
108
+ if (!hidden.left) this.menu.appendChild(this.btnLeft)
109
+ if (!hidden.center) this.menu.appendChild(this.btnCenter)
110
+ if (!hidden.right) this.menu.appendChild(this.btnRight)
111
+ if (!hidden.clear) this.menu.appendChild(this.btnClear)
112
+ if (!hidden.preview) this.menu.appendChild(this.btnView)
113
+ if (!hidden.size50) this.menu.appendChild(this.btnSize50)
114
+ if (!hidden.size100) this.menu.appendChild(this.btnSize100)
102
115
 
103
116
  this.updateMenuPosition(node.attrs.alignMenuPosition != null ? node.attrs.alignMenuPosition : (this.options && this.options.alignMenuPosition != null ? this.options.alignMenuPosition : 'below'))
104
117
 
@@ -111,6 +124,38 @@ class ResizableImageView {
111
124
 
112
125
  this.dragging = null
113
126
  this.applyAlignment(node.attrs.align)
127
+
128
+ this.modalOverlay = document.createElement('div')
129
+ this.modalOverlay.style.position = 'fixed'
130
+ this.modalOverlay.style.inset = '0'
131
+ this.modalOverlay.style.background = 'rgba(0,0,0,0.8)'
132
+ this.modalOverlay.style.display = 'flex'
133
+ this.modalOverlay.style.alignItems = 'center'
134
+ this.modalOverlay.style.justifyContent = 'center'
135
+ this.modalOverlay.style.zIndex = '9999'
136
+ this.modalOverlay.style.cursor = 'zoom-out'
137
+ this.modalImg = document.createElement('img')
138
+ this.modalImg.src = this.img.src
139
+ this.modalImg.style.maxWidth = '90vw'
140
+ this.modalImg.style.maxHeight = '90vh'
141
+ this.modalImg.style.boxShadow = '0 8px 24px rgba(0,0,0,0.5)'
142
+ const modalClose = document.createElement('button')
143
+ modalClose.textContent = '✕'
144
+ modalClose.style.position = 'absolute'
145
+ modalClose.style.top = '16px'
146
+ modalClose.style.right = '16px'
147
+ modalClose.style.background = '#fff'
148
+ modalClose.style.border = 'none'
149
+ modalClose.style.borderRadius = '6px'
150
+ modalClose.style.padding = '6px 8px'
151
+ modalClose.style.cursor = 'pointer'
152
+ this.modalOverlay.appendChild(this.modalImg)
153
+ this.modalOverlay.appendChild(modalClose)
154
+ this.modalOverlay.addEventListener('click', (ev) => {
155
+ if (ev.target === this.modalOverlay) this.closeModal()
156
+ })
157
+ modalClose.addEventListener('click', () => this.closeModal())
158
+
114
159
  }
115
160
 
116
161
  updateMenuPosition(pos) {
@@ -151,6 +196,15 @@ class ResizableImageView {
151
196
  }
152
197
  }
153
198
 
199
+ applyResizePercent(percent) {
200
+ const naturalW = this.img.naturalWidth || Math.round(this.img.getBoundingClientRect().width)
201
+ const targetW = Math.max(20, Math.round(naturalW * percent))
202
+ const targetH = this.aspect ? Math.round(targetW / this.aspect) : Math.round(this.img.getBoundingClientRect().height)
203
+ const pos = this.getPos()
204
+ const tr = this.view.state.tr.setNodeMarkup(pos, null, { ...this.node.attrs, width: targetW, height: targetH })
205
+ this.view.dispatch(tr)
206
+ }
207
+
154
208
  onPointerDown(event) {
155
209
  event.preventDefault()
156
210
  const direction = event.currentTarget.dataset.direction
@@ -224,6 +278,8 @@ class ResizableImageView {
224
278
  this.btnCenter.innerHTML = (node.attrs.iconCenter != null ? node.attrs.iconCenter : (this.options && this.options.alignMenuIcons ? this.options.alignMenuIcons.center : '⇔'))
225
279
  this.btnRight.innerHTML = (node.attrs.iconRight != null ? node.attrs.iconRight : (this.options && this.options.alignMenuIcons ? this.options.alignMenuIcons.right : '⟹'))
226
280
  this.btnClear.innerHTML = (node.attrs.iconClear != null ? node.attrs.iconClear : (this.options && this.options.alignMenuIcons ? this.options.alignMenuIcons.clear : 'x'))
281
+ if (this.btnView) this.btnView.innerHTML = (this.options && this.options.alignMenuIcons ? this.options.alignMenuIcons.preview : '🔍')
282
+ this.btnView.innerHTML = this.options.iconView ?? '🔍'
227
283
  this.aspect = (node.attrs.height && node.attrs.width) ? node.attrs.width / node.attrs.height : this.aspect
228
284
  return true
229
285
  }
@@ -271,6 +327,15 @@ class ResizableImageView {
271
327
  destroy() {
272
328
  window.removeEventListener('pointermove', this._moveHandler)
273
329
  window.removeEventListener('pointerup', this._upHandler)
330
+ if (this.modalOverlay && this.modalOverlay.parentNode) this.modalOverlay.parentNode.removeChild(this.modalOverlay)
331
+ }
332
+
333
+ openModal() {
334
+ if (!this.modalOverlay.parentNode) document.body.appendChild(this.modalOverlay)
335
+ }
336
+
337
+ closeModal() {
338
+ if (this.modalOverlay && this.modalOverlay.parentNode) this.modalOverlay.parentNode.removeChild(this.modalOverlay)
274
339
  }
275
340
  }
276
341
 
@@ -316,12 +381,8 @@ export const TiptapIzzyExtensionResizableImage = Node.create({
316
381
  height: null,
317
382
  showAlignMenu: true,
318
383
  alignMenuPosition: 'below',
319
- alignMenuIcons: {
320
- left: '<i class="bi bi-text-left"></i>',
321
- center: '<i class="bi bi-text-center"></i>',
322
- right: '<i class="bi bi-text-right"></i>',
323
- clear: 'x'
324
- },
384
+ alignMenuIcons: { left: '⟸', center: '⇔', right: '⟹', clear: 'x', preview: '🔍' },
385
+ alignMenuButtonsHide: {},
325
386
  }
326
387
  },
327
388
 
@@ -343,6 +404,7 @@ export const TiptapIzzyExtensionResizableImage = Node.create({
343
404
  iconCenter: this.options.alignMenuIcons?.center,
344
405
  iconRight: this.options.alignMenuIcons?.right,
345
406
  iconClear: this.options.alignMenuIcons?.clear,
407
+ iconView: this.options.alignMenuIcons?.preview,
346
408
  },
347
409
  })
348
410
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiptap-extension-izzy-resizable-image",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Resizable image NodeView for TipTap v2 with alignment menu and defaults",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "repository": {
17
17
  "type": "git",
18
- "url": "https://github.com/BernanR/TipTap-Extension-Izzy-Resizable-Image.git"
18
+ "url": "git+https://github.com/startinn/TipTap-Extension-Izzy-Resizable-Image.git"
19
19
  },
20
20
  "keywords": [
21
21
  "tiptap",