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.
- package/README.md +25 -12
- package/npm/index.js +73 -11
- 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://
|
|
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: '
|
|
79
|
-
center: '
|
|
80
|
-
right: '
|
|
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.
|
|
99
|
-
this.
|
|
100
|
-
|
|
101
|
-
this.
|
|
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
|
-
|
|
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.
|
|
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/
|
|
18
|
+
"url": "git+https://github.com/startinn/TipTap-Extension-Izzy-Resizable-Image.git"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"tiptap",
|