quasar-ui-sellmate-ui-kit 3.4.3 → 3.5.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.
- package/dist/index.common.js +2 -2
- package/dist/index.css +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.min.css +1 -1
- package/dist/index.rtl.css +1 -1
- package/dist/index.rtl.min.css +1 -1
- package/dist/index.umd.js +153 -25
- package/dist/index.umd.min.js +2 -2
- package/package.json +4 -1
- package/src/components/SEditor.vue +104 -20
- package/src/composables/resizable.js +58 -0
- package/vitest.config.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quasar-ui-sellmate-ui-kit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"author": "Sellmate Dev Team <dev@sellmate.co.kr>",
|
|
5
5
|
"description": "Sellmate UI Kit",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@quasar/extras": "^1.16.9",
|
|
35
35
|
"@quasar/quasar-app-extension-testing-unit-vitest": "1.0.0",
|
|
36
36
|
"@rollup/plugin-buble": "^0.21.3",
|
|
37
|
+
"@rollup/plugin-commonjs": "^21.1.0",
|
|
37
38
|
"@rollup/plugin-json": "^4.0.0",
|
|
38
39
|
"@rollup/plugin-node-resolve": "^11.2.1",
|
|
39
40
|
"@rollup/plugin-replace": "^2.4.2",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"eslint-plugin-vue": "^9.20.1",
|
|
56
57
|
"fs-extra": "^8.1.0",
|
|
57
58
|
"happy-dom": "^13.6.2",
|
|
59
|
+
"interactjs": "^1.10.27",
|
|
58
60
|
"open": "^7.3.0",
|
|
59
61
|
"postcss": "^8.1.9",
|
|
60
62
|
"prettier": "^3.2.4",
|
|
@@ -86,6 +88,7 @@
|
|
|
86
88
|
"last 4 iOS versions"
|
|
87
89
|
],
|
|
88
90
|
"dependencies": {
|
|
91
|
+
"@interactjs/inertia": "^1.10.27",
|
|
89
92
|
"material-icons": "^1.13.12",
|
|
90
93
|
"sass": "^1.70.0"
|
|
91
94
|
}
|
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
}"
|
|
8
8
|
:min-height="`${minHeight}px`"
|
|
9
9
|
:max-height="maxHeight != null ? `${maxHeight}px` : null"
|
|
10
|
-
paragraph-tag="
|
|
10
|
+
paragraph-tag="p"
|
|
11
11
|
v-bind="$attrs"
|
|
12
12
|
:definitions="toolDefinitions"
|
|
13
13
|
:toolbar="toolbarDefinitions"
|
|
14
14
|
v-model="model"
|
|
15
15
|
class="s-editor"
|
|
16
|
+
@update:model-value="updateModelValue"
|
|
16
17
|
v-on="usePlaintextPasting ? { paste: onPaste } : {}"
|
|
17
18
|
>
|
|
18
19
|
<template #upload>
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
<q-input
|
|
25
26
|
class="bg-white font-12-400"
|
|
26
27
|
style="width: 352px"
|
|
27
|
-
v-model="
|
|
28
|
+
v-model="imgValue"
|
|
28
29
|
dense
|
|
29
30
|
outlined
|
|
30
31
|
type="textarea"
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
</template>
|
|
51
52
|
|
|
52
53
|
<script>
|
|
53
|
-
import { defineComponent, ref, watch, computed } from 'vue';
|
|
54
|
+
import { defineComponent, ref, watch, computed, onMounted } from 'vue';
|
|
54
55
|
import {
|
|
55
56
|
extend,
|
|
56
57
|
QCard,
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
QBtn,
|
|
63
64
|
useQuasar,
|
|
64
65
|
} from 'quasar';
|
|
66
|
+
import resizeImage from '../composables/resizable';
|
|
65
67
|
|
|
66
68
|
export default defineComponent({
|
|
67
69
|
name: 'SEditor',
|
|
@@ -133,11 +135,77 @@
|
|
|
133
135
|
},
|
|
134
136
|
setup(props, { emit }) {
|
|
135
137
|
const model = ref(props.modelValue);
|
|
138
|
+
const editorRef = ref(null);
|
|
139
|
+
|
|
140
|
+
// delete handler element
|
|
141
|
+
function unwrapImg() {
|
|
142
|
+
const contentEl = editorRef.value.getContentEl();
|
|
143
|
+
const resizeHandler = contentEl.querySelectorAll('.img-resize-handler');
|
|
144
|
+
|
|
145
|
+
resizeHandler.forEach(div => {
|
|
146
|
+
const img = div.querySelector('img');
|
|
147
|
+
const parentParagraph = div.parentNode;
|
|
148
|
+
parentParagraph.replaceChild(img, div);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
model.value = contentEl.innerHTML;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// create handler element & resize event
|
|
155
|
+
function wrapImgWithDiv(targetImg) {
|
|
156
|
+
const newDiv = document.createElement('div');
|
|
157
|
+
newDiv.classList.add('img-resize-handler');
|
|
158
|
+
newDiv.style.height = `${targetImg.height}px`;
|
|
159
|
+
newDiv.style.width = `${targetImg.width}px`;
|
|
160
|
+
|
|
161
|
+
targetImg.parentNode.append(newDiv);
|
|
162
|
+
newDiv.append(targetImg);
|
|
163
|
+
resizeImage();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// add click event in contents
|
|
167
|
+
function addImageClickListener() {
|
|
168
|
+
if (!editorRef.value) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const contentEl = editorRef.value.getContentEl();
|
|
172
|
+
|
|
173
|
+
if (!contentEl) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
contentEl.addEventListener('click', event => {
|
|
178
|
+
const images = contentEl.querySelectorAll('img');
|
|
179
|
+
if (!images.length) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// when clicked, check image document in contents
|
|
184
|
+
if (Array.from(images).some(el => el.contains(event.target))) {
|
|
185
|
+
wrapImgWithDiv(event.target);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
unwrapImg();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function updateModelValue(val) {
|
|
194
|
+
model.value = val;
|
|
195
|
+
emit('update:modelValue', val);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
onMounted(() => {
|
|
199
|
+
const contentEl = editorRef.value.getContentEl();
|
|
200
|
+
if (contentEl) {
|
|
201
|
+
addImageClickListener();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
136
205
|
const $q = useQuasar();
|
|
137
206
|
|
|
138
207
|
// Toolbar Begin
|
|
139
|
-
|
|
140
|
-
const value = ref('');
|
|
208
|
+
const imgValue = ref('');
|
|
141
209
|
|
|
142
210
|
function setFontSize(fontSize) {
|
|
143
211
|
function isRootNode(node) {
|
|
@@ -410,13 +478,13 @@
|
|
|
410
478
|
};
|
|
411
479
|
|
|
412
480
|
function onOkClick() {
|
|
413
|
-
|
|
481
|
+
imgValue.value.split('\n').forEach((el, idx) => {
|
|
414
482
|
if (el === '') return;
|
|
415
|
-
model.value += `<p><img style="max-width:100%;" src=${el} :alt="image${idx}"/></p>`;
|
|
483
|
+
model.value += `<p><img style="max-width:100%;" src=${el} :alt="image${idx}" /></p>`;
|
|
416
484
|
});
|
|
417
485
|
|
|
418
|
-
|
|
419
|
-
|
|
486
|
+
addImageClickListener();
|
|
487
|
+
imgValue.value = '';
|
|
420
488
|
}
|
|
421
489
|
// Toolbar End
|
|
422
490
|
|
|
@@ -427,15 +495,6 @@
|
|
|
427
495
|
},
|
|
428
496
|
);
|
|
429
497
|
|
|
430
|
-
watch(
|
|
431
|
-
() => model.value,
|
|
432
|
-
() => {
|
|
433
|
-
emit('update:modelValue', model.value);
|
|
434
|
-
},
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
const editorRef = ref(null);
|
|
438
|
-
|
|
439
498
|
// https://quasar.dev/vue-components/editor#plaintext-pasting 참조
|
|
440
499
|
function onPaste(event) {
|
|
441
500
|
if (event.target.nodeName === 'INPUT') return;
|
|
@@ -464,12 +523,12 @@
|
|
|
464
523
|
return bar.map(group => group.map(item => (item === 'fontSizes' ? fontSizes : item)));
|
|
465
524
|
}),
|
|
466
525
|
toolDefinitions,
|
|
467
|
-
// isOpened,
|
|
468
526
|
model,
|
|
469
|
-
|
|
527
|
+
imgValue,
|
|
470
528
|
onOkClick,
|
|
471
529
|
onPaste,
|
|
472
530
|
editorRef,
|
|
531
|
+
updateModelValue,
|
|
473
532
|
};
|
|
474
533
|
},
|
|
475
534
|
});
|
|
@@ -495,5 +554,30 @@
|
|
|
495
554
|
}
|
|
496
555
|
}
|
|
497
556
|
}
|
|
557
|
+
.q-editor__content {
|
|
558
|
+
.img-resize-handler {
|
|
559
|
+
position: relative;
|
|
560
|
+
background-color: transparent;
|
|
561
|
+
display: inline-block;
|
|
562
|
+
resize: both;
|
|
563
|
+
overflow: hidden;
|
|
564
|
+
&:after {
|
|
565
|
+
content: '';
|
|
566
|
+
position: absolute;
|
|
567
|
+
border: 1px solid;
|
|
568
|
+
width: 100%;
|
|
569
|
+
height: 100%;
|
|
570
|
+
top: 0;
|
|
571
|
+
left: 0;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
> img {
|
|
575
|
+
display: block;
|
|
576
|
+
object-fit: contain;
|
|
577
|
+
width: 100%;
|
|
578
|
+
height: 100%;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
498
582
|
}
|
|
499
583
|
</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import interact from 'interactjs';
|
|
2
|
+
|
|
3
|
+
function resizeImage() {
|
|
4
|
+
|
|
5
|
+
interact('.img-resize-handler')
|
|
6
|
+
.origin('self')
|
|
7
|
+
.resizable({
|
|
8
|
+
// resize from all edges and corners
|
|
9
|
+
edges: { left: true, right: true, bottom: true, top: true },
|
|
10
|
+
|
|
11
|
+
listeners: {
|
|
12
|
+
move(event) {
|
|
13
|
+
const { target } = event
|
|
14
|
+
let x = (parseFloat(target.getAttribute('data-x')) || 0)
|
|
15
|
+
let y = (parseFloat(target.getAttribute('data-y')) || 0)
|
|
16
|
+
|
|
17
|
+
// update the element's style
|
|
18
|
+
target.style.width = `${event.rect.width}px`
|
|
19
|
+
target.style.height = `${event.rect.height}px`
|
|
20
|
+
|
|
21
|
+
// translate when resizing from top or left edges
|
|
22
|
+
x += event.deltaRect.left
|
|
23
|
+
y += event.deltaRect.top
|
|
24
|
+
|
|
25
|
+
target.style.transform = `translate(${x}px,${y}px)`
|
|
26
|
+
|
|
27
|
+
target.setAttribute('data-x', x)
|
|
28
|
+
target.setAttribute('data-y', y)
|
|
29
|
+
const imgEl = target.querySelector('img')
|
|
30
|
+
imgEl.width = target.style.width.replace('px', '')
|
|
31
|
+
imgEl.height = target.style.height.replace('px', '')
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
modifiers: [
|
|
35
|
+
// keep the edges inside the parent
|
|
36
|
+
interact.modifiers.restrictEdges({
|
|
37
|
+
outer: 'parent'
|
|
38
|
+
}),
|
|
39
|
+
|
|
40
|
+
// // minimum size
|
|
41
|
+
// interact.modifiers.restrictSize({
|
|
42
|
+
// min: { width: 100, height: 50 }
|
|
43
|
+
// })
|
|
44
|
+
interact.modifiers.aspectRatio({
|
|
45
|
+
// make sure the width is always double the height
|
|
46
|
+
ratio: 'preserve',
|
|
47
|
+
// also restrict the size by nesting another modifier
|
|
48
|
+
modifiers: [
|
|
49
|
+
interact.modifiers.restrictSize({ max: 'parent' }),
|
|
50
|
+
],
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
|
|
54
|
+
inertia: true
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default resizeImage
|
package/vitest.config.ts
CHANGED