@mixd-id/web-scaffold 0.1.230406003 → 0.1.230406005

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.230406003",
4
+ "version": "0.1.230406005",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -26,6 +26,9 @@
26
26
  <button class="p-3" type="button" @click="createLink">
27
27
  <svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>
28
28
  </button>
29
+ <button class="p-3" type="button" @click="removeFormat">
30
+ <svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>
31
+ </button>
29
32
  </div>
30
33
 
31
34
  <ContextMenu ref="headingContext">
@@ -105,7 +108,7 @@
105
108
  </template>
106
109
  <div class="flex-1 flex flex-col gap-5 p-5">
107
110
  <div>
108
- <Image class="min-h-[100px] bg-base-300 rounded-xl" ref="image" :src="newImage.file" editable="true"
111
+ <Image class="min-h-[100px] bg-base-300 rounded-xl" ref="image" :src="newImage.src" editable="true"
109
112
  @click="$refs.image.edit()" @change="onImageChanged">
110
113
  <template #empty="{ instance }">
111
114
  <div class="absolute right-0 top-0 bottom-0 left-0 flex items-center justify-center">
@@ -135,7 +138,7 @@
135
138
  </div>
136
139
  </Modal>
137
140
 
138
- <Modal ref="linkModal" width="360" height="360" dismissable="true">
141
+ <Modal ref="linkModal" width="360" height="400" dismissable="true">
139
142
  <template v-slot:head>
140
143
  <div class="relative p-5">
141
144
  <h3>Add Link</h3>
@@ -149,8 +152,9 @@
149
152
  </div>
150
153
  </template>
151
154
  <template v-slot:foot>
152
- <div class="p-5">
153
- <Button class="w-full" :disabled="!linkCanSave" @click="addLink">Add Link</Button>
155
+ <div class="p-5 flex flex-row gap-3">
156
+ <Button variant="secondary" class="w-[100px]" :disabled="!linkCanSave" @click="removeLink">Remove</Button>
157
+ <Button class="flex-1" :disabled="!linkCanSave" @click="addLink">Add Link</Button>
154
158
  </div>
155
159
  </template>
156
160
  <div class="flex-1 flex flex-col gap-5 p-5">
@@ -174,8 +178,8 @@
174
178
  </div>
175
179
  </Modal>
176
180
 
177
- <article ref="article" contenteditable="true" v-html="html" @paste="onPaste"
178
- @input="onInput"
181
+ <article ref="article" contenteditable="true" v-html="html" @paste="onPaste" spellcheck="false"
182
+ @input="onInput" @click="onClick"
179
183
  @blur="onBlur">
180
184
  </article>
181
185
 
@@ -184,13 +188,16 @@
184
188
 
185
189
  <script>
186
190
 
187
- import * as Vue from "vue/dist/vue.esm-bundler";
188
191
  import {restoreSelection, saveSelection} from "../utils/selection";
189
192
  import {downsizeImage} from "../utils/helpers.mjs";
190
193
  import axios from "axios";
191
194
 
192
195
  export default{
193
196
 
197
+ emits: [ 'change', 'update:modelValue' ],
198
+
199
+ inject: [ 'alert' ],
200
+
194
201
  props:{
195
202
  modelValue: String,
196
203
  uploadImage: String
@@ -228,6 +235,27 @@ export default{
228
235
  this.updateModelValue()
229
236
  },
230
237
 
238
+ onClick(e){
239
+
240
+ if(e.target.tagName === 'IMG'){
241
+ this.newImage = {
242
+ src: e.target.src,
243
+ width: e.target.getAttribute('width'),
244
+ height: e.target.getAttribute('height'),
245
+ maxWidth: e.target.style.maxWidth,
246
+ maxHeight: e.target.style.maxHeight,
247
+ element: e.target
248
+ }
249
+ }
250
+ else{
251
+ this.newImage = {
252
+ width: "",
253
+ height: "",
254
+ element: null
255
+ }
256
+ }
257
+ },
258
+
231
259
  onBlur(){
232
260
  this.$emit('change')
233
261
  },
@@ -249,16 +277,25 @@ export default{
249
277
 
250
278
  createLink(){
251
279
 
252
- const { text = '' } = saveSelection(this.$refs.article)
280
+ let { text = '', element, href = 'https://', target = '' } = saveSelection(this.$refs.article)
281
+
282
+ if(element && element.parentNode && element.parentNode.tagName === 'A'){
283
+ href = element.parentNode.href
284
+ target = element.parentNode.target
285
+ }
253
286
 
254
287
  this.newLink = {
255
- href: 'https://',
288
+ href,
256
289
  text,
257
- target: ''
290
+ target
258
291
  }
259
292
  this.$refs.linkModal.open()
260
293
  },
261
294
 
295
+ removeFormat(){
296
+ document.execCommand('removeFormat', false);
297
+ },
298
+
262
299
  addLink(){
263
300
 
264
301
  restoreSelection()
@@ -274,20 +311,29 @@ export default{
274
311
  this.$refs.linkModal.close()
275
312
  },
276
313
 
314
+ removeLink(){
315
+
316
+ const { element } = saveSelection(this.$refs.article)
317
+
318
+ if(element && element.parentNode && element.parentNode.tagName === 'A'){
319
+ element.parentNode.parentNode.replaceChild(element, element.parentNode)
320
+ }
321
+
322
+ this.newLink = null
323
+ this.$refs.linkModal.close()
324
+
325
+ this.updateModelValue()
326
+ },
327
+
277
328
  createImage(){
278
329
 
279
330
  saveSelection(this.$refs.article)
280
331
 
281
- this.newImage = {
282
- width: "",
283
- height: ""
284
- }
285
332
  this.$refs.imageModal.open()
286
333
  },
287
334
 
288
335
  onImageChanged(base64, file){
289
- this.newImage.base64 = base64
290
- this.newImage.file = file
336
+ this.newImage.src = file
291
337
  },
292
338
 
293
339
  onImageAdd(){
@@ -322,39 +368,52 @@ export default{
322
368
 
323
369
  async saveImage(){
324
370
 
325
- const blob = await downsizeImage(this.newImage.file, this.newImage.width ?? 600)
371
+ const blob = await downsizeImage(this.newImage.src, this.newImage.width ?? 600)
326
372
 
327
373
  const data = new FormData()
328
374
  data.append('image', blob)
329
375
 
330
- const res = await axios.post(this.uploadImage,
331
- data,
332
- {
333
- headers: {
334
- "Content-Type": "multipart/form-data",
335
- },
376
+ try{
377
+ const res = await axios.post(this.uploadImage,
378
+ data,
379
+ {
380
+ headers: {
381
+ "Content-Type": "multipart/form-data",
382
+ },
383
+ }
384
+ )
385
+
386
+ const { url } = res.data
387
+
388
+ restoreSelection()
389
+
390
+ const props = {}
391
+ if(this.newImage.width)
392
+ props.width = this.newImage.width
393
+ if(this.newImage.height)
394
+ props.height = this.newImage.height
395
+
396
+ let propHtml = []
397
+ for(let key in props){
398
+ propHtml.push(`${key}='${props[key]}'`)
336
399
  }
337
- )
338
-
339
- const { url } = res.data
340
400
 
341
- restoreSelection()
401
+ if(this.newImage.element){
402
+ const aDiv = document.createElement("div")
403
+ aDiv.innerHTML = "<img src=\"" + url + "\" " + propHtml.join(' ') + "/>"
404
+ this.newImage.element.parentNode.replaceChild(aDiv.firstChild, this.newImage.element)
405
+ }
342
406
 
343
- const props = {}
344
- if(this.newImage.width)
345
- props.width = this.newImage.width
346
- if(this.newImage.height)
347
- props.height = this.newImage.height
407
+ else{
408
+ document.execCommand("insertHTML", false,
409
+ "<img src=\"" + url + "\" " + propHtml.join(' ') + "/>");
410
+ }
348
411
 
349
- let propHtml = []
350
- for(let key in props){
351
- propHtml.push(`${key}='${props[key]}'`)
412
+ this.$refs.imageModal.close()
413
+ }
414
+ catch(e){
415
+ this.alert(e.response.data)
352
416
  }
353
-
354
- document.execCommand("insertHTML", false,
355
- "<img src=\"" + url + "\" " + propHtml.join(' ') + "/>");
356
-
357
- this.$refs.imageModal.close()
358
417
  },
359
418
 
360
419
  onPaste(e){
@@ -373,7 +432,7 @@ export default{
373
432
  updateModelValue(){
374
433
 
375
434
  let html = this.$refs.article.innerHTML
376
- html = html.replaceAll("<a ", "<Ahref ").replaceAll("</a>", "</Ahref>")
435
+ //html = html.replaceAll("<a ", "<Ahref ").replaceAll("</a>", "</Ahref>")
377
436
 
378
437
  this.$emit('update:modelValue', html)
379
438
  }
@@ -387,10 +446,20 @@ export default{
387
446
  <style module>
388
447
 
389
448
  .comp{
449
+ @apply flex-1 flex flex-col;
390
450
  }
391
451
 
392
452
  .comp article {
393
- @apply min-h-[var(--h-cp)] w-full outline-none p-2;
453
+ @apply min-h-[var(--h-cp)] w-full outline-none p-2 leading-6;
454
+ @apply flex-1 overflow-auto;
455
+ }
456
+
457
+ .comp article a{
458
+ @apply text-primary underline;
459
+ }
460
+
461
+ .comp ol li{
462
+ @apply list-disc ml-6;
394
463
  }
395
464
 
396
465
  </style>
@@ -904,9 +904,9 @@ export default{
904
904
  },
905
905
 
906
906
  mounted() {
907
- this.patch()
908
907
  this.socket.onAny(this.onHooks)
909
908
  window.addEventListener('focus', this.onFocus)
909
+ window.setTimeout(() => { this.patch() }, 300)
910
910
  },
911
911
 
912
912
  unmounted() {
@@ -166,6 +166,28 @@ const bufferToStream = (buffer) => {
166
166
  return stream
167
167
  }
168
168
 
169
+ const sequelizeChunk = async (model, opt, callback, chunkSize = 5000) => {
170
+
171
+ let offset = 0
172
+ let iter = 0
173
+ while(true){
174
+ const items = await model.findAll({
175
+ ...opt,
176
+ offset,
177
+ limit: chunkSize
178
+ })
179
+
180
+ if(items.length > 0){
181
+ await callback(items, iter)
182
+ offset += chunkSize
183
+ iter++
184
+ }
185
+ else{
186
+ break
187
+ }
188
+ }
189
+ }
190
+
169
191
 
170
192
  module.exports = {
171
193
  ceil,
@@ -181,5 +203,6 @@ module.exports = {
181
203
  strSlug,
182
204
  strVars,
183
205
  writeStorage,
184
- bufferToStream
206
+ bufferToStream,
207
+ sequelizeChunk
185
208
  }
@@ -30,9 +30,9 @@ const csvToArray = (str, delimiter = null) => {
30
30
  const downsizeImage = async (url, targetWidth, imageType, quality, opt = {}) => {
31
31
 
32
32
  let defaultImageType = 'image/jpeg'
33
+ targetWidth = isNaN(parseInt(targetWidth)) ? 2048 : parseInt(targetWidth)
33
34
 
34
35
  if(url instanceof File){
35
-
36
36
  defaultImageType = url.type
37
37
 
38
38
  await (new Promise((resolve, reject) => {
@@ -16,7 +16,8 @@ const saveSelection = (container) =>{
16
16
  start: start,
17
17
  end: start + range.toString().length,
18
18
  container,
19
- text: range.toString()
19
+ text: range.toString(),
20
+ element: range.startContainer
20
21
  }
21
22
 
22
23
  return selection
@@ -56,6 +57,8 @@ const restoreSelection = () => {
56
57
  const sel = window.getSelection();
57
58
  sel.removeAllRanges();
58
59
  sel.addRange(range);
60
+
61
+ return range
59
62
  }
60
63
 
61
64
  export {