quill-resize-module 1.2.4 → 2.0.0

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.
@@ -1,21 +1,22 @@
1
1
  import DefaultOptions from './DefaultOptions'
2
+ import Base from './modules/BaseModule.js'
2
3
  import DisplaySize from './modules/DisplaySize'
3
4
  import Toolbar from './modules/Toolbar'
4
5
  import Resize from './modules/Resize'
5
6
  import Keyboard from './modules/Keyboard'
6
7
 
7
8
  import _Quill from 'quill'
9
+ import { randomString } from './Util.js'
8
10
  const Quill = window.Quill || _Quill
9
11
  const Parchment = Quill.import('parchment')
10
12
 
11
- const knownModules = { DisplaySize, Toolbar, Resize, Keyboard }
12
-
13
13
  /**
14
14
  * Custom module for quilljs to allow user to resize elements
15
15
  * (Works on Chrome, Edge, Safari and replaces Firefox's native resize behavior)
16
16
  * @see https://quilljs.com/blog/building-a-custom-module/
17
17
  */
18
18
  export default class QuillResize {
19
+ static Modules = { Base, DisplaySize, Toolbar, Resize, Keyboard }
19
20
  constructor (quill, options = {}) {
20
21
  quill.resizer = this
21
22
  // save the quill reference and options
@@ -51,8 +52,7 @@ export default class QuillResize {
51
52
 
52
53
  this.quill.emitter.on('resize-edit', this.handleEdit.bind(this))
53
54
 
54
- this.quill.root.parentNode.style.position =
55
- this.quill.root.parentNode.style.position || 'relative'
55
+ this.quill.container.style.position = this.quill.container.style.position || 'relative'
56
56
 
57
57
  // add class to selected parchment
58
58
  this.selectedBlots = []
@@ -69,22 +69,39 @@ export default class QuillResize {
69
69
  if (this.options.keyboardSelect) {
70
70
  Keyboard.injectInit(this.quill)
71
71
  }
72
+
73
+ // create embed elements style
74
+ if (this.options.embedTags) {
75
+ this.initializeEmbed()
76
+ }
72
77
  }
73
78
 
74
79
  initializeModules () {
75
80
  this.removeModules()
76
81
 
77
82
  this.modules = this.moduleClasses.map(
78
- ModuleClass => new (knownModules[ModuleClass] || ModuleClass)(this)
83
+ ModuleClass => new (this.constructor.Modules[ModuleClass] || ModuleClass)(this)
79
84
  )
80
85
 
81
86
  this.modules.forEach(module => {
82
- module.onCreate()
87
+ module.onCreate(this)
83
88
  })
84
89
 
85
90
  this.onUpdate()
86
91
  }
87
92
 
93
+ initializeEmbed () {
94
+ if (!this.options.embedTags.length) return
95
+ this.embedClassName = `ql-${randomString()}`
96
+ let cssText = [''].concat(this.options.embedTags).join(`, .${this.embedClassName} `).slice(2)
97
+ cssText += '{pointer-events: none;}'
98
+
99
+ const style = document.createElement('style')
100
+ style.textContent = cssText
101
+ this.quill.container.appendChild(style)
102
+ this.quill.root.classList.add(this.embedClassName)
103
+ }
104
+
88
105
  onUpdate (fromModule) {
89
106
  this.updateFromModule = fromModule
90
107
  this.repositionElements()
@@ -112,8 +129,15 @@ export default class QuillResize {
112
129
  handleClick (evt) {
113
130
  let show = false
114
131
  let blot
115
- const target = evt.target
116
-
132
+ let target = evt.target
133
+
134
+ // 如果是根元素,意味着可能是embed元素事件穿透,需要根据光标位置获取元素
135
+ if (target === this.quill.root && this.options.embedTags) {
136
+ const root = this.quill.root
137
+ root.classList.remove(this.embedClassName)
138
+ target = document.elementFromPoint(evt.clientX, evt.clientY)
139
+ root.classList.add(this.embedClassName)
140
+ }
117
141
  if (target && target.tagName) {
118
142
  blot = this.quill.constructor.find(target)
119
143
  if (blot) {
@@ -193,7 +217,7 @@ export default class QuillResize {
193
217
  Object.assign(this.overlay.style, this.options.styles.overlay)
194
218
  this.overlay.addEventListener('dblclick', this.handleEdit.bind(this), false)
195
219
 
196
- this.quill.root.parentNode.appendChild(this.overlay)
220
+ this.quill.container.appendChild(this.overlay)
197
221
 
198
222
  this.hideProxy = evt => {
199
223
  if (!this.activeEle) return
@@ -214,11 +238,10 @@ export default class QuillResize {
214
238
  }
215
239
 
216
240
  // Remove the overlay
217
- this.quill.root.parentNode.removeChild(this.overlay)
241
+ this.quill.container.removeChild(this.overlay)
218
242
  this.overlay = undefined
219
243
 
220
244
  // stop listening for image deletion or movement
221
- document.removeEventListener('keydown', this.keyboardProxy, true)
222
245
  this.quill.root.removeEventListener('input', this.hideProxy, true)
223
246
  this.quill.root.removeEventListener('scroll', this.updateOverlayPositionProxy)
224
247
 
@@ -232,7 +255,7 @@ export default class QuillResize {
232
255
  }
233
256
 
234
257
  // position the overlay over the image
235
- const parent = this.quill.root.parentNode
258
+ const parent = this.quill.container
236
259
  const eleRect = this.activeEle.getBoundingClientRect()
237
260
  const containerRect = parent.getBoundingClientRect()
238
261
 
@@ -255,7 +278,7 @@ export default class QuillResize {
255
278
  this.selectedBlots = []
256
279
  return
257
280
  }
258
- const leaves = this.quill.scroll.descendants(Parchment.Leaf, range.index, range.length)
281
+ const leaves = this.quill.scroll.descendants(Parchment.Leaf || Parchment.LeafBlot, range.index, range.length)
259
282
  const blots = leaves.filter(blot => {
260
283
  const canBeHandle = !!this.options.parchment[blot.statics.blotName]
261
284
  if (canBeHandle) blot.domNode.classList.add(this.options.selectedClass)
package/src/Util.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 生成一个精炼的随机字符串
3
+ * @param {number} length - 生成字符串的长度
4
+ * @returns {string} - 随机字符串
5
+ */
6
+ export function randomString (length = 8) {
7
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
8
+ return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
9
+ }
@@ -13,44 +13,3 @@
13
13
  .ql-resize-style-full {
14
14
  width: 100% !important;
15
15
  }
16
-
17
- .ql-embed-placeholder {
18
- display: inline-flex;
19
- flex-direction: column;
20
- justify-content: center;
21
- text-align: center;
22
- width: 400px;
23
- height: 225px;
24
- background-color: #f0f0f0;
25
- border: 1px solid #ccc;
26
- cursor: default !important;
27
- margin-bottom: 1em;
28
- padding: 2em;
29
- /* user-select: none; */
30
- }
31
- .ql-embed-placeholder:hover {
32
- background-color: #e8e8e8;
33
- }
34
- .ql-embed-placeholder::before {
35
- content: "<" attr(data-type) ">";
36
- font-size: 1.5em;
37
- margin-bottom: .5em;
38
- text-transform: uppercase;
39
- }
40
- .ql-embed-placeholder::after {
41
- content: attr(data-src);
42
- color: #666;
43
- }
44
-
45
- .ql-embed-placeholder.active {
46
- border-color: #abc9e9;
47
- background-color: #d2e5f9;
48
- }
49
- .ql-embed-placeholder.selected {
50
- color: #fff;
51
- border-color: #4185d2;
52
- background-color: #5f9de2;
53
- }
54
- .ql-embed-placeholder.selected::after {
55
- color: #fff;
56
- }
package/src/index.js CHANGED
@@ -1,38 +1,4 @@
1
- import _Quill from 'quill'
1
+ import QuillResize from './QuillResize'
2
2
  import './assets/resize.css'
3
3
 
4
- import Resize from './QuillResize'
5
- import { Image } from './formats/image'
6
- import PlaceholderRegister from './formats/placeholder'
7
-
8
- const Quill = window.Quill || _Quill
9
- Quill.register(Image, true)
10
-
11
- export {
12
- EmbedPlaceholder,
13
- TagPlaceholder,
14
- ClassNamePlaceholder,
15
- convertPlaceholderHTML
16
- } from './formats/placeholder'
17
-
18
- export default Resize
19
- export { Resize, Image, PlaceholderRegister }
20
-
21
- // Polyfill for IE and Element.closest
22
- if (!Element.prototype.matches) {
23
- Element.prototype.matches =
24
- Element.prototype.msMatchesSelector ||
25
- Element.prototype.webkitMatchesSelector
26
- }
27
-
28
- if (!Element.prototype.closest) {
29
- Element.prototype.closest = function (s) {
30
- var el = this
31
- if (!document.documentElement.contains(el)) return null
32
- do {
33
- if (el.matches(s)) return el
34
- el = el.parentElement || el.parentNode
35
- } while (el !== null && el.nodeType === 1)
36
- return null
37
- }
38
- }
4
+ export default QuillResize
@@ -1,5 +1,22 @@
1
1
  import BaseModule from './BaseModule'
2
2
 
3
+ import _Quill from 'quill'
4
+ const Quill = window.Quill || _Quill
5
+
6
+ const Parchment = Quill.import('parchment')
7
+
8
+ const keyCodes = {
9
+ BACKSPACE: 8,
10
+ TAB: 9,
11
+ ENTER: 13,
12
+ ESCAPE: 27,
13
+ LEFT: 37,
14
+ UP: 38,
15
+ RIGHT: 39,
16
+ DOWN: 40,
17
+ DELETE: 46
18
+ }
19
+
3
20
  export default class Keyboard extends BaseModule {
4
21
  static injectInit (quill) {
5
22
  // left/right
@@ -10,21 +27,6 @@ export default class Keyboard extends BaseModule {
10
27
  bindings[this.keys.RIGHT].unshift(
11
28
  this.makeArrowHandler(this.keys.RIGHT, false)
12
29
  )
13
-
14
- // // up
15
- // const upBinding = this.makeArrowHandler(this.keys.UP, false)
16
- // if (bindings[this.keys.UP]) {
17
- // bindings[this.keys.UP].unshift(upBinding)
18
- // } else {
19
- // quill.keyboard.addBinding(upBinding)
20
- // }
21
- // // down
22
- // const downBinding = this.makeArrowHandler(this.keys.DOWN, false)
23
- // if (bindings[this.keys.DOWN]) {
24
- // bindings[this.keys.DOWN].unshift(downBinding)
25
- // } else {
26
- // quill.keyboard.addBinding(downBinding)
27
- // }
28
30
  }
29
31
 
30
32
  static makeArrowHandler (key, shiftKey) {
@@ -38,24 +40,29 @@ export default class Keyboard extends BaseModule {
38
40
  if (!this.quill.resizer) return true
39
41
 
40
42
  let index = range.index
43
+ const isLeft = key === Keyboard.keys.LEFT
44
+ const isRight = key === Keyboard.keys.RIGHT
41
45
 
42
46
  // check end of line
43
- const [line] = this.quill.getLine(range.index)
47
+ const [line] = this.quill.getLine(index + (isLeft ? -1 : 0))
48
+ if (this.quill.resizer.judgeShow(line)) return false
44
49
  const lineIndex = this.quill.getIndex(line)
45
- if (key === Keyboard.keys.RIGHT && lineIndex + line.length() - 1 === index) return true
50
+
51
+ if (isRight && lineIndex + line.length() - 1 === index) return true
46
52
 
47
53
  // get leaf/offset
48
- if (key === Keyboard.keys.RIGHT) {
54
+ if (isRight) {
49
55
  index += (range.length + 1)
50
56
  }
51
57
  let [leaf] = this.quill.getLeaf(index)
52
58
  const offset = leaf.offset(leaf.parent)
59
+ const isBlock = leaf.constructor.scope === Parchment.Scope.BLOCK_BLOT
53
60
 
54
61
  // check start of line
55
- if (key === Keyboard.keys.LEFT && (index === 0 || index === lineIndex)) return true
62
+ if (isLeft && ((isBlock && index === offset) || (index === 0 || index === lineIndex))) return true
56
63
 
57
64
  // get previous leaf
58
- if (key === Keyboard.keys.LEFT) {
65
+ if (isLeft) {
59
66
  if (offset === 0) {
60
67
  index -= 1
61
68
  leaf = this.quill.getLeaf(index)[0]
@@ -89,19 +96,19 @@ export default class Keyboard extends BaseModule {
89
96
  let handled = false
90
97
 
91
98
  // delete
92
- if (code === Keyboard.keys.BACKSPACE || code === Keyboard.keys.DELETE) {
99
+ if (code === keyCodes.BACKSPACE || code === keyCodes.DELETE) {
93
100
  this.blot.deleteAt(0)
94
101
  this.blot.parent.optimize()
95
102
  handled = true
96
103
 
97
104
  // direction key
98
- } else if (code >= Keyboard.keys.LEFT && code <= Keyboard.keys.DOWN) {
99
- if (code === Keyboard.keys.RIGHT) {
100
- index++
101
- } else if (code === Keyboard.keys.UP) {
105
+ } else if (code >= keyCodes.LEFT && code <= keyCodes.DOWN) {
106
+ if (code === keyCodes.RIGHT) {
107
+ index += this.blot.length() || 1
108
+ } else if (code === keyCodes.UP) {
102
109
  index = this.getOtherLineIndex(-1)
103
110
  nextBlot = this.quill.getLeaf(index)[0]
104
- } else if (code === Keyboard.keys.DOWN) {
111
+ } else if (code === keyCodes.DOWN) {
105
112
  index = this.getOtherLineIndex(1)
106
113
  nextBlot = this.quill.getLeaf(index)[0]
107
114
  }
@@ -143,14 +150,18 @@ export default class Keyboard extends BaseModule {
143
150
  }
144
151
  }
145
152
 
146
- Keyboard.keys = {
147
- BACKSPACE: 8,
148
- TAB: 9,
149
- ENTER: 13,
150
- ESCAPE: 27,
151
- LEFT: 37,
152
- UP: 38,
153
- RIGHT: 39,
154
- DOWN: 40,
155
- DELETE: 46
153
+ if (/^2\./.test(Quill.version)) {
154
+ Keyboard.keys = {
155
+ BACKSPACE: 'Backspace',
156
+ TAB: 'Tab',
157
+ ENTER: 'Enter',
158
+ ESCAPE: 'Escape',
159
+ LEFT: 'ArrowLeft',
160
+ UP: 'ArrowUp',
161
+ RIGHT: 'ArrowRight',
162
+ DOWN: 'ArrowDown',
163
+ DELETE: 'Delete'
164
+ }
165
+ } else {
166
+ Keyboard.keys = keyCodes
156
167
  }
@@ -2,6 +2,7 @@ import BaseModule from './BaseModule'
2
2
 
3
3
  export default class Resize extends BaseModule {
4
4
  onCreate () {
5
+ this.blotOptions = this.options.parchment[this.blot.statics.blotName]
5
6
  // track resize handles
6
7
  this.boxes = []
7
8
 
@@ -80,7 +81,12 @@ export default class Resize extends BaseModule {
80
81
  document.addEventListener('mouseup', this.handleMouseupProxy, false)
81
82
  }
82
83
 
83
- handleMouseup () {
84
+ handleMouseup (evt) {
85
+ // save size, clear style
86
+ const calcSize = this.calcSize(evt, this.blotOptions.limit)
87
+ Object.assign(this.activeEle, calcSize)
88
+ Object.assign(this.activeEle.style, { width: null, height: null })
89
+
84
90
  // reset cursor everywhere
85
91
  this.setCursor('')
86
92
  this.blot.handling && this.blot.handling(false)
@@ -94,15 +100,25 @@ export default class Resize extends BaseModule {
94
100
  // activeEle not set yet
95
101
  return
96
102
  }
103
+
104
+ const limit = {
105
+ ...this.blotOptions.limit,
106
+ unit: true
107
+ }
108
+ Object.assign(this.activeEle.style, this.calcSize(evt, limit))
109
+
110
+ this.requestUpdate()
111
+ }
112
+
113
+ calcSize (evt, limit = {}) {
97
114
  // update size
98
115
  const deltaX = evt.clientX - this.dragStartX
99
116
  const deltaY = evt.clientY - this.dragStartY
100
117
 
101
- const options = this.options.parchment[this.blot.statics.blotName]
102
118
  const size = {}
103
119
  let direction = 1
104
120
 
105
- ;(options.attribute || ['width']).forEach(key => {
121
+ ;(this.blotOptions.attribute || ['width']).forEach(key => {
106
122
  size[key] = this.preDragSize[key]
107
123
  })
108
124
 
@@ -118,11 +134,6 @@ export default class Resize extends BaseModule {
118
134
  size.height = Math.round(this.preDragSize.height + deltaY * direction)
119
135
  }
120
136
 
121
- Object.assign(this.activeEle.style, this.calcSize(size, options.limit))
122
- this.requestUpdate()
123
- }
124
-
125
- calcSize (size, limit = {}) {
126
137
  let { width, height } = size
127
138
 
128
139
  // keep ratio
@@ -156,10 +167,15 @@ export default class Resize extends BaseModule {
156
167
  }
157
168
  }
158
169
 
159
- if (width) size.width = width + 'px'
160
- if (height) size.height = height + 'px'
170
+ if (limit.unit) {
171
+ if (width) width = width + 'px'
172
+ if (height) height = height + 'px'
173
+ }
161
174
 
162
- return size
175
+ const res = {}
176
+ if (width) res.width = width
177
+ if (height) res.height = height
178
+ return res
163
179
  }
164
180
 
165
181
  getNaturalSize () {
package/demo/demo.png DELETED
Binary file
package/demo/umd.html DELETED
@@ -1,110 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
-
4
- <head>
5
- <meta charset="utf-8">
6
- <title>Quill Resize Module Demo</title>
7
- <link href="http://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
8
- <link href="../dist/resize.css" rel="stylesheet">
9
- <style>
10
- body {
11
- width: 1000px;
12
- margin: 0 auto;
13
- }
14
-
15
- .editor-box {
16
- height: 500px;
17
- padding-top: 42px;
18
- margin: 0 auto;
19
- }
20
-
21
- .editor-box .ql-toolbar {
22
- margin-top: -42px;
23
- }
24
-
25
- .editor-box .ql-container {
26
- overflow: hidden;
27
- }
28
-
29
- .editor-box .ql-editor img {
30
- cursor: default !important;
31
- }
32
-
33
- .ql-embed-placeholder[data-type="ql-embed"] {
34
- background-color: #fff;
35
- }
36
-
37
- .buttons {
38
- margin: 1em 0 2em;
39
- }
40
-
41
- .btn {
42
- padding: 3px .5em;
43
- }
44
-
45
- #result {
46
- width: 100%;
47
- }
48
- </style>
49
- </head>
50
-
51
- <body>
52
- <h1>Quill Resize Module Demo</h1>
53
- <div class="editor-box">
54
- <div id="editor">
55
- <p>Click on the Image to resize.</p>
56
- <p><img src="http://n.sinaimg.cn/sinacn10101/486/w2048h1638/20190518/9909-hwzkfpv0668515.jpg" style="width: 300px;"></p>
57
- <p><br></p>
58
- <p>And you can custom any element to be resized.</p>
59
- <p><iframe src="https://vjs.zencdn.net/" frameborder="0"></iframe>
60
- It's inline-block
61
- <video src="https://vjs.zencdn.net/v/oceans.mp4" controls preload="auto" poster="https://vjs.zencdn.net/v/oceans.png"></video></p>
62
- <p>Try to press left/right key around the placeholder.</p>
63
- </div>
64
- </div>
65
- <div class="buttons">
66
- <button class="btn btn-html">getHtml</button>
67
- <button class="btn btn-content">getContent</button>
68
- <button class="btn btn-text">getText</button>
69
- &nbsp;&nbsp;
70
- <button class="btn btn-undo">undo</button>
71
- <button class="btn btn-redo">redo</button>
72
- </div>
73
- <textarea id="result" rows="10"></textarea>
74
- <script src="http://cdn.quilljs.com/1.3.6/quill.min.js"></script>
75
- <script src="../dist/resize.js"></script>
76
- <script>
77
- // eslint-disable-next-line no-undef
78
- QuillResize.PlaceholderRegister()
79
- // eslint-disable-next-line no-undef
80
- var demoEditor = new Quill('#editor', {
81
- theme: 'snow',
82
- modules: {
83
- resize: {}
84
- }
85
- })
86
-
87
- var $result = document.querySelector('#result')
88
- document.querySelector('.btn-html').addEventListener('click', function () {
89
- // call convertPlaceholderHTML to replace source element style
90
- // eslint-disable-next-line no-undef
91
- var html = QuillResize.convertPlaceholderHTML(demoEditor.root.innerHTML)
92
- $result.value = html
93
- })
94
- document.querySelector('.btn-content').addEventListener('click', function () {
95
- var result = demoEditor.getContents()
96
- $result.value = JSON.stringify(result)
97
- })
98
- document.querySelector('.btn-text').addEventListener('click', function () {
99
- $result.value = demoEditor.getText()
100
- })
101
- document.querySelector('.btn-undo').addEventListener('click', function () {
102
- demoEditor.history.undo()
103
- })
104
- document.querySelector('.btn-redo').addEventListener('click', function () {
105
- demoEditor.history.redo()
106
- })
107
- </script>
108
- </body>
109
-
110
- </html>
@@ -1,40 +0,0 @@
1
- import _Quill from 'quill'
2
- const Quill = window.Quill || _Quill
3
-
4
- // BEGIN allow image alignment styles
5
- const ATTRIBUTES = ['alt', 'height', 'width', 'style', 'data-size']
6
-
7
- var BaseImageFormat = Quill.import('formats/image')
8
- class Image extends BaseImageFormat {
9
- static formats (domNode) {
10
- if (domNode.__handling && domNode.__formats) {
11
- return domNode.__formats
12
- }
13
-
14
- return ATTRIBUTES.reduce(function (formats, attribute) {
15
- if (domNode.hasAttribute(attribute)) {
16
- formats[attribute] = domNode.getAttribute(attribute)
17
- }
18
- return formats
19
- }, {})
20
- }
21
-
22
- format (name, value) {
23
- if (ATTRIBUTES.indexOf(name) > -1) {
24
- if (value) {
25
- this.domNode.setAttribute(name, value)
26
- } else {
27
- this.domNode.removeAttribute(name)
28
- }
29
- } else {
30
- super.format(name, value)
31
- }
32
- }
33
-
34
- handling (handling) {
35
- this.domNode.__formats = this.constructor.formats(this.domNode)
36
- this.domNode.__handling = handling
37
- }
38
- }
39
-
40
- export { Image, ATTRIBUTES }