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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-sellmate-ui-kit",
3
- "version": "3.4.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="div"
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="value"
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
- // const isOpened = ref(false);
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
- value.value.split('\n').forEach((el, idx) => {
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
- // isOpened.value = false;
419
- value.value = '';
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
- value,
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
@@ -16,6 +16,9 @@ export default defineConfig({
16
16
  'test/vitest/__tests__/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}',
17
17
  ],
18
18
  },
19
+ rollupoptions: {
20
+ context: 'window',
21
+ },
19
22
  plugins: [
20
23
  vue({
21
24
  template: { transformAssetUrls },