quasar-ui-sellmate-ui-kit 3.4.3 → 3.5.1
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 +207 -81
- package/dist/index.umd.min.js +2 -2
- package/package.json +4 -1
- package/src/components/SEditor.vue +104 -16
- package/src/composables/resizable.js +55 -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.1",
|
|
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,7 +7,7 @@
|
|
|
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"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<q-input
|
|
25
25
|
class="bg-white font-12-400"
|
|
26
26
|
style="width: 352px"
|
|
27
|
-
v-model="
|
|
27
|
+
v-model="imgValue"
|
|
28
28
|
dense
|
|
29
29
|
outlined
|
|
30
30
|
type="textarea"
|
|
@@ -47,10 +47,11 @@
|
|
|
47
47
|
</q-btn>
|
|
48
48
|
</template>
|
|
49
49
|
</q-editor>
|
|
50
|
+
{{ model }}
|
|
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,72 @@
|
|
|
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
|
+
const className = 'img-resize-handler';
|
|
158
|
+
newDiv.classList.add(className);
|
|
159
|
+
newDiv.style.height = `${targetImg.height}px`;
|
|
160
|
+
newDiv.style.width = `${targetImg.width}px`;
|
|
161
|
+
targetImg.parentNode.insertBefore(newDiv, targetImg);
|
|
162
|
+
newDiv.append(targetImg);
|
|
163
|
+
resizeImage(className);
|
|
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
|
+
onMounted(() => {
|
|
194
|
+
const contentEl = editorRef.value.getContentEl();
|
|
195
|
+
if (contentEl) {
|
|
196
|
+
addImageClickListener();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
136
200
|
const $q = useQuasar();
|
|
137
201
|
|
|
138
202
|
// Toolbar Begin
|
|
139
|
-
|
|
140
|
-
const value = ref('');
|
|
203
|
+
const imgValue = ref('');
|
|
141
204
|
|
|
142
205
|
function setFontSize(fontSize) {
|
|
143
206
|
function isRootNode(node) {
|
|
@@ -410,13 +473,13 @@
|
|
|
410
473
|
};
|
|
411
474
|
|
|
412
475
|
function onOkClick() {
|
|
413
|
-
|
|
476
|
+
imgValue.value.split('\n').forEach((el, idx) => {
|
|
414
477
|
if (el === '') return;
|
|
415
|
-
model.value += `<p><img style="max-width:100%;" src=${el} :alt="image${idx}"/></p>`;
|
|
478
|
+
model.value += `<p><img style="max-width:100%;" src=${el} :alt="image${idx}" /></p>`;
|
|
416
479
|
});
|
|
417
480
|
|
|
418
|
-
|
|
419
|
-
|
|
481
|
+
addImageClickListener();
|
|
482
|
+
imgValue.value = '';
|
|
420
483
|
}
|
|
421
484
|
// Toolbar End
|
|
422
485
|
|
|
@@ -426,16 +489,14 @@
|
|
|
426
489
|
model.value = newValue;
|
|
427
490
|
},
|
|
428
491
|
);
|
|
429
|
-
|
|
430
492
|
watch(
|
|
431
493
|
() => model.value,
|
|
432
|
-
|
|
433
|
-
emit('update:modelValue',
|
|
494
|
+
newValue => {
|
|
495
|
+
emit('update:modelValue', newValue);
|
|
434
496
|
},
|
|
497
|
+
{ deep: true },
|
|
435
498
|
);
|
|
436
499
|
|
|
437
|
-
const editorRef = ref(null);
|
|
438
|
-
|
|
439
500
|
// https://quasar.dev/vue-components/editor#plaintext-pasting 참조
|
|
440
501
|
function onPaste(event) {
|
|
441
502
|
if (event.target.nodeName === 'INPUT') return;
|
|
@@ -464,9 +525,8 @@
|
|
|
464
525
|
return bar.map(group => group.map(item => (item === 'fontSizes' ? fontSizes : item)));
|
|
465
526
|
}),
|
|
466
527
|
toolDefinitions,
|
|
467
|
-
// isOpened,
|
|
468
528
|
model,
|
|
469
|
-
|
|
529
|
+
imgValue,
|
|
470
530
|
onOkClick,
|
|
471
531
|
onPaste,
|
|
472
532
|
editorRef,
|
|
@@ -495,5 +555,33 @@
|
|
|
495
555
|
}
|
|
496
556
|
}
|
|
497
557
|
}
|
|
558
|
+
.q-editor__content {
|
|
559
|
+
> p > img {
|
|
560
|
+
display: inline-block;
|
|
561
|
+
}
|
|
562
|
+
.img-resize-handler {
|
|
563
|
+
position: relative;
|
|
564
|
+
background-color: transparent;
|
|
565
|
+
display: inline-block;
|
|
566
|
+
resize: both;
|
|
567
|
+
overflow: hidden;
|
|
568
|
+
&:after {
|
|
569
|
+
content: '';
|
|
570
|
+
position: absolute;
|
|
571
|
+
border: 1px solid;
|
|
572
|
+
width: 100%;
|
|
573
|
+
height: 100%;
|
|
574
|
+
top: 0;
|
|
575
|
+
left: 0;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
> img {
|
|
579
|
+
display: inline-block;
|
|
580
|
+
object-fit: contain;
|
|
581
|
+
width: 100%;
|
|
582
|
+
height: 100%;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
498
586
|
}
|
|
499
587
|
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import interact from 'interactjs';
|
|
2
|
+
|
|
3
|
+
function resizeImage(className) {
|
|
4
|
+
interact(`.${className}`)
|
|
5
|
+
.origin('self')
|
|
6
|
+
.resizable({
|
|
7
|
+
// resize from all edges and corners
|
|
8
|
+
edges: { left: true, right: true, bottom: true, top: true },
|
|
9
|
+
|
|
10
|
+
listeners: {
|
|
11
|
+
move(event) {
|
|
12
|
+
const { target } = event
|
|
13
|
+
let x = (parseFloat(target.getAttribute('data-x')) || 0)
|
|
14
|
+
let y = (parseFloat(target.getAttribute('data-y')) || 0)
|
|
15
|
+
|
|
16
|
+
// update the element's style
|
|
17
|
+
target.style.width = `${event.rect.width}px`
|
|
18
|
+
target.style.height = `${event.rect.height}px`
|
|
19
|
+
|
|
20
|
+
// translate when resizing from top or left edges
|
|
21
|
+
x += event.deltaRect.left
|
|
22
|
+
y += event.deltaRect.top
|
|
23
|
+
|
|
24
|
+
target.style.transform = `translate(${x}px,${y}px)`
|
|
25
|
+
|
|
26
|
+
target.setAttribute('data-x', x)
|
|
27
|
+
target.setAttribute('data-y', y)
|
|
28
|
+
const imgEl = target.querySelector('img')
|
|
29
|
+
if (imgEl) {
|
|
30
|
+
imgEl.width = target.style.width.replace('px', '')
|
|
31
|
+
imgEl.height = target.style.height.replace('px', '')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
modifiers: [
|
|
36
|
+
// keep the edges inside the parent
|
|
37
|
+
interact.modifiers.restrictEdges({
|
|
38
|
+
outer: 'parent'
|
|
39
|
+
}),
|
|
40
|
+
|
|
41
|
+
interact.modifiers.aspectRatio({
|
|
42
|
+
// make sure the width is always double the height
|
|
43
|
+
ratio: 'preserve',
|
|
44
|
+
// also restrict the size by nesting another modifier
|
|
45
|
+
modifiers: [
|
|
46
|
+
interact.modifiers.restrictSize({ max: 'parent' }),
|
|
47
|
+
],
|
|
48
|
+
}),
|
|
49
|
+
],
|
|
50
|
+
|
|
51
|
+
inertia: true
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default resizeImage
|
package/vitest.config.ts
CHANGED