vue-editify 0.0.27 → 0.0.28

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,712 +1,712 @@
1
- <template>
2
- <Transition :name="animation ? 'editify-layer-' + animation : 'editify-layer'" @enter="handleEnter" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave">
3
- <div v-if="modelValue" class="editify-layer" :data-editify-placement="realPlacement || null" :style="{ zIndex: zIndex }">
4
- <Triangle v-if="showTriangle" :color="border && borderColor ? borderColor : background" :background="background" :placement="triPlacement" ref="triangle" />
5
- <div ref="wrap" class="editify-layer-wrap" :class="{ border: border }" :style="wrapStyle">
6
- <slot></slot>
7
- </div>
8
- </div>
9
- </Transition>
10
- </template>
11
- <script>
12
- import { getCurrentInstance } from 'vue'
13
- import Dap from 'dap-util'
14
- import Triangle from './Triangle'
15
- export default {
16
- name: 'Layer',
17
- emits: ['update:modelValue', 'show', 'shown', 'hidden'],
18
- props: {
19
- //是否显示
20
- modelValue: {
21
- type: Boolean,
22
- default: false
23
- },
24
- //关联元素
25
- node: {
26
- type: [String, Node],
27
- default: null
28
- },
29
- //是否显示边框
30
- border: {
31
- type: Boolean,
32
- default: false
33
- },
34
- //边框颜色
35
- borderColor: {
36
- type: String,
37
- default: null
38
- },
39
- //背景色
40
- background: {
41
- type: String,
42
- default: null
43
- },
44
- //字体颜色
45
- color: {
46
- type: String,
47
- default: null
48
- },
49
- //位置
50
- placement: {
51
- type: String,
52
- default: 'bottom',
53
- validator(value) {
54
- return ['top', 'bottom', 'top-start', 'top-end', 'bottom-start', 'bottom-end'].includes(value)
55
- }
56
- },
57
- //是否显示三角形
58
- showTriangle: {
59
- type: Boolean,
60
- default: false
61
- },
62
- //层级
63
- zIndex: {
64
- type: Number,
65
- default: 10
66
- },
67
- //动画
68
- animation: {
69
- type: String,
70
- default: null,
71
- validator(value) {
72
- return ['translate', 'fade', null].includes(value)
73
- }
74
- },
75
- //是否根据range对象来定位,此时不需要传入node
76
- useRange: {
77
- type: Boolean,
78
- default: false
79
- }
80
- },
81
- setup() {
82
- const uid = getCurrentInstance().uid
83
- return {
84
- uid
85
- }
86
- },
87
- data() {
88
- return {
89
- realPlacement: null,
90
- //三角图标大小
91
- triangleSize: 6
92
- }
93
- },
94
- components: {
95
- Triangle
96
- },
97
- computed: {
98
- //三角形位置
99
- triPlacement() {
100
- if (this.realPlacement == 'bottom-start' || this.realPlacement == 'bottom' || this.realPlacement == 'bottom-end') {
101
- return 'top'
102
- }
103
- if (this.realPlacement == 'top-start' || this.realPlacement == 'top' || this.realPlacement == 'top-end') {
104
- return 'bottom'
105
- }
106
- if (this.realPlacement == 'left-start' || this.realPlacement == 'left' || this.realPlacement == 'left-end') {
107
- return 'right'
108
- }
109
- if (this.realPlacement == 'right-start' || this.realPlacement == 'right' || this.realPlacement == 'right-end') {
110
- return 'left'
111
- }
112
- return 'top'
113
- },
114
- wrapStyle() {
115
- return {
116
- borderColor: this.border ? this.borderColor || '' : '',
117
- background: this.background || '',
118
- color: this.color || ''
119
- }
120
- }
121
- },
122
- mounted() {
123
- if (this.modelValue) {
124
- this.setPosition()
125
- }
126
- Dap.event.on(window, `click.editify_layer_${this.uid}`, this.handleClick)
127
- Dap.event.on(window, `resize.editify_layer_${this.uid}`, this.handleResize)
128
- },
129
- methods: {
130
- //显示时
131
- handleEnter(el) {
132
- this.setPosition()
133
- this.$emit('show', el)
134
- },
135
- //完全显示后
136
- handleAfterEnter(el) {
137
- this.$emit('shown', el)
138
- },
139
- //完全隐藏后
140
- handleAfterLeave(el) {
141
- this.$emit('hidden', el)
142
- },
143
- //窗口尺寸改动
144
- handleResize() {
145
- if (this.modelValue) {
146
- this.$emit('update:modelValue', false)
147
- }
148
- },
149
- //点击定位父元素外的元素关闭浮层
150
- handleClick(e) {
151
- if (!Dap.element.isElement(this.$el)) {
152
- return
153
- }
154
- if (Dap.element.isContains(this.$el.offsetParent, e.target)) {
155
- return
156
- }
157
- if (this.modelValue) {
158
- this.$emit('update:modelValue', false)
159
- }
160
- },
161
- //根据range设置三角形位置
162
- setTrianglePositionByRange() {
163
- const selection = window.getSelection()
164
- if (selection.rangeCount) {
165
- const range = selection.getRangeAt(0)
166
- const rects = range.getClientRects()
167
- if (rects.length) {
168
- //range的第一个位置
169
- const firstRect = rects[0]
170
- //range的最后一个位置
171
- const lastRect = rects[rects.length - 1]
172
- if (this.realPlacement == 'top') {
173
- this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
174
- this.$refs.triangle.$el.style.right = 'auto'
175
- this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
176
- this.$refs.triangle.$el.style.bottom = 'auto'
177
- } else if (this.realPlacement == 'top-start') {
178
- this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > firstRect.width ? firstRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
179
- this.$refs.triangle.$el.style.right = 'auto'
180
- this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
181
- this.$refs.triangle.$el.style.bottom = 'auto'
182
- } else if (this.realPlacement == 'top-end') {
183
- this.$refs.triangle.$el.style.left = 'auto'
184
- this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > firstRect.width ? firstRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
185
- this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
186
- this.$refs.triangle.$el.style.bottom = 'auto'
187
- } else if (this.realPlacement == 'bottom') {
188
- this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
189
- this.$refs.triangle.$el.style.right = 'auto'
190
- this.$refs.triangle.$el.style.top = 'auto'
191
- this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
192
- } else if (this.realPlacement == 'bottom-start') {
193
- this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > lastRect.width ? lastRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
194
- this.$refs.triangle.$el.style.right = 'auto'
195
- this.$refs.triangle.$el.style.top = 'auto'
196
- this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
197
- } else if (this.realPlacement == 'bottom-end') {
198
- this.$refs.triangle.$el.style.left = 'auto'
199
- this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > lastRect.width ? lastRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
200
- this.$refs.triangle.$el.style.top = 'auto'
201
- this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
202
- } else {
203
- this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
204
- this.$refs.triangle.$el.style.right = 'auto'
205
- this.$refs.triangle.$el.style.top = -this.$refs.triangle.$el.offsetHeight + 1 + 'px'
206
- this.$refs.triangle.$el.style.bottom = 'auto'
207
- }
208
- }
209
- }
210
- },
211
- //根据node设置三角形位置
212
- setTrianglePositionByNode() {
213
- const node = this.getNode()
214
- if (!Dap.element.isElement(node)) {
215
- return
216
- }
217
- if (this.realPlacement == 'top') {
218
- this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
219
- this.$refs.triangle.$el.style.right = 'auto'
220
- this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
221
- this.$refs.triangle.$el.style.bottom = 'auto'
222
- } else if (this.realPlacement == 'top-start') {
223
- this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
224
- this.$refs.triangle.$el.style.right = 'auto'
225
- this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
226
- this.$refs.triangle.$el.style.bottom = 'auto'
227
- } else if (this.realPlacement == 'top-end') {
228
- this.$refs.triangle.$el.style.left = 'auto'
229
- this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
230
- this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
231
- this.$refs.triangle.$el.style.bottom = 'auto'
232
- } else if (this.realPlacement == 'bottom') {
233
- this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
234
- this.$refs.triangle.$el.style.right = 'auto'
235
- this.$refs.triangle.$el.style.top = 'auto'
236
- this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
237
- } else if (this.realPlacement == 'bottom-start') {
238
- this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
239
- this.$refs.triangle.$el.style.right = 'auto'
240
- this.$refs.triangle.$el.style.top = 'auto'
241
- this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
242
- } else if (this.realPlacement == 'bottom-end') {
243
- this.$refs.triangle.$el.style.left = 'auto'
244
- this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
245
- this.$refs.triangle.$el.style.top = 'auto'
246
- this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
247
- } else {
248
- this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
249
- this.$refs.triangle.$el.style.right = 'auto'
250
- this.$refs.triangle.$el.style.top = -this.$refs.triangle.$el.offsetHeight + 1 + 'px'
251
- this.$refs.triangle.$el.style.bottom = 'auto'
252
- }
253
- },
254
- //根据range设置位置
255
- setPositionByRange() {
256
- //重置
257
- this.realPlacement = null
258
- const selection = window.getSelection()
259
- if (selection.rangeCount) {
260
- const range = selection.getRangeAt(0)
261
- const rects = range.getClientRects()
262
- if (rects.length) {
263
- //range的第一个位置
264
- const firstRect = rects[0]
265
- //range的最后一个位置
266
- const lastRect = rects[rects.length - 1]
267
- //定位父元素的位置
268
- const parentRect = Dap.element.getElementBounding(this.$el.offsetParent)
269
- //可视窗口高度
270
- const documentHeight = document.documentElement.clientHeight || window.innerHeight
271
- //可视窗口宽度
272
- const documentWidth = document.documentElement.clientWidth || window.innerWidth
273
- if (this.placement == 'top' || this.placement == 'top-start' || this.placement == 'top-end') {
274
- if (firstRect.top >= 0 && firstRect.top >= parentRect.top && firstRect.top >= this.$el.offsetHeight) {
275
- this.realPlacement = this.placement
276
- } else if (documentHeight - firstRect.bottom >= 0 && documentHeight - firstRect.bottom >= parentRect.bottom && documentHeight - firstRect.bottom >= this.$el.offsetHeight) {
277
- this.realPlacement = this.placement == 'top' ? 'bottom' : this.placement == 'top-start' ? 'bottom-start' : 'bottom-end'
278
- }
279
- } else if (this.placement == 'bottom' || this.placement == 'bottom-start' || this.placement == 'bottom-end') {
280
- if (documentHeight - lastRect.bottom >= 0 && documentHeight - lastRect.bottom >= parentRect.bottom && documentHeight - lastRect.bottom >= this.$el.offsetHeight) {
281
- this.realPlacement = this.placement
282
- } else if (lastRect.top >= 0 && lastRect.top >= parentRect.top && lastRect.top >= this.$el.offsetHeight) {
283
- this.realPlacement = this.placement == 'bottom' ? 'top' : this.placement == 'bottom-start' ? 'top-start' : 'top-end'
284
- }
285
- }
286
-
287
- //判断左右是否足够空间显示
288
- if (this.realPlacement == 'top') {
289
- if (documentWidth - firstRect.right + firstRect.width / 2 < this.$el.offsetWidth / 2) {
290
- this.realPlacement = 'top-end'
291
- } else if (firstRect.left + firstRect.width / 2 < this.$el.offsetWidth / 2) {
292
- this.realPlacement = 'top-start'
293
- }
294
- } else if (this.realPlacement == 'bottom') {
295
- if (documentWidth - lastRect.right + lastRect.width / 2 < this.$el.offsetWidth / 2) {
296
- this.realPlacement = 'bottom-end'
297
- } else if (lastRect.left + lastRect.width / 2 < this.$el.offsetWidth / 2) {
298
- this.realPlacement = 'bottom-start'
299
- }
300
- } else if (this.realPlacement == 'top-start') {
301
- if (documentWidth - firstRect.right + firstRect.width < this.$el.offsetWidth) {
302
- if (documentWidth - firstRect.right + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
303
- this.realPlacement = 'top'
304
- } else {
305
- this.realPlacement = 'top-end'
306
- }
307
- }
308
- } else if (this.realPlacement == 'bottom-start') {
309
- if (documentWidth - lastRect.right + lastRect.width < this.$el.offsetWidth) {
310
- if (documentWidth - lastRect.right + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
311
- this.realPlacement = 'bottom'
312
- } else {
313
- this.realPlacement = 'bottom-end'
314
- }
315
- }
316
- } else if (this.realPlacement == 'top-end') {
317
- if (firstRect.left + firstRect.width < this.$el.offsetWidth) {
318
- if (firstRect.left + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
319
- this.realPlacement = 'top'
320
- } else {
321
- this.realPlacement = 'top-start'
322
- }
323
- }
324
- } else if (this.realPlacement == 'bottom-end') {
325
- if (lastRect.left + lastRect.width < this.$el.offsetWidth) {
326
- if (lastRect.left + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
327
- this.realPlacement = 'bottom'
328
- } else {
329
- this.realPlacement = 'bottom-start'
330
- }
331
- }
332
- }
333
- this.$nextTick(() => {
334
- //设置位置对应的样式
335
- if (this.realPlacement == 'top') {
336
- this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
337
- this.$el.style.right = 'auto'
338
- this.$el.style.top = firstRect.top - parentRect.top - this.$el.offsetHeight + 'px'
339
- this.$el.style.bottom = 'auto'
340
- } else if (this.realPlacement == 'top-start') {
341
- this.$el.style.left = firstRect.left - parentRect.left + 'px'
342
- this.$el.style.right = 'auto'
343
- this.$el.style.top = firstRect.top - parentRect.top - this.$el.offsetHeight + 'px'
344
- this.$el.style.bottom = 'auto'
345
- } else if (this.realPlacement == 'top-end') {
346
- this.$el.style.left = 'auto'
347
- this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
348
- this.$el.style.top = firstRect.top - parentRect.top - this.$el.offsetHeight + 'px'
349
- this.$el.style.bottom = 'auto'
350
- } else if (this.realPlacement == 'bottom') {
351
- this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
352
- this.$el.style.right = 'auto'
353
- this.$el.style.top = 'auto'
354
- this.$el.style.bottom = documentHeight - lastRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
355
- } else if (this.realPlacement == 'bottom-start') {
356
- this.$el.style.left = lastRect.left - parentRect.left + 'px'
357
- this.$el.style.right = 'auto'
358
- this.$el.style.top = 'auto'
359
- this.$el.style.bottom = documentHeight - lastRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
360
- } else if (this.realPlacement == 'bottom-end') {
361
- this.$el.style.left = 'auto'
362
- this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
363
- this.$el.style.top = 'auto'
364
- this.$el.style.bottom = documentHeight - lastRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
365
- } else {
366
- this.$el.style.top = 'auto'
367
- this.$el.style.bottom = (parentRect.bottom < 0 ? -parentRect.bottom : 0) + 'px'
368
- if (this.placement == 'top') {
369
- //top-end
370
- if (documentWidth - firstRect.right + firstRect.width / 2 < this.$el.offsetWidth / 2) {
371
- this.$el.style.left = 'auto'
372
- this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
373
- }
374
- //top-start
375
- else if (firstRect.left + firstRect.width / 2 < this.$el.offsetWidth / 2) {
376
- this.$el.style.left = firstRect.left - parentRect.left + 'px'
377
- this.$el.style.right = 'auto'
378
- }
379
- //top
380
- else {
381
- this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
382
- this.$el.style.right = 'auto'
383
- }
384
- } else if (this.placement == 'bottom') {
385
- //bottom-end
386
- if (documentWidth - lastRect.right + lastRect.width / 2 < this.$el.offsetWidth / 2) {
387
- this.$el.style.left = 'auto'
388
- this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
389
- }
390
- //bottom-start
391
- else if (lastRect.left + lastRect.width / 2 < this.$el.offsetWidth / 2) {
392
- this.$el.style.left = lastRect.left - parentRect.left + 'px'
393
- this.$el.style.right = 'auto'
394
- }
395
- //bottom
396
- else {
397
- this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
398
- this.$el.style.right = 'auto'
399
- }
400
- } else if (this.placement == 'top-start') {
401
- if (documentWidth - firstRect.right + firstRect.width < this.$el.offsetWidth) {
402
- //top
403
- if (documentWidth - firstRect.right + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
404
- this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
405
- this.$el.style.right = 'auto'
406
- }
407
- //top-end
408
- else {
409
- this.$el.style.left = 'auto'
410
- this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
411
- }
412
- }
413
- //top-start
414
- else {
415
- this.$el.style.left = firstRect.left - parentRect.left + 'px'
416
- this.$el.style.right = 'auto'
417
- }
418
- } else if (this.placement == 'bottom-start') {
419
- if (documentWidth - lastRect.right + lastRect.width < this.$el.offsetWidth) {
420
- //bottom
421
- if (documentWidth - lastRect.right + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
422
- this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
423
- this.$el.style.right = 'auto'
424
- }
425
- //bottom-end
426
- else {
427
- this.$el.style.left = 'auto'
428
- this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
429
- }
430
- }
431
- //bottom-start
432
- else {
433
- this.$el.style.left = lastRect.left - parentRect.left + 'px'
434
- this.$el.style.right = 'auto'
435
- }
436
- } else if (this.placement == 'top-end') {
437
- if (firstRect.left + firstRect.width < this.$el.offsetWidth) {
438
- //top
439
- if (firstRect.left + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
440
- this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
441
- this.$el.style.right = 'auto'
442
- }
443
- //top-start
444
- else {
445
- this.$el.style.left = firstRect.left - parentRect.left + 'px'
446
- this.$el.style.right = 'auto'
447
- }
448
- }
449
- //top-end
450
- else {
451
- this.$el.style.left = 'auto'
452
- this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
453
- }
454
- } else if (this.placement == 'bottom-end') {
455
- if (lastRect.left + lastRect.width < this.$el.offsetWidth) {
456
- //bottom
457
- if (lastRect.left + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
458
- this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
459
- this.$el.style.right = 'auto'
460
- }
461
- //bottom-start
462
- else {
463
- this.$el.style.left = lastRect.left - parentRect.left + 'px'
464
- this.$el.style.right = 'auto'
465
- }
466
- }
467
- //bottom-end
468
- else {
469
- this.$el.style.left = 'auto'
470
- this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
471
- }
472
- }
473
- }
474
- //三角形位置
475
- if (this.showTriangle) {
476
- this.setTrianglePositionByRange()
477
- }
478
- })
479
- }
480
- }
481
- },
482
- //根据node设置位置
483
- setPositionByNode() {
484
- const node = this.getNode()
485
- if (!Dap.element.isElement(node)) {
486
- return
487
- }
488
- //重置
489
- this.realPlacement = null
490
- //关联元素位置
491
- const nodeRect = Dap.element.getElementBounding(node)
492
- //定位父元素位置
493
- const parentRect = Dap.element.getElementBounding(this.$el.offsetParent)
494
- //设置真实的位置
495
- if (this.placement == 'top' || this.placement == 'top-start' || this.placement == 'top-end') {
496
- if (nodeRect.top >= 0 && nodeRect.top >= parentRect.top && nodeRect.top >= this.$el.offsetHeight) {
497
- this.realPlacement = this.placement
498
- } else if (nodeRect.bottom >= 0 && nodeRect.bottom >= parentRect.bottom && nodeRect.bottom >= this.$el.offsetHeight) {
499
- this.realPlacement = this.placement == 'top' ? 'bottom' : this.placement == 'top-start' ? 'bottom-start' : 'bottom-end'
500
- }
501
- } else if (this.placement == 'bottom' || this.placement == 'bottom-start' || this.placement == 'bottom-end') {
502
- if (nodeRect.bottom >= 0 && nodeRect.bottom >= parentRect.bottom && nodeRect.bottom >= this.$el.offsetHeight) {
503
- this.realPlacement = this.placement
504
- } else if (nodeRect.top >= 0 && nodeRect.top >= parentRect.top && nodeRect.top >= this.$el.offsetHeight) {
505
- this.realPlacement = this.placement == 'bottom' ? 'top' : this.placement == 'bottom-start' ? 'top-start' : 'top-end'
506
- }
507
- }
508
- //判断左右是否足够空间显示
509
- if (this.realPlacement == 'top') {
510
- if (nodeRect.right + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
511
- this.realPlacement = 'top-end'
512
- } else if (nodeRect.left + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
513
- this.realPlacement = 'top-start'
514
- }
515
- } else if (this.realPlacement == 'top-start') {
516
- if (nodeRect.right + node.offsetWidth < this.$el.offsetWidth) {
517
- if (nodeRect.right + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
518
- this.realPlacement = 'top'
519
- } else {
520
- this.realPlacement = 'top-end'
521
- }
522
- }
523
- } else if (this.realPlacement == 'top-end') {
524
- if (nodeRect.left + node.offsetWidth < this.$el.offsetWidth) {
525
- if (nodeRect.left + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
526
- this.realPlacement = 'top'
527
- } else {
528
- this.realPlacement = 'top-start'
529
- }
530
- }
531
- } else if (this.realPlacement == 'bottom') {
532
- if (nodeRect.right + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
533
- this.realPlacement = 'bottom-end'
534
- } else if (nodeRect.left + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
535
- this.realPlacement = 'bottom-start'
536
- }
537
- } else if (this.realPlacement == 'bottom-start') {
538
- if (nodeRect.right + node.offsetWidth < this.$el.offsetWidth) {
539
- if (nodeRect.right + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
540
- this.realPlacement = 'bottom'
541
- } else {
542
- this.realPlacement = 'bottom-end'
543
- }
544
- }
545
- } else if (this.realPlacement == 'bottom-end') {
546
- if (nodeRect.left + node.offsetWidth < this.$el.offsetWidth) {
547
- if (nodeRect.left + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
548
- this.realPlacement = 'bottom'
549
- } else {
550
- this.realPlacement = 'bottom-start'
551
- }
552
- }
553
- }
554
-
555
- this.$nextTick(() => {
556
- //设置位置对应的样式
557
- if (this.realPlacement == 'top') {
558
- this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
559
- this.$el.style.right = 'auto'
560
- this.$el.style.top = nodeRect.top - parentRect.top - this.$el.offsetHeight + 'px'
561
- this.$el.style.bottom = 'auto'
562
- } else if (this.realPlacement == 'top-start') {
563
- this.$el.style.left = nodeRect.left - parentRect.left + 'px'
564
- this.$el.style.right = 'auto'
565
- this.$el.style.top = nodeRect.top - parentRect.top - this.$el.offsetHeight + 'px'
566
- this.$el.style.bottom = 'auto'
567
- } else if (this.realPlacement == 'top-end') {
568
- this.$el.style.left = 'auto'
569
- this.$el.style.right = nodeRect.right - parentRect.right + 'px'
570
- this.$el.style.top = nodeRect.top - parentRect.top - this.$el.offsetHeight + 'px'
571
- this.$el.style.bottom = 'auto'
572
- } else if (this.realPlacement == 'bottom') {
573
- this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
574
- this.$el.style.right = 'auto'
575
- this.$el.style.top = 'auto'
576
- this.$el.style.bottom = nodeRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
577
- } else if (this.realPlacement == 'bottom-start') {
578
- this.$el.style.left = nodeRect.left - parentRect.left + 'px'
579
- this.$el.style.right = 'auto'
580
- this.$el.style.top = 'auto'
581
- this.$el.style.bottom = nodeRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
582
- } else if (this.realPlacement == 'bottom-end') {
583
- this.$el.style.left = 'auto'
584
- this.$el.style.right = nodeRect.right - parentRect.right + 'px'
585
- this.$el.style.top = 'auto'
586
- this.$el.style.bottom = nodeRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
587
- } else {
588
- this.$el.style.top = 'auto'
589
- this.$el.style.bottom = (parentRect.bottom < 0 ? -parentRect.bottom : 0) + 'px'
590
- if (this.placement == 'top' || this.placement == 'bottom') {
591
- if (nodeRect.right + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
592
- this.$el.style.left = 'auto'
593
- this.$el.style.right = nodeRect.right - parentRect.right + 'px'
594
- } else if (nodeRect.left + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
595
- this.$el.style.left = nodeRect.left - parentRect.left + 'px'
596
- this.$el.style.right = 'auto'
597
- } else {
598
- this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
599
- this.$el.style.right = 'auto'
600
- }
601
- } else if (this.placement == 'top-start' || this.placement == 'bottom-start') {
602
- if (nodeRect.right + node.offsetWidth < this.$el.offsetWidth) {
603
- if (nodeRect.right + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
604
- this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
605
- this.$el.style.right = 'auto'
606
- } else {
607
- this.$el.style.left = 'auto'
608
- this.$el.style.right = nodeRect.right - parentRect.right + 'px'
609
- }
610
- } else {
611
- this.$el.style.left = nodeRect.left - parentRect.left + 'px'
612
- this.$el.style.right = 'auto'
613
- }
614
- } else if (this.placement == 'top-end' || this.placement == 'bottom-end') {
615
- if (nodeRect.left + node.offsetWidth < this.$el.offsetWidth) {
616
- if (nodeRect.left + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
617
- this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
618
- this.$el.style.right = 'auto'
619
- } else {
620
- this.$el.style.left = nodeRect.left - parentRect.left + 'px'
621
- this.$el.style.right = 'auto'
622
- }
623
- } else {
624
- this.$el.style.left = 'auto'
625
- this.$el.style.right = nodeRect.right - parentRect.right + 'px'
626
- }
627
- }
628
- }
629
- //三角形位置
630
- if (this.showTriangle) {
631
- this.setTrianglePositionByNode()
632
- }
633
- })
634
- },
635
- //设置位置
636
- setPosition() {
637
- //如果根据range来定位
638
- if (this.useRange) {
639
- this.setPositionByRange()
640
- }
641
- //根据传入的node来定位
642
- else {
643
- this.setPositionByNode()
644
- }
645
- },
646
- //获取目标元素
647
- getNode() {
648
- if (!this.node) {
649
- return null
650
- }
651
- if (Dap.element.isElement(this.node)) {
652
- return this.node
653
- }
654
- return document.body.querySelector(this.node)
655
- }
656
- },
657
- beforeUnmount() {
658
- Dap.event.off(window, `click.editify_layer_${this.uid} resize.editify_layer_${this.uid}`)
659
- }
660
- }
661
- </script>
662
- <style lang="less" scoped>
663
- .editify-layer-fade-enter-from,
664
- .editify-layer-fade-leave-to {
665
- opacity: 0;
666
- }
667
- .editify-layer-fade-enter-active,
668
- .editify-layer-fade-leave-active {
669
- transition: opacity 200ms linear;
670
- }
671
-
672
- .editify-layer-translate-enter-from,
673
- .editify-layer-translate-leave-to {
674
- opacity: 0;
675
- transform: translateY(20px);
676
- }
677
-
678
- .editify-layer-translate-enter-active,
679
- .editify-layer-translate-leave-active {
680
- transition: opacity 200ms ease-in-out, transform 200ms ease-in-out;
681
- }
682
-
683
- .editify-layer {
684
- display: block;
685
- position: absolute;
686
- padding: 0 0 10px 0;
687
- font-size: @font-size;
688
- color: @font-color;
689
-
690
- &[data-editify-placement='bottom'],
691
- &[data-editify-placement='bottom-start'],
692
- &[data-editify-placement='bottom-end'] {
693
- padding: 10px 0 0 0;
694
- }
695
-
696
- :deep(.editify-triangle) {
697
- position: absolute;
698
- z-index: 1;
699
- }
700
-
701
- .editify-layer-wrap {
702
- display: block;
703
- background-color: @background;
704
- box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
705
- border-radius: 4px;
706
-
707
- &.border {
708
- border: 1px solid @border-color;
709
- }
710
- }
711
- }
712
- </style>
1
+ <template>
2
+ <Transition :name="animation ? 'editify-layer-' + animation : 'editify-layer'" @enter="handleEnter" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave">
3
+ <div v-if="modelValue" class="editify-layer" :data-editify-placement="realPlacement || null" :style="{ zIndex: zIndex }">
4
+ <Triangle v-if="showTriangle" :color="border && borderColor ? borderColor : background" :background="background" :placement="triPlacement" ref="triangle" />
5
+ <div ref="wrap" class="editify-layer-wrap" :class="{ border: border }" :style="wrapStyle">
6
+ <slot></slot>
7
+ </div>
8
+ </div>
9
+ </Transition>
10
+ </template>
11
+ <script>
12
+ import { getCurrentInstance } from 'vue'
13
+ import Dap from 'dap-util'
14
+ import Triangle from './Triangle'
15
+ export default {
16
+ name: 'Layer',
17
+ emits: ['update:modelValue', 'show', 'shown', 'hidden'],
18
+ props: {
19
+ //是否显示
20
+ modelValue: {
21
+ type: Boolean,
22
+ default: false
23
+ },
24
+ //关联元素
25
+ node: {
26
+ type: [String, Node],
27
+ default: null
28
+ },
29
+ //是否显示边框
30
+ border: {
31
+ type: Boolean,
32
+ default: false
33
+ },
34
+ //边框颜色
35
+ borderColor: {
36
+ type: String,
37
+ default: null
38
+ },
39
+ //背景色
40
+ background: {
41
+ type: String,
42
+ default: null
43
+ },
44
+ //字体颜色
45
+ color: {
46
+ type: String,
47
+ default: null
48
+ },
49
+ //位置
50
+ placement: {
51
+ type: String,
52
+ default: 'bottom',
53
+ validator(value) {
54
+ return ['top', 'bottom', 'top-start', 'top-end', 'bottom-start', 'bottom-end'].includes(value)
55
+ }
56
+ },
57
+ //是否显示三角形
58
+ showTriangle: {
59
+ type: Boolean,
60
+ default: false
61
+ },
62
+ //层级
63
+ zIndex: {
64
+ type: Number,
65
+ default: 10
66
+ },
67
+ //动画
68
+ animation: {
69
+ type: String,
70
+ default: null,
71
+ validator(value) {
72
+ return ['translate', 'fade', null].includes(value)
73
+ }
74
+ },
75
+ //是否根据range对象来定位,此时不需要传入node
76
+ useRange: {
77
+ type: Boolean,
78
+ default: false
79
+ }
80
+ },
81
+ setup() {
82
+ const uid = getCurrentInstance().uid
83
+ return {
84
+ uid
85
+ }
86
+ },
87
+ data() {
88
+ return {
89
+ realPlacement: null,
90
+ //三角图标大小
91
+ triangleSize: 6
92
+ }
93
+ },
94
+ components: {
95
+ Triangle
96
+ },
97
+ computed: {
98
+ //三角形位置
99
+ triPlacement() {
100
+ if (this.realPlacement == 'bottom-start' || this.realPlacement == 'bottom' || this.realPlacement == 'bottom-end') {
101
+ return 'top'
102
+ }
103
+ if (this.realPlacement == 'top-start' || this.realPlacement == 'top' || this.realPlacement == 'top-end') {
104
+ return 'bottom'
105
+ }
106
+ if (this.realPlacement == 'left-start' || this.realPlacement == 'left' || this.realPlacement == 'left-end') {
107
+ return 'right'
108
+ }
109
+ if (this.realPlacement == 'right-start' || this.realPlacement == 'right' || this.realPlacement == 'right-end') {
110
+ return 'left'
111
+ }
112
+ return 'top'
113
+ },
114
+ wrapStyle() {
115
+ return {
116
+ borderColor: this.border ? this.borderColor || '' : '',
117
+ background: this.background || '',
118
+ color: this.color || ''
119
+ }
120
+ }
121
+ },
122
+ mounted() {
123
+ if (this.modelValue) {
124
+ this.setPosition()
125
+ }
126
+ Dap.event.on(window, `click.editify_layer_${this.uid}`, this.handleClick)
127
+ Dap.event.on(window, `resize.editify_layer_${this.uid}`, this.handleResize)
128
+ },
129
+ methods: {
130
+ //显示时
131
+ handleEnter(el) {
132
+ this.setPosition()
133
+ this.$emit('show', el)
134
+ },
135
+ //完全显示后
136
+ handleAfterEnter(el) {
137
+ this.$emit('shown', el)
138
+ },
139
+ //完全隐藏后
140
+ handleAfterLeave(el) {
141
+ this.$emit('hidden', el)
142
+ },
143
+ //窗口尺寸改动
144
+ handleResize() {
145
+ if (this.modelValue) {
146
+ this.$emit('update:modelValue', false)
147
+ }
148
+ },
149
+ //点击定位父元素外的元素关闭浮层
150
+ handleClick(e) {
151
+ if (!Dap.element.isElement(this.$el)) {
152
+ return
153
+ }
154
+ if (Dap.element.isContains(this.$el.offsetParent, e.target)) {
155
+ return
156
+ }
157
+ if (this.modelValue) {
158
+ this.$emit('update:modelValue', false)
159
+ }
160
+ },
161
+ //根据range设置三角形位置
162
+ setTrianglePositionByRange() {
163
+ const selection = window.getSelection()
164
+ if (selection.rangeCount) {
165
+ const range = selection.getRangeAt(0)
166
+ const rects = range.getClientRects()
167
+ if (rects.length) {
168
+ //range的第一个位置
169
+ const firstRect = rects[0]
170
+ //range的最后一个位置
171
+ const lastRect = rects[rects.length - 1]
172
+ if (this.realPlacement == 'top') {
173
+ this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
174
+ this.$refs.triangle.$el.style.right = 'auto'
175
+ this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
176
+ this.$refs.triangle.$el.style.bottom = 'auto'
177
+ } else if (this.realPlacement == 'top-start') {
178
+ this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > firstRect.width ? firstRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
179
+ this.$refs.triangle.$el.style.right = 'auto'
180
+ this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
181
+ this.$refs.triangle.$el.style.bottom = 'auto'
182
+ } else if (this.realPlacement == 'top-end') {
183
+ this.$refs.triangle.$el.style.left = 'auto'
184
+ this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > firstRect.width ? firstRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
185
+ this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
186
+ this.$refs.triangle.$el.style.bottom = 'auto'
187
+ } else if (this.realPlacement == 'bottom') {
188
+ this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
189
+ this.$refs.triangle.$el.style.right = 'auto'
190
+ this.$refs.triangle.$el.style.top = 'auto'
191
+ this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
192
+ } else if (this.realPlacement == 'bottom-start') {
193
+ this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > lastRect.width ? lastRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
194
+ this.$refs.triangle.$el.style.right = 'auto'
195
+ this.$refs.triangle.$el.style.top = 'auto'
196
+ this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
197
+ } else if (this.realPlacement == 'bottom-end') {
198
+ this.$refs.triangle.$el.style.left = 'auto'
199
+ this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > lastRect.width ? lastRect.width : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
200
+ this.$refs.triangle.$el.style.top = 'auto'
201
+ this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
202
+ } else {
203
+ this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
204
+ this.$refs.triangle.$el.style.right = 'auto'
205
+ this.$refs.triangle.$el.style.top = -this.$refs.triangle.$el.offsetHeight + 1 + 'px'
206
+ this.$refs.triangle.$el.style.bottom = 'auto'
207
+ }
208
+ }
209
+ }
210
+ },
211
+ //根据node设置三角形位置
212
+ setTrianglePositionByNode() {
213
+ const node = this.getNode()
214
+ if (!Dap.element.isElement(node)) {
215
+ return
216
+ }
217
+ if (this.realPlacement == 'top') {
218
+ this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
219
+ this.$refs.triangle.$el.style.right = 'auto'
220
+ this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
221
+ this.$refs.triangle.$el.style.bottom = 'auto'
222
+ } else if (this.realPlacement == 'top-start') {
223
+ this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
224
+ this.$refs.triangle.$el.style.right = 'auto'
225
+ this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
226
+ this.$refs.triangle.$el.style.bottom = 'auto'
227
+ } else if (this.realPlacement == 'top-end') {
228
+ this.$refs.triangle.$el.style.left = 'auto'
229
+ this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
230
+ this.$refs.triangle.$el.style.top = this.$refs.wrap.offsetHeight - 1 + 'px'
231
+ this.$refs.triangle.$el.style.bottom = 'auto'
232
+ } else if (this.realPlacement == 'bottom') {
233
+ this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
234
+ this.$refs.triangle.$el.style.right = 'auto'
235
+ this.$refs.triangle.$el.style.top = 'auto'
236
+ this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
237
+ } else if (this.realPlacement == 'bottom-start') {
238
+ this.$refs.triangle.$el.style.left = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
239
+ this.$refs.triangle.$el.style.right = 'auto'
240
+ this.$refs.triangle.$el.style.top = 'auto'
241
+ this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
242
+ } else if (this.realPlacement == 'bottom-end') {
243
+ this.$refs.triangle.$el.style.left = 'auto'
244
+ this.$refs.triangle.$el.style.right = (this.$refs.wrap.offsetWidth > node.offsetWidth ? node.offsetWidth : this.$refs.wrap.offsetWidth) / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
245
+ this.$refs.triangle.$el.style.top = 'auto'
246
+ this.$refs.triangle.$el.style.bottom = this.$refs.wrap.offsetHeight - 1 + 'px'
247
+ } else {
248
+ this.$refs.triangle.$el.style.left = this.$refs.wrap.offsetWidth / 2 - this.$refs.triangle.$el.offsetWidth / 2 + 'px'
249
+ this.$refs.triangle.$el.style.right = 'auto'
250
+ this.$refs.triangle.$el.style.top = -this.$refs.triangle.$el.offsetHeight + 1 + 'px'
251
+ this.$refs.triangle.$el.style.bottom = 'auto'
252
+ }
253
+ },
254
+ //根据range设置位置
255
+ setPositionByRange() {
256
+ //重置
257
+ this.realPlacement = null
258
+ const selection = window.getSelection()
259
+ if (selection.rangeCount) {
260
+ const range = selection.getRangeAt(0)
261
+ const rects = range.getClientRects()
262
+ if (rects.length) {
263
+ //range的第一个位置
264
+ const firstRect = rects[0]
265
+ //range的最后一个位置
266
+ const lastRect = rects[rects.length - 1]
267
+ //定位父元素的位置
268
+ const parentRect = Dap.element.getElementBounding(this.$el.offsetParent)
269
+ //可视窗口高度
270
+ const documentHeight = document.documentElement.clientHeight || window.innerHeight
271
+ //可视窗口宽度
272
+ const documentWidth = document.documentElement.clientWidth || window.innerWidth
273
+ if (this.placement == 'top' || this.placement == 'top-start' || this.placement == 'top-end') {
274
+ if (firstRect.top >= 0 && firstRect.top >= parentRect.top && firstRect.top >= this.$el.offsetHeight) {
275
+ this.realPlacement = this.placement
276
+ } else if (documentHeight - firstRect.bottom >= 0 && documentHeight - firstRect.bottom >= parentRect.bottom && documentHeight - firstRect.bottom >= this.$el.offsetHeight) {
277
+ this.realPlacement = this.placement == 'top' ? 'bottom' : this.placement == 'top-start' ? 'bottom-start' : 'bottom-end'
278
+ }
279
+ } else if (this.placement == 'bottom' || this.placement == 'bottom-start' || this.placement == 'bottom-end') {
280
+ if (documentHeight - lastRect.bottom >= 0 && documentHeight - lastRect.bottom >= parentRect.bottom && documentHeight - lastRect.bottom >= this.$el.offsetHeight) {
281
+ this.realPlacement = this.placement
282
+ } else if (lastRect.top >= 0 && lastRect.top >= parentRect.top && lastRect.top >= this.$el.offsetHeight) {
283
+ this.realPlacement = this.placement == 'bottom' ? 'top' : this.placement == 'bottom-start' ? 'top-start' : 'top-end'
284
+ }
285
+ }
286
+
287
+ //判断左右是否足够空间显示
288
+ if (this.realPlacement == 'top') {
289
+ if (documentWidth - firstRect.right + firstRect.width / 2 < this.$el.offsetWidth / 2) {
290
+ this.realPlacement = 'top-end'
291
+ } else if (firstRect.left + firstRect.width / 2 < this.$el.offsetWidth / 2) {
292
+ this.realPlacement = 'top-start'
293
+ }
294
+ } else if (this.realPlacement == 'bottom') {
295
+ if (documentWidth - lastRect.right + lastRect.width / 2 < this.$el.offsetWidth / 2) {
296
+ this.realPlacement = 'bottom-end'
297
+ } else if (lastRect.left + lastRect.width / 2 < this.$el.offsetWidth / 2) {
298
+ this.realPlacement = 'bottom-start'
299
+ }
300
+ } else if (this.realPlacement == 'top-start') {
301
+ if (documentWidth - firstRect.right + firstRect.width < this.$el.offsetWidth) {
302
+ if (documentWidth - firstRect.right + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
303
+ this.realPlacement = 'top'
304
+ } else {
305
+ this.realPlacement = 'top-end'
306
+ }
307
+ }
308
+ } else if (this.realPlacement == 'bottom-start') {
309
+ if (documentWidth - lastRect.right + lastRect.width < this.$el.offsetWidth) {
310
+ if (documentWidth - lastRect.right + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
311
+ this.realPlacement = 'bottom'
312
+ } else {
313
+ this.realPlacement = 'bottom-end'
314
+ }
315
+ }
316
+ } else if (this.realPlacement == 'top-end') {
317
+ if (firstRect.left + firstRect.width < this.$el.offsetWidth) {
318
+ if (firstRect.left + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
319
+ this.realPlacement = 'top'
320
+ } else {
321
+ this.realPlacement = 'top-start'
322
+ }
323
+ }
324
+ } else if (this.realPlacement == 'bottom-end') {
325
+ if (lastRect.left + lastRect.width < this.$el.offsetWidth) {
326
+ if (lastRect.left + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
327
+ this.realPlacement = 'bottom'
328
+ } else {
329
+ this.realPlacement = 'bottom-start'
330
+ }
331
+ }
332
+ }
333
+ this.$nextTick(() => {
334
+ //设置位置对应的样式
335
+ if (this.realPlacement == 'top') {
336
+ this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
337
+ this.$el.style.right = 'auto'
338
+ this.$el.style.top = firstRect.top - parentRect.top - this.$el.offsetHeight + 'px'
339
+ this.$el.style.bottom = 'auto'
340
+ } else if (this.realPlacement == 'top-start') {
341
+ this.$el.style.left = firstRect.left - parentRect.left + 'px'
342
+ this.$el.style.right = 'auto'
343
+ this.$el.style.top = firstRect.top - parentRect.top - this.$el.offsetHeight + 'px'
344
+ this.$el.style.bottom = 'auto'
345
+ } else if (this.realPlacement == 'top-end') {
346
+ this.$el.style.left = 'auto'
347
+ this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
348
+ this.$el.style.top = firstRect.top - parentRect.top - this.$el.offsetHeight + 'px'
349
+ this.$el.style.bottom = 'auto'
350
+ } else if (this.realPlacement == 'bottom') {
351
+ this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
352
+ this.$el.style.right = 'auto'
353
+ this.$el.style.top = 'auto'
354
+ this.$el.style.bottom = documentHeight - lastRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
355
+ } else if (this.realPlacement == 'bottom-start') {
356
+ this.$el.style.left = lastRect.left - parentRect.left + 'px'
357
+ this.$el.style.right = 'auto'
358
+ this.$el.style.top = 'auto'
359
+ this.$el.style.bottom = documentHeight - lastRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
360
+ } else if (this.realPlacement == 'bottom-end') {
361
+ this.$el.style.left = 'auto'
362
+ this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
363
+ this.$el.style.top = 'auto'
364
+ this.$el.style.bottom = documentHeight - lastRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
365
+ } else {
366
+ this.$el.style.top = 'auto'
367
+ this.$el.style.bottom = (parentRect.bottom < 0 ? -parentRect.bottom : 0) + 'px'
368
+ if (this.placement == 'top') {
369
+ //top-end
370
+ if (documentWidth - firstRect.right + firstRect.width / 2 < this.$el.offsetWidth / 2) {
371
+ this.$el.style.left = 'auto'
372
+ this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
373
+ }
374
+ //top-start
375
+ else if (firstRect.left + firstRect.width / 2 < this.$el.offsetWidth / 2) {
376
+ this.$el.style.left = firstRect.left - parentRect.left + 'px'
377
+ this.$el.style.right = 'auto'
378
+ }
379
+ //top
380
+ else {
381
+ this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
382
+ this.$el.style.right = 'auto'
383
+ }
384
+ } else if (this.placement == 'bottom') {
385
+ //bottom-end
386
+ if (documentWidth - lastRect.right + lastRect.width / 2 < this.$el.offsetWidth / 2) {
387
+ this.$el.style.left = 'auto'
388
+ this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
389
+ }
390
+ //bottom-start
391
+ else if (lastRect.left + lastRect.width / 2 < this.$el.offsetWidth / 2) {
392
+ this.$el.style.left = lastRect.left - parentRect.left + 'px'
393
+ this.$el.style.right = 'auto'
394
+ }
395
+ //bottom
396
+ else {
397
+ this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
398
+ this.$el.style.right = 'auto'
399
+ }
400
+ } else if (this.placement == 'top-start') {
401
+ if (documentWidth - firstRect.right + firstRect.width < this.$el.offsetWidth) {
402
+ //top
403
+ if (documentWidth - firstRect.right + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
404
+ this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
405
+ this.$el.style.right = 'auto'
406
+ }
407
+ //top-end
408
+ else {
409
+ this.$el.style.left = 'auto'
410
+ this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
411
+ }
412
+ }
413
+ //top-start
414
+ else {
415
+ this.$el.style.left = firstRect.left - parentRect.left + 'px'
416
+ this.$el.style.right = 'auto'
417
+ }
418
+ } else if (this.placement == 'bottom-start') {
419
+ if (documentWidth - lastRect.right + lastRect.width < this.$el.offsetWidth) {
420
+ //bottom
421
+ if (documentWidth - lastRect.right + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
422
+ this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
423
+ this.$el.style.right = 'auto'
424
+ }
425
+ //bottom-end
426
+ else {
427
+ this.$el.style.left = 'auto'
428
+ this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
429
+ }
430
+ }
431
+ //bottom-start
432
+ else {
433
+ this.$el.style.left = lastRect.left - parentRect.left + 'px'
434
+ this.$el.style.right = 'auto'
435
+ }
436
+ } else if (this.placement == 'top-end') {
437
+ if (firstRect.left + firstRect.width < this.$el.offsetWidth) {
438
+ //top
439
+ if (firstRect.left + firstRect.width / 2 >= this.$el.offsetWidth / 2) {
440
+ this.$el.style.left = firstRect.left - parentRect.left + firstRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
441
+ this.$el.style.right = 'auto'
442
+ }
443
+ //top-start
444
+ else {
445
+ this.$el.style.left = firstRect.left - parentRect.left + 'px'
446
+ this.$el.style.right = 'auto'
447
+ }
448
+ }
449
+ //top-end
450
+ else {
451
+ this.$el.style.left = 'auto'
452
+ this.$el.style.right = documentWidth - firstRect.right - parentRect.right + 'px'
453
+ }
454
+ } else if (this.placement == 'bottom-end') {
455
+ if (lastRect.left + lastRect.width < this.$el.offsetWidth) {
456
+ //bottom
457
+ if (lastRect.left + lastRect.width / 2 >= this.$el.offsetWidth / 2) {
458
+ this.$el.style.left = lastRect.left - parentRect.left + lastRect.width / 2 - this.$el.offsetWidth / 2 + 'px'
459
+ this.$el.style.right = 'auto'
460
+ }
461
+ //bottom-start
462
+ else {
463
+ this.$el.style.left = lastRect.left - parentRect.left + 'px'
464
+ this.$el.style.right = 'auto'
465
+ }
466
+ }
467
+ //bottom-end
468
+ else {
469
+ this.$el.style.left = 'auto'
470
+ this.$el.style.right = documentWidth - lastRect.right - parentRect.right + 'px'
471
+ }
472
+ }
473
+ }
474
+ //三角形位置
475
+ if (this.showTriangle) {
476
+ this.setTrianglePositionByRange()
477
+ }
478
+ })
479
+ }
480
+ }
481
+ },
482
+ //根据node设置位置
483
+ setPositionByNode() {
484
+ const node = this.getNode()
485
+ if (!Dap.element.isElement(node)) {
486
+ return
487
+ }
488
+ //重置
489
+ this.realPlacement = null
490
+ //关联元素位置
491
+ const nodeRect = Dap.element.getElementBounding(node)
492
+ //定位父元素位置
493
+ const parentRect = Dap.element.getElementBounding(this.$el.offsetParent)
494
+ //设置真实的位置
495
+ if (this.placement == 'top' || this.placement == 'top-start' || this.placement == 'top-end') {
496
+ if (nodeRect.top >= 0 && nodeRect.top >= parentRect.top && nodeRect.top >= this.$el.offsetHeight) {
497
+ this.realPlacement = this.placement
498
+ } else if (nodeRect.bottom >= 0 && nodeRect.bottom >= parentRect.bottom && nodeRect.bottom >= this.$el.offsetHeight) {
499
+ this.realPlacement = this.placement == 'top' ? 'bottom' : this.placement == 'top-start' ? 'bottom-start' : 'bottom-end'
500
+ }
501
+ } else if (this.placement == 'bottom' || this.placement == 'bottom-start' || this.placement == 'bottom-end') {
502
+ if (nodeRect.bottom >= 0 && nodeRect.bottom >= parentRect.bottom && nodeRect.bottom >= this.$el.offsetHeight) {
503
+ this.realPlacement = this.placement
504
+ } else if (nodeRect.top >= 0 && nodeRect.top >= parentRect.top && nodeRect.top >= this.$el.offsetHeight) {
505
+ this.realPlacement = this.placement == 'bottom' ? 'top' : this.placement == 'bottom-start' ? 'top-start' : 'top-end'
506
+ }
507
+ }
508
+ //判断左右是否足够空间显示
509
+ if (this.realPlacement == 'top') {
510
+ if (nodeRect.right + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
511
+ this.realPlacement = 'top-end'
512
+ } else if (nodeRect.left + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
513
+ this.realPlacement = 'top-start'
514
+ }
515
+ } else if (this.realPlacement == 'top-start') {
516
+ if (nodeRect.right + node.offsetWidth < this.$el.offsetWidth) {
517
+ if (nodeRect.right + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
518
+ this.realPlacement = 'top'
519
+ } else {
520
+ this.realPlacement = 'top-end'
521
+ }
522
+ }
523
+ } else if (this.realPlacement == 'top-end') {
524
+ if (nodeRect.left + node.offsetWidth < this.$el.offsetWidth) {
525
+ if (nodeRect.left + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
526
+ this.realPlacement = 'top'
527
+ } else {
528
+ this.realPlacement = 'top-start'
529
+ }
530
+ }
531
+ } else if (this.realPlacement == 'bottom') {
532
+ if (nodeRect.right + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
533
+ this.realPlacement = 'bottom-end'
534
+ } else if (nodeRect.left + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
535
+ this.realPlacement = 'bottom-start'
536
+ }
537
+ } else if (this.realPlacement == 'bottom-start') {
538
+ if (nodeRect.right + node.offsetWidth < this.$el.offsetWidth) {
539
+ if (nodeRect.right + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
540
+ this.realPlacement = 'bottom'
541
+ } else {
542
+ this.realPlacement = 'bottom-end'
543
+ }
544
+ }
545
+ } else if (this.realPlacement == 'bottom-end') {
546
+ if (nodeRect.left + node.offsetWidth < this.$el.offsetWidth) {
547
+ if (nodeRect.left + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
548
+ this.realPlacement = 'bottom'
549
+ } else {
550
+ this.realPlacement = 'bottom-start'
551
+ }
552
+ }
553
+ }
554
+
555
+ this.$nextTick(() => {
556
+ //设置位置对应的样式
557
+ if (this.realPlacement == 'top') {
558
+ this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
559
+ this.$el.style.right = 'auto'
560
+ this.$el.style.top = nodeRect.top - parentRect.top - this.$el.offsetHeight + 'px'
561
+ this.$el.style.bottom = 'auto'
562
+ } else if (this.realPlacement == 'top-start') {
563
+ this.$el.style.left = nodeRect.left - parentRect.left + 'px'
564
+ this.$el.style.right = 'auto'
565
+ this.$el.style.top = nodeRect.top - parentRect.top - this.$el.offsetHeight + 'px'
566
+ this.$el.style.bottom = 'auto'
567
+ } else if (this.realPlacement == 'top-end') {
568
+ this.$el.style.left = 'auto'
569
+ this.$el.style.right = nodeRect.right - parentRect.right + 'px'
570
+ this.$el.style.top = nodeRect.top - parentRect.top - this.$el.offsetHeight + 'px'
571
+ this.$el.style.bottom = 'auto'
572
+ } else if (this.realPlacement == 'bottom') {
573
+ this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
574
+ this.$el.style.right = 'auto'
575
+ this.$el.style.top = 'auto'
576
+ this.$el.style.bottom = nodeRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
577
+ } else if (this.realPlacement == 'bottom-start') {
578
+ this.$el.style.left = nodeRect.left - parentRect.left + 'px'
579
+ this.$el.style.right = 'auto'
580
+ this.$el.style.top = 'auto'
581
+ this.$el.style.bottom = nodeRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
582
+ } else if (this.realPlacement == 'bottom-end') {
583
+ this.$el.style.left = 'auto'
584
+ this.$el.style.right = nodeRect.right - parentRect.right + 'px'
585
+ this.$el.style.top = 'auto'
586
+ this.$el.style.bottom = nodeRect.bottom - parentRect.bottom - this.$el.offsetHeight + 'px'
587
+ } else {
588
+ this.$el.style.top = 'auto'
589
+ this.$el.style.bottom = (parentRect.bottom < 0 ? -parentRect.bottom : 0) + 'px'
590
+ if (this.placement == 'top' || this.placement == 'bottom') {
591
+ if (nodeRect.right + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
592
+ this.$el.style.left = 'auto'
593
+ this.$el.style.right = nodeRect.right - parentRect.right + 'px'
594
+ } else if (nodeRect.left + node.offsetWidth / 2 < this.$el.offsetWidth / 2) {
595
+ this.$el.style.left = nodeRect.left - parentRect.left + 'px'
596
+ this.$el.style.right = 'auto'
597
+ } else {
598
+ this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
599
+ this.$el.style.right = 'auto'
600
+ }
601
+ } else if (this.placement == 'top-start' || this.placement == 'bottom-start') {
602
+ if (nodeRect.right + node.offsetWidth < this.$el.offsetWidth) {
603
+ if (nodeRect.right + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
604
+ this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
605
+ this.$el.style.right = 'auto'
606
+ } else {
607
+ this.$el.style.left = 'auto'
608
+ this.$el.style.right = nodeRect.right - parentRect.right + 'px'
609
+ }
610
+ } else {
611
+ this.$el.style.left = nodeRect.left - parentRect.left + 'px'
612
+ this.$el.style.right = 'auto'
613
+ }
614
+ } else if (this.placement == 'top-end' || this.placement == 'bottom-end') {
615
+ if (nodeRect.left + node.offsetWidth < this.$el.offsetWidth) {
616
+ if (nodeRect.left + node.offsetWidth / 2 >= this.$el.offsetWidth / 2) {
617
+ this.$el.style.left = nodeRect.left - parentRect.left + node.offsetWidth / 2 - this.$el.offsetWidth / 2 + 'px'
618
+ this.$el.style.right = 'auto'
619
+ } else {
620
+ this.$el.style.left = nodeRect.left - parentRect.left + 'px'
621
+ this.$el.style.right = 'auto'
622
+ }
623
+ } else {
624
+ this.$el.style.left = 'auto'
625
+ this.$el.style.right = nodeRect.right - parentRect.right + 'px'
626
+ }
627
+ }
628
+ }
629
+ //三角形位置
630
+ if (this.showTriangle) {
631
+ this.setTrianglePositionByNode()
632
+ }
633
+ })
634
+ },
635
+ //设置位置
636
+ setPosition() {
637
+ //如果根据range来定位
638
+ if (this.useRange) {
639
+ this.setPositionByRange()
640
+ }
641
+ //根据传入的node来定位
642
+ else {
643
+ this.setPositionByNode()
644
+ }
645
+ },
646
+ //获取目标元素
647
+ getNode() {
648
+ if (!this.node) {
649
+ return null
650
+ }
651
+ if (Dap.element.isElement(this.node)) {
652
+ return this.node
653
+ }
654
+ return document.body.querySelector(this.node)
655
+ }
656
+ },
657
+ beforeUnmount() {
658
+ Dap.event.off(window, `click.editify_layer_${this.uid} resize.editify_layer_${this.uid}`)
659
+ }
660
+ }
661
+ </script>
662
+ <style lang="less" scoped>
663
+ .editify-layer-fade-enter-from,
664
+ .editify-layer-fade-leave-to {
665
+ opacity: 0;
666
+ }
667
+ .editify-layer-fade-enter-active,
668
+ .editify-layer-fade-leave-active {
669
+ transition: opacity 200ms linear;
670
+ }
671
+
672
+ .editify-layer-translate-enter-from,
673
+ .editify-layer-translate-leave-to {
674
+ opacity: 0;
675
+ transform: translateY(20px);
676
+ }
677
+
678
+ .editify-layer-translate-enter-active,
679
+ .editify-layer-translate-leave-active {
680
+ transition: opacity 200ms ease-in-out, transform 200ms ease-in-out;
681
+ }
682
+
683
+ .editify-layer {
684
+ display: block;
685
+ position: absolute;
686
+ padding: 0 0 10px 0;
687
+ font-size: @font-size;
688
+ color: @font-color;
689
+
690
+ &[data-editify-placement='bottom'],
691
+ &[data-editify-placement='bottom-start'],
692
+ &[data-editify-placement='bottom-end'] {
693
+ padding: 10px 0 0 0;
694
+ }
695
+
696
+ :deep(.editify-triangle) {
697
+ position: absolute;
698
+ z-index: 1;
699
+ }
700
+
701
+ .editify-layer-wrap {
702
+ display: block;
703
+ background-color: @background;
704
+ box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
705
+ border-radius: 4px;
706
+
707
+ &.border {
708
+ border: 1px solid @border-color;
709
+ }
710
+ }
711
+ }
712
+ </style>