@mixd-id/web-scaffold 0.1.230406106 → 0.1.230406108

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.230406106",
4
+ "version": "0.1.230406108",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -43,6 +43,7 @@
43
43
  "glob": "^8.0.3",
44
44
  "lodash": "^4.17.21",
45
45
  "md5": "^2.3.0",
46
+ "monaco-editor": "^0.41.0",
46
47
  "nprogress": "^0.2.0",
47
48
  "pinia": "^2.0.14",
48
49
  "prismjs": "^1.28.0",
@@ -10,7 +10,7 @@
10
10
  <script>
11
11
 
12
12
  import { componentMixin } from '../mixin/component';
13
- import { getSelection } from "../utils/selection";
13
+ import {getSelection, restoreSelection, saveSelection} from "../utils/selection";
14
14
 
15
15
  export default{
16
16
 
@@ -24,6 +24,27 @@ export default{
24
24
 
25
25
  methods: {
26
26
 
27
+ getSelection(){
28
+ const { text = '', href = '', target = '' } = saveSelection(this.$el)
29
+ return {
30
+ text,
31
+ href,
32
+ target
33
+ }
34
+ },
35
+
36
+ addLink(link){
37
+
38
+ restoreSelection()
39
+
40
+ document.execCommand("insertHTML", false,
41
+ "<a style='font-size:inherit;font-family:inherit;background-color:inherit' " +
42
+ "href=\"" + link.href + "\" " +
43
+ "target=\"" + link.target + "\">" +
44
+ link.text +
45
+ "</a>");
46
+ },
47
+
27
48
  onInput(){
28
49
  if(this.editable){
29
50
  this.triggerUpdate()
@@ -198,13 +219,72 @@ export default{
198
219
 
199
220
  mounted() {
200
221
  if('edit-mode' in this.$route.query){
201
- window.addEventListener('message', ({ data }) => {
222
+ window.addEventListener('message', (e) => {
223
+
224
+ const { data, source } = e
225
+
202
226
  if(data.uid === this.uid){
203
227
 
204
- const { action, type, value } = data.data
228
+ const { action, type, value } = data.data ?? {}
229
+
230
+ let result
205
231
 
206
232
  switch(action){
207
233
 
234
+ case 'saveSelection':
235
+ case 'getSelection':
236
+ const sel = saveSelection(this.$el)
237
+ const { text, target, href } = sel
238
+ result = {
239
+ text,
240
+ target,
241
+ href
242
+ }
243
+ break
244
+
245
+ case 'restoreSelection':
246
+ restoreSelection()
247
+ break
248
+
249
+ case 'insertLink':
250
+ restoreSelection()
251
+
252
+ document.execCommand("insertHTML", false,
253
+ "<a style='font-size:inherit;font-family:inherit;background-color:inherit' " +
254
+ "href=\"" + value.href + "\" " +
255
+ "target=\"" + value.target + "\">" +
256
+ value.text +
257
+ "</a>");
258
+ break
259
+
260
+ case 'removeLink':
261
+ const { element } = getSelection(this.$el)
262
+
263
+ let a = element
264
+ while(a && a.tagName !== 'A'){
265
+ a = a.parentNode
266
+ }
267
+ if(a){
268
+ a.parentNode.replaceChild(element, a)
269
+ }
270
+ break
271
+
272
+ case 'insertImage':
273
+ const props = {}
274
+ if(value.width)
275
+ props.width = parseInt(value.width) + 'px'
276
+ if(value.height)
277
+ props.height = parseInt(value.height) + 'px'
278
+
279
+ let propHtml = []
280
+ for(let key in props){
281
+ propHtml.push(`${key}='${props[key]}'`)
282
+ }
283
+
284
+ document.execCommand("insertHTML", false,
285
+ "<img src=\"" + value.url + "\" " + propHtml.join(' ') + "/>");
286
+ break
287
+
208
288
  case 'insertTable':
209
289
  this.insertTable()
210
290
  break
@@ -241,7 +321,9 @@ export default{
241
321
 
242
322
  }
243
323
 
324
+ source.postMessage(result, '*')
244
325
  }
326
+
245
327
  })
246
328
  this.editable = true
247
329
  }
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+ <div class="flex flex-row p-2">
4
+ <code>{{ language }}</code>
5
+ <div class="flex-1"></div>
6
+ <button type="button" class="text-primary" @click="reformat">Beautify</button>
7
+ </div>
8
+ <div ref="editorContainer" :class="$style['monaco-editor-container']"></div>
9
+ </div>
10
+ </template>
11
+
12
+ <script>
13
+ import * as monaco from 'monaco-editor'
14
+ import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
15
+ import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
16
+ import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
17
+ import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
18
+ import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
19
+
20
+ self.MonacoEnvironment = {
21
+ getWorker(_, label) {
22
+ if (label === 'json') {
23
+ return new jsonWorker()
24
+ }
25
+ if (label === 'css' || label === 'scss' || label === 'less') {
26
+ return new cssWorker()
27
+ }
28
+ if (label === 'html' || label === 'handlebars' || label === 'razor') {
29
+ return new htmlWorker()
30
+ }
31
+ if (label === 'typescript' || label === 'javascript') {
32
+ return new tsWorker()
33
+ }
34
+ return new editorWorker()
35
+ }
36
+ }
37
+
38
+ export default {
39
+
40
+ props: {
41
+
42
+ language: {
43
+ type: String,
44
+ default: 'json'
45
+ },
46
+
47
+ theme: {
48
+ type: String,
49
+ default: 'vs-dark'
50
+ },
51
+
52
+ lineNumbers: {
53
+ type: String,
54
+ default: "off"
55
+ },
56
+
57
+ modelValue: String
58
+
59
+ },
60
+
61
+ methods: {
62
+
63
+ createEditor() {
64
+ this.editor = monaco.editor.create(this.$refs.editorContainer, {
65
+ value: this.modelValue,
66
+ language: this.language,
67
+ lineNumbers: this.lineNumbers,
68
+ minimap: {
69
+ enabled: false
70
+ },
71
+ theme: this.theme
72
+ });
73
+
74
+ this.editor.onDidChangeModelContent(event => {
75
+ const updatedCode = this.editor.getValue();
76
+ this.$emit('update:modelValue', updatedCode)
77
+ });
78
+ },
79
+
80
+ getErrors(){
81
+ const markers = monaco.editor.getModelMarkers({ owner: this.editor.getModel() });
82
+
83
+ let errors = []
84
+ if (markers.length >= 0) {
85
+ markers.forEach(marker => {
86
+ errors.push({
87
+ message: marker.message,
88
+ line: marker.startLineNumber,
89
+ column: marker.startColumn
90
+ })
91
+ });
92
+ }
93
+
94
+ return errors.length > 0 ? errors : null
95
+ },
96
+
97
+ reformat(){
98
+ const formatAction = this.editor.getAction('editor.action.formatDocument');
99
+ formatAction.run();
100
+ }
101
+
102
+ },
103
+
104
+ beforeDestroy() {
105
+ this.editor.dispose();
106
+ },
107
+
108
+ mounted() {
109
+ this.createEditor();
110
+ }
111
+
112
+ };
113
+ </script>
114
+
115
+ <style module>
116
+
117
+ .comp{
118
+ @apply flex-1 flex flex-col;
119
+ }
120
+
121
+ .monaco-editor-container {
122
+ width: 100%;
123
+ height: 300px;
124
+ }
125
+
126
+ .monaco-editor-container *{
127
+ font-family: Menlo, Monaco, "Courier New", monospace;
128
+ --vscode-editor-background: rgb(var(--base-300));
129
+ --vscode-editorGutter-background: rgb(var(--base-400));
130
+ }
131
+
132
+ .monaco-editor-container .margin-view-overlays .line-numbers {
133
+ font-family: 'Menlo', monospace;
134
+ }
135
+
136
+ </style>
package/src/index.js CHANGED
@@ -321,6 +321,7 @@ export default{
321
321
  app.component('Grid', defineAsyncComponent(() => import("./components/Grid.vue")))
322
322
  app.component('Modal', defineAsyncComponent(() => import("./components/Modal.vue")))
323
323
  app.component('Modal2', defineAsyncComponent(() => import("./components/Modal2.vue")))
324
+ app.component('MonacoEditor', defineAsyncComponent(() => import("./components/MonacoEditor.vue")))
324
325
  app.component('OTPField', defineAsyncComponent(() => import("./components/OTPField.vue")))
325
326
  app.component('PageBuilder', defineAsyncComponent(() => import("./components/PageBuilder.vue")))
326
327
  app.component('Radio', defineAsyncComponent(() => import("./components/Radio.vue")))
@@ -22,6 +22,12 @@
22
22
  <button class="p-3" type="button" ref="listBtn" @click="$refs.listContext.open($refs.listBtn)">
23
23
  <svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M64 144c26.5 0 48-21.5 48-48s-21.5-48-48-48S16 69.5 16 96s21.5 48 48 48zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zM64 464c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm48-208c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48z"/></svg>
24
24
  </button>
25
+ <button class="p-3" type="button" @click="createImage">
26
+ <svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M152 120c-26.51 0-48 21.49-48 48s21.49 48 48 48s48-21.49 48-48S178.5 120 152 120zM447.1 32h-384C28.65 32-.0091 60.65-.0091 96v320c0 35.35 28.65 64 63.1 64h384c35.35 0 64-28.65 64-64V96C511.1 60.65 483.3 32 447.1 32zM463.1 409.3l-136.8-185.9C323.8 218.8 318.1 216 312 216c-6.113 0-11.82 2.768-15.21 7.379l-106.6 144.1l-37.09-46.1c-3.441-4.279-8.934-6.809-14.77-6.809c-5.842 0-11.33 2.529-14.78 6.809l-75.52 93.81c0-.0293 0 .0293 0 0L47.99 96c0-8.822 7.178-16 16-16h384c8.822 0 16 7.178 16 16V409.3z"/></svg>
27
+ </button>
28
+ <button class="p-3" type="button" @click="insertLink">
29
+ <svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M314.222 197.78c51.091 51.091 54.377 132.287 9.75 187.16-6.242 7.73-2.784 3.865-84.94 86.02-54.696 54.696-143.266 54.745-197.99 0-54.711-54.69-54.734-143.255 0-197.99 32.773-32.773 51.835-51.899 63.409-63.457 7.463-7.452 20.331-2.354 20.486 8.192a173.31 173.31 0 0 0 4.746 37.828c.966 4.029-.272 8.269-3.202 11.198L80.632 312.57c-32.755 32.775-32.887 85.892 0 118.8 32.775 32.755 85.892 32.887 118.8 0l75.19-75.2c32.718-32.725 32.777-86.013 0-118.79a83.722 83.722 0 0 0-22.814-16.229c-4.623-2.233-7.182-7.25-6.561-12.346 1.356-11.122 6.296-21.885 14.815-30.405l4.375-4.375c3.625-3.626 9.177-4.594 13.76-2.294 12.999 6.524 25.187 15.211 36.025 26.049zM470.958 41.04c-54.724-54.745-143.294-54.696-197.99 0-82.156 82.156-78.698 78.29-84.94 86.02-44.627 54.873-41.341 136.069 9.75 187.16 10.838 10.838 23.026 19.525 36.025 26.049 4.582 2.3 10.134 1.331 13.76-2.294l4.375-4.375c8.52-8.519 13.459-19.283 14.815-30.405.621-5.096-1.938-10.113-6.561-12.346a83.706 83.706 0 0 1-22.814-16.229c-32.777-32.777-32.718-86.065 0-118.79l75.19-75.2c32.908-32.887 86.025-32.755 118.8 0 32.887 32.908 32.755 86.025 0 118.8l-45.848 45.84c-2.93 2.929-4.168 7.169-3.202 11.198a173.31 173.31 0 0 1 4.746 37.828c.155 10.546 13.023 15.644 20.486 8.192 11.574-11.558 30.636-30.684 63.409-63.457 54.733-54.735 54.71-143.3-.001-197.991z"/></svg>
30
+ </button>
25
31
  <button class="p-3" type="button" @click="insertTable">
26
32
  <svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M464 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM224 416H64v-96h160v96zm0-160H64v-96h160v96zm224 160H288v-96h160v96zm0-160H288v-96h160v96z"/></svg>
27
33
  </button>
@@ -216,6 +222,94 @@
216
222
  </div>
217
223
  </ContextMenu>
218
224
 
225
+ <Modal ref="linkModal" width="360" height="400" dismissable="true">
226
+ <template v-slot:head>
227
+ <div class="relative p-5">
228
+ <h3>Add Link</h3>
229
+ <div class="absolute top-0 right-0 p-2">
230
+ <button type="button" class="p-2" @click="$refs.linkModal.close()">
231
+ <svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
232
+ <path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
233
+ </svg>
234
+ </button>
235
+ </div>
236
+ </div>
237
+ </template>
238
+ <template v-slot:foot="{ context }">
239
+ <div class="p-5 flex flex-row gap-3">
240
+ <Button variant="secondary" class="w-[100px]" @click="removeLink">Remove</Button>
241
+ <Button class="flex-1" @click="addLink(context)">Add Link</Button>
242
+ </div>
243
+ </template>
244
+ <template #default="{ context }">
245
+ <div class="flex-1 flex flex-col gap-5 p-5">
246
+ <div>
247
+ <label>Text</label>
248
+ <Textbox class="w-full mt-1" v-model="context.text" />
249
+ </div>
250
+ <div>
251
+ <label>Href</label>
252
+ <Textbox class="w-full mt-1" v-model="context.href" />
253
+ </div>
254
+ <div>
255
+ <label>Target</label>
256
+ <Dropdown class="w-[150px] mt-1" v-model="context.target">
257
+ <option value="">_self</option>
258
+ <option value="_blank">_blank</option>
259
+ <option value="_parent">_parent</option>
260
+ <option value="_top">_top</option>
261
+ </Dropdown>
262
+ </div>
263
+ </div>
264
+ </template>
265
+ </Modal>
266
+
267
+ <Modal ref="imageModal" width="360" height="420" dismissable="true">
268
+ <template v-slot:head>
269
+ <div class="relative p-5">
270
+ <h3>Add Image</h3>
271
+ <div class="absolute top-0 right-0 p-2">
272
+ <button type="button" class="p-2" @click="$refs.imageModal.close()">
273
+ <svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
274
+ <path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
275
+ </svg>
276
+ </button>
277
+ </div>
278
+ </div>
279
+ </template>
280
+ <template #foot="{ context }">
281
+ <div class="p-5">
282
+ <Button class="w-full" @click="addImage(context)" :state="!context.src ? -1 : 1">Add Image</Button>
283
+ </div>
284
+ </template>
285
+ <template #default="{ context }">
286
+ <div class="flex-1 flex flex-col gap-5 p-5">
287
+ <div>
288
+ <Image class="min-h-[100px] bg-base-300 rounded-xl" ref="image" :src="context.src" editable="true"
289
+ @click="$refs.image.edit()" @change="(_, file) => context.src = file">
290
+ <template #empty="{ instance }">
291
+ <div class="absolute right-0 top-0 bottom-0 left-0 flex items-center justify-center">
292
+ Click to Add Image...
293
+ </div>
294
+ </template>
295
+ </Image>
296
+ </div>
297
+ <div class="grid grid-cols-2 gap-5">
298
+ <div>
299
+ <label>Width</label>
300
+ <Textbox class="mt-1" v-model="context.width" maxlength="3" />
301
+ </div>
302
+ <div>
303
+ <label>Height</label>
304
+ <Textbox class="mt-1" v-model="context.height" maxlength="3" />
305
+ </div>
306
+ </div>
307
+ <br />
308
+ <br />
309
+ </div>
310
+ </template>
311
+ </Modal>
312
+
219
313
  <ComponentSetting :item="item"
220
314
  :view-type="viewType"
221
315
  :view-types="viewTypes"
@@ -227,10 +321,16 @@
227
321
 
228
322
  <script>
229
323
 
324
+ import {restoreSelection} from "../utils/selection";
325
+ import {downsizeImage} from "../utils/helpers.mjs";
326
+ import axios from "axios";
327
+
230
328
  export default{
231
329
 
232
330
  emits: [ 'change', 'postMessageToIframe' ],
233
331
 
332
+ inject: [ 'alert', 'postIframe' ],
333
+
234
334
  props: {
235
335
 
236
336
  item: {
@@ -246,6 +346,64 @@ export default{
246
346
 
247
347
  methods: {
248
348
 
349
+ createImage(){
350
+ this.postIframe({ uid:this.item.uid, data: { action: 'saveSelection' } })
351
+ .then(_ => {
352
+ this.$refs.imageModal.open()
353
+ })
354
+ },
355
+
356
+ async addImage(image){
357
+
358
+ this.postIframe({ uid:this.item.uid, data: { action: 'restoreSelection' }})
359
+ .then(_ => {
360
+
361
+ downsizeImage(image.src, image.width ?? 300)
362
+ .then(async (blob) => {
363
+
364
+ const data = new FormData()
365
+ data.append('image', blob)
366
+
367
+ try{
368
+ const res = await axios.post(import.meta.env.VITE_API_HOST + '/import/image',
369
+ data,
370
+ {
371
+ headers: {
372
+ "Content-Type": "multipart/form-data",
373
+ },
374
+ }
375
+ )
376
+ image.url = res.data.url
377
+
378
+ const value = JSON.parse(JSON.stringify(image))
379
+ await this.postIframe({ uid:this.item.uid, data:{ action: 'insertImage', value }})
380
+ }
381
+ catch(e){
382
+ this.alert(e)
383
+ }
384
+ })
385
+ .finally(_ => {
386
+ this.$refs.imageModal.close()
387
+ })
388
+
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]}'`)
399
+ }
400
+
401
+ document.execCommand("insertHTML", false,
402
+ "<img src=\"" + this.newImage.base64 + "\" " + propHtml.join(' ') + "/>");*/
403
+
404
+ })
405
+ },
406
+
249
407
  format(type, value){
250
408
 
251
409
  switch(type){
@@ -259,6 +417,40 @@ export default{
259
417
  }
260
418
  },
261
419
 
420
+ insertLink(){
421
+ this.postIframe({
422
+ uid:this.item.uid,
423
+ data: {
424
+ action: 'getSelection'
425
+ }
426
+ })
427
+ .then(res => {
428
+ this.$refs.linkModal.open(res)
429
+ })
430
+ },
431
+
432
+ addLink(link){
433
+ link = JSON.parse(JSON.stringify(link))
434
+ this.postIframe({
435
+ uid:this.item.uid,
436
+ data: {
437
+ action: 'insertLink',
438
+ value: link
439
+ }
440
+ })
441
+ this.$refs.linkModal.close()
442
+ },
443
+
444
+ removeLink(){
445
+ this.postIframe({
446
+ uid:this.item.uid,
447
+ data: {
448
+ action: 'removeLink'
449
+ }
450
+ })
451
+ this.$refs.linkModal.close()
452
+ },
453
+
262
454
  insertTable(){
263
455
  this.$emit('postMessageToIframe', this.item.uid, { action:'insertTable' })
264
456
  },
@@ -444,19 +444,6 @@
444
444
 
445
445
  </div>
446
446
 
447
- <div class="h-[1px] bg-text-50"></div>
448
-
449
- <div class="flex flex-row items-center gap-4">
450
- <label class="flex-1 text-text-400">ID</label>
451
- <Textbox v-model="item.props.id" :maxlength="10" @keyup.enter="apply" class="w-[120px]"/>
452
- </div>
453
-
454
- <div v-if="item.uid" class="h-[1px] bg-text-50"></div>
455
-
456
- <div v-if="item.uid" class="py-6 text-center">
457
- <small class="text-text-400">uid: {{ item.uid }}</small>
458
- </div>
459
-
460
447
  <br />
461
448
  <br />
462
449
 
@@ -242,6 +242,7 @@
242
242
  <option value="75%">75%</option>
243
243
  <option value="50%">50%</option>
244
244
  </Dropdown>
245
+
245
246
  <Textbox :readonly="1" :value="iframeSrc" class="w-full">
246
247
  <template #start>
247
248
  <button @click="reloadIframe" class="p-3">
@@ -379,7 +380,7 @@
379
380
  </template>
380
381
  </Modal>
381
382
 
382
- <Modal ref="ldjsonModal" width="480" height="480">
383
+ <Modal ref="ldjsonModal" width="600" height="480">
383
384
  <template v-slot:head>
384
385
  <div class="relative p-5">
385
386
  <h3>JSON-LD</h3>
@@ -395,18 +396,14 @@
395
396
  <template #foot="{ context }">
396
397
  <div class="p-5">
397
398
  <Button type="button" class="w-[100px]"
398
- @click="'index' in context ?
399
- page.ldjson[context.index] = context.code :
400
- page.ldjson.push(context.code);
401
- $refs.ldjsonModal.close()
402
- save()">
399
+ @click="saveLdjson(context)">
403
400
  Save
404
401
  </Button>
405
402
  </div>
406
403
  </template>
407
404
  <template #default="{ context }">
408
405
  <div class="flex-1 p-5">
409
- <CodeEditor v-model="context.code" rows="10"/>
406
+ <MonacoEditor ref="monacoEditor" v-model="context.code"/>
410
407
  </div>
411
408
  </template>
412
409
  </Modal>
@@ -511,6 +508,7 @@ export default{
511
508
 
512
509
  this.page = page
513
510
  this.layouts = layouts
511
+ this.iframeSrc = import.meta.env.VITE_WEB_HOST + '/' + (this.page.path ?? '')
514
512
  this.$nextTick(() => this.resize())
515
513
  })
516
514
  .catch((err) => {
@@ -532,6 +530,19 @@ export default{
532
530
  }
533
531
  },
534
532
 
533
+ postIframe(data){
534
+ return new Promise((resolve, reject) => {
535
+ const handleResponse = (e) => {
536
+ window.removeEventListener('message', handleResponse)
537
+ resolve(e.data)
538
+ }
539
+
540
+ window.addEventListener('message', handleResponse)
541
+
542
+ this.$refs.iframe.contentWindow.postMessage(data, '*')
543
+ })
544
+ },
545
+
535
546
  resize(){
536
547
 
537
548
  const transformOrigin = this.store.viewType === '' ? 'center top' : '0 0'
@@ -640,11 +651,13 @@ export default{
640
651
  if(!component.instance)
641
652
  component.instance = {}
642
653
 
654
+ const compUid = '_' + component.uid.substring(0, 4) + ' '
655
+
643
656
  Object.assign(component.instance, {
644
657
  type: component.type,
645
658
  uid: component.uid,
646
659
 
647
- class: this.compClasses.map(key => {
660
+ class: compUid + this.compClasses.map(key => {
648
661
  return Array.isArray(component.props[key]) ? component.props[key].join(' ') : ''
649
662
  })
650
663
  .filter(_ => _)
@@ -686,6 +699,7 @@ export default{
686
699
  this.socketEmit2('page.save', this.page)
687
700
  .then(() => {
688
701
  if(reload){
702
+ this.iframeSrc = import.meta.env.VITE_WEB_HOST + '/' + (this.page.path ?? '')
689
703
  this.reloadIframe()
690
704
  }
691
705
  })
@@ -709,6 +723,20 @@ export default{
709
723
  })
710
724
  }, 300),
711
725
 
726
+ saveLdjson(context){
727
+ const errors = this.$refs.monacoEditor.getErrors()
728
+ if(errors){
729
+ this.alert('JSON is not valid', errors.map(_ => _.message).join("\n"))
730
+ return
731
+ }
732
+
733
+ 'index' in context ?
734
+ this.page.ldjson[context.index] = context.code :
735
+ this.page.ldjson.push(context.code);
736
+ this.$refs.ldjsonModal.close()
737
+ this.save()
738
+ },
739
+
712
740
  imageSrc(imageUrl){
713
741
  return typeof imageUrl === 'string' ? import.meta.env.VITE_IMAGE_HOST + '/' + imageUrl : imageUrl
714
742
  },
@@ -765,6 +793,10 @@ export default{
765
793
  }
766
794
  }
767
795
  }
796
+ },
797
+
798
+ getPage(){
799
+ return this.page
768
800
  }
769
801
 
770
802
  },
@@ -848,10 +880,6 @@ export default{
848
880
 
849
881
  },
850
882
 
851
- iframeSrc(){
852
- return import.meta.env.VITE_WEB_HOST + '/' + (this.page.path ?? '')
853
- },
854
-
855
883
  layout(){
856
884
  if(this.page){
857
885
  return this.layouts.find((_) => _.id === parseInt(this.page.layoutId))
@@ -889,6 +917,7 @@ export default{
889
917
  data(){
890
918
  return {
891
919
  iframeStyle: {},
920
+ iframeSrc: '',
892
921
  page: null,
893
922
  layouts: [],
894
923
  tabItems: [
@@ -1012,14 +1041,14 @@ export default{
1012
1041
 
1013
1042
  emits: [ 'close' ],
1014
1043
 
1015
- inject: [ 'confirm', 'socket', 'socketEmit2', 'toast' ],
1044
+ inject: [ 'alert', 'confirm', 'socket', 'socketEmit2', 'toast' ],
1016
1045
 
1017
1046
  mounted() {
1018
1047
  this.load()
1019
1048
 
1020
1049
  window.addEventListener('message', (event) => {
1021
1050
 
1022
- const { uid, type, value } = event.data
1051
+ const { uid, type, value } = event.data ?? {}
1023
1052
 
1024
1053
  let comp
1025
1054
  let compType
@@ -1074,7 +1103,9 @@ export default{
1074
1103
  return {
1075
1104
  imageUrl: this.imageUrl,
1076
1105
  imageSrc: this.imageSrc,
1077
- store: this.store
1106
+ store: this.store,
1107
+ postIframe: this.postIframe,
1108
+ getPage: this.getPage
1078
1109
  }
1079
1110
  },
1080
1111
 
@@ -52,6 +52,8 @@ import groupBy from "lodash/groupBy";
52
52
 
53
53
  export default{
54
54
 
55
+ emits: [ 'save' ],
56
+
55
57
  inject: [ 'alert', 'socketEmit2' ],
56
58
 
57
59
  props: {
@@ -96,7 +98,7 @@ export default{
96
98
  async create(){
97
99
  try{
98
100
  const res = await this.socketEmit2('page.create', this.selectedTemplate.page)
99
- this.$router.push({ name: 'WebPageEdit', params: { uid: res.uid } })
101
+ this.$emit('save', res)
100
102
  this.close()
101
103
  }
102
104
  catch(err){