@mixd-id/web-scaffold 0.1.230406109 → 0.1.230406111

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.230406109",
4
+ "version": "0.1.230406111",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -1,31 +1,199 @@
1
1
  <template>
2
- <a v-if="isExternalLink" :href="href" :target="target">
3
- <slot />
2
+ <a v-if="isExternalLink" :href="href" :target="target" :class="compClass">
3
+ <slot>{{ text }}</slot>
4
4
  </a>
5
- <router-link v-else :to="href" :target="target">
6
- <slot />
5
+ <router-link v-else :to="href" :target="target" :class="compClass">
6
+ <slot>{{ text }}</slot>
7
7
  </router-link>
8
8
  </template>
9
9
 
10
10
  <script>
11
11
 
12
+ import {componentMixin} from "../mixin/component";
13
+
12
14
  export default {
13
15
 
16
+ mixins: [ componentMixin ],
17
+
14
18
  props: {
15
- href: String,
16
- target: String
19
+
20
+ href: {
21
+ type: String,
22
+ default: ''
23
+ },
24
+
25
+ target: String,
26
+
27
+ text: String,
28
+
29
+ variant: {
30
+ type: [ String, Number ],
31
+ default: "primary" // primary|secondary|outline, default:primary
32
+ },
17
33
  },
18
34
 
19
35
  computed: {
20
36
 
21
37
  isExternalLink() {
22
38
  return typeof this.href === 'string' && this.href.indexOf('://') > 0
23
- }
39
+ },
40
+
41
+ compClass(){
42
+ return [
43
+ this.$style.button,
44
+ this.$style['button-' + this.variant]
45
+ ]
46
+ .filter(_ => _)
47
+ .join(' ')
48
+ },
24
49
  },
25
50
  }
26
51
  </script>
27
52
 
28
53
  <style module>
29
54
 
55
+ .button{
56
+ @apply p-2 h-[var(--h-cp)];
57
+ @apply relative flex items-center justify-center;
58
+ @apply whitespace-nowrap text-ellipsis overflow-hidden;
59
+ @apply rounded-lg;
60
+ background-image: v-bind(bgImages[0]);
61
+ }
62
+ .button>*:first-child{
63
+ @apply flex items-center justify-center
64
+ }
65
+ .button:disabled{
66
+ @apply opacity-60;
67
+ }
68
+ .button:active{
69
+ @apply top-[1px] left-[1px] relative;
70
+ }
71
+
72
+ .button-primary{
73
+ @apply bg-primary-500 text-white rounded-lg;
74
+ box-shadow: 0 2px 1px rgba(0, 0, 0, .05);
75
+ outline: solid 1px rgb(var(--primary-700));
76
+ border: solid 1px rgb(var(--primary-400));
77
+ }
78
+ .button-primary:focus{
79
+ outline-color: rgb(var(--primary-800));
80
+ border-color: rgb(var(--primary-600));
81
+ }
82
+ .button-primary:hover{
83
+ @apply bg-primary-600;
84
+ outline-color: rgb(var(--primary-800))
85
+ }
86
+ .button-primary:disabled{
87
+ @apply bg-primary-500 opacity-50 top-0 left-0 cursor-not-allowed;
88
+ @apply top-0 left-0;
89
+ outline: solid 1px rgb(var(--primary-700));
90
+ }
91
+ .button-primary *{
92
+ @apply text-white fill-white;
93
+ }
94
+ .button-primary.loading *{
95
+ @apply fill-transparent
96
+ }
97
+ .button-primary .svgBg{
98
+ stroke: #fff;
99
+ stroke-opacity: 25%;
100
+ }
101
+ .button-primary .svgHg{
102
+ fill: #fff;
103
+ fill-opacity: 75%;
104
+ }
105
+
106
+ .button-outline{
107
+ @apply bg-transparent text-primary-500;
108
+ outline: solid 1px rgb(var(--primary-700));
109
+ }
110
+ .button-outline:hover{
111
+ outline-color: rgb(var(--primary-800))
112
+ }
113
+ .button-outline:disabled{
114
+ @apply opacity-50 top-0 left-0 cursor-not-allowed;
115
+ @apply text-text border-primary-500;
116
+ }
117
+ .button-outline *{
118
+ @apply text-primary-500 fill-primary-500;
119
+ }
120
+ .button-outline.loading *{
121
+ @apply fill-transparent
122
+ }
123
+ .button-outline .svgBg{
124
+ stroke: rgb(var(--primary-600));
125
+ stroke-opacity: 50%;
126
+ }
127
+ .button-outline .svgHg{
128
+ fill: rgb(var(--primary));
129
+ fill-opacity: 100%;
130
+ }
131
+
132
+ .button-secondary{
133
+ @apply bg-secondary text-text-500 rounded-lg;
134
+ box-shadow: 0 2px 1px rgba(0, 0, 0, .05);
135
+ outline: solid 1px rgb(var(--secondary-700));
136
+ border: solid 1px rgb(var(--secondary-400));
137
+ }
138
+ .button-secondary:hover{
139
+ @apply bg-secondary-600;
140
+ outline-color: rgb(var(--secondary-800))
141
+ }
142
+ .button-secondary:disabled{
143
+ @apply bg-secondary-500 opacity-50 top-0 left-0 cursor-not-allowed;
144
+ outline: solid 1px rgb(var(--secondary-700));
145
+ }
146
+ .button-secondary *{
147
+ @apply text-text-500 fill-white;
148
+ }
149
+ .button-secondary.loading *{
150
+ @apply fill-transparent
151
+ }
152
+ .button-secondary .svgBg{
153
+ stroke: rgb(var(--text-400));
154
+ stroke-opacity: 25%;
155
+ }
156
+ .button-secondary .svgHg{
157
+ fill: rgb(var(--text-500));
158
+ fill-opacity: 75%;
159
+ }
160
+
161
+ .button-red{
162
+ @apply bg-red-500 text-white rounded-md border-[2px] border-red-500;
163
+ }
164
+ .button-red:hover{
165
+ @apply bg-red-600 border-red-600;
166
+ }
167
+ .button-red:disabled{
168
+ @apply bg-red-500 border-red-500 opacity-50 top-0 left-0 cursor-not-allowed;
169
+ }
170
+ .button-red *{
171
+ @apply text-white fill-white;
172
+ }
173
+ .button-red.loading *{
174
+ @apply fill-transparent
175
+ }
176
+ .button-red .svgBg{
177
+ @apply stroke-red-400;
178
+ stroke-opacity: 50%;
179
+ }
180
+ .button-red .svgHg{
181
+ @apply fill-text-500;
182
+ fill-opacity: 100%;
183
+ }
184
+
185
+ .button-minimal{
186
+ @apply border-[2px] border-transparent;
187
+ }
188
+ .button-minimal:hover{
189
+ }
190
+ .button-minimal .svgBg{
191
+ stroke: rgb(var(--text-500));
192
+ stroke-opacity: 25%;
193
+ }
194
+ .button-minimal .svgHg{
195
+ fill: rgb(var(--text));
196
+ fill-opacity: 75%;
197
+ }
30
198
 
31
199
  </style>
@@ -6,6 +6,8 @@
6
6
 
7
7
  <script>
8
8
 
9
+ import {copyToClipboard} from "../utils/helpers.mjs";
10
+
9
11
  export default{
10
12
 
11
13
  emits: [ 'copied', 'error' ],
@@ -19,41 +21,14 @@ export default{
19
21
 
20
22
  methods: {
21
23
 
22
- fallbackCopyTextToClipboard(text){
23
- var textArea = document.createElement("textarea");
24
- textArea.value = this.value;
25
- textArea.style.top = "0";
26
- textArea.style.left = "0";
27
- textArea.style.position = "fixed";
28
- document.body.appendChild(textArea);
29
- textArea.focus();
30
- textArea.select();
31
-
32
- try {
33
- document.execCommand('copy')
34
- this.$emit('copied')
35
- } catch (err) {
36
- this.$emit('error')
37
- }
38
-
39
- document.body.removeChild(textArea);
40
- },
41
-
42
24
  copyToClipboard(){
43
-
44
- if (!navigator.clipboard) {
45
- this.fallbackCopyTextToClipboard(this.value);
46
- return;
47
- }
48
- navigator.clipboard.writeText(this.value).then(() => {
49
- this.$emit('copied')
50
- }, (err) => {
51
- this.$emit('error')
52
- });
25
+ copyToClipboard(this.value)
26
+ .then(() => this.$emit('copied'))
27
+ .catch(() => this.$emit('error'))
53
28
  }
54
29
 
55
30
  }
56
31
 
57
32
  }
58
33
 
59
- </script>
34
+ </script>
@@ -9,7 +9,9 @@
9
9
  @movedown="moveDown(item)"
10
10
  @change="$emit('change')"
11
11
  @remove="confirm($t('Remove this item?'), { onConfirm: () => { modelValue.splice(index, 1);$emit('change') }})"
12
- @add="(items) => $emit('add', items)">
12
+ @add="(items) => $emit('add', items)"
13
+ @paste.stop.prevent="(e) => paste(e, item)"
14
+ @item-paste="(p1, p2, p3) => $emit('item-paste', p1, p2, p3)">
13
15
  <template #default="{ item }">
14
16
  <slot :item="item"></slot>
15
17
  </template>
@@ -26,7 +28,7 @@ export default{
26
28
 
27
29
  components: {TreeViewItem},
28
30
 
29
- emits: [ 'add', 'change' ],
31
+ emits: [ 'add', 'change', 'item-paste' ],
30
32
 
31
33
  inject: [ 'confirm' ],
32
34
 
@@ -40,6 +42,19 @@ export default{
40
42
 
41
43
  methods: {
42
44
 
45
+ paste(event, subItem){
46
+ const clipboardData = event.clipboardData || window.clipboardData;
47
+ const pastedText = clipboardData.getData('text');
48
+
49
+ try{
50
+ const newItem = JSON.parse(pastedText)
51
+ this.$emit('item-paste', this.modelValue, subItem, newItem)
52
+ }
53
+ catch(e){
54
+
55
+ }
56
+ },
57
+
43
58
  moveDown(item){
44
59
  const idx = this.modelValue.indexOf(item)
45
60
  if(idx < this.modelValue.length - 1){
@@ -1,27 +1,20 @@
1
1
  <template>
2
- <div>
2
+ <div @copy.stop.prevent="copy">
3
3
  <div ref="item" :class="itemClass"
4
- @mouseover="hoverMouseOver" @mouseout="hoverMouseOut">
5
- <div class="cursor-move" ref="drag" @mousedown="mouseDown">
6
- <svg width="14" height="14" class="fill-text-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
7
- </div>
8
- <div class="flex flex-col hidden">
9
- <button type="button" @click="$emit('moveup')">
10
- <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M32.032 352h255.93c28.425 0 42.767-34.488 22.627-54.627l-127.962-128c-12.496-12.496-32.758-12.497-45.255 0l-127.968 128C-10.695 317.472 3.55 352 32.032 352zM160 192l128 128H32l128-128z"/></svg>
11
- </button>
12
- <button type="button" @click="$emit('movedown')">
13
- <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M287.968 160H32.038c-28.425 0-42.767 34.488-22.627 54.627l127.962 128c12.496 12.496 32.758 12.497 45.255 0l127.968-128C330.695 194.528 316.45 160 287.968 160zM160 320L32 192h256L160 320z"/></svg>
14
- </button>
15
- </div>
16
- <slot :item="item"></slot>
17
- <button type="button" class="py-2" @click="$emit('remove')">
18
- <svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
19
- </button>
4
+ @mouseover="hoverMouseOver"
5
+ @mouseout="hoverMouseOut"
6
+ @mousedown="mouseDown"
7
+ @mouseup="hoverMouseUp">
20
8
  <button type="button" class="py-2" v-if="Array.isArray((item ?? {}).items) && item.items.length > 0"
21
- @click="childCollapsed = !childCollapsed">
9
+ @click="childCollapsed = !childCollapsed">
22
10
  <svg v-if="!childCollapsed" width="16" height="16" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"/></svg>
23
11
  <svg v-else width="16" height="16" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"/></svg>
24
12
  </button>
13
+ <div v-else class="pl-1"></div>
14
+ <slot :item="item"></slot>
15
+ <button type="button" class="py-2" @click="$emit('remove')">
16
+ <svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
17
+ </button>
25
18
  </div>
26
19
  <div ref="container" v-if="item && Array.isArray(item.items) && !childCollapsed" class="ml-4">
27
20
  <TreeViewItem v-for="(subItem, index) in item.items"
@@ -32,7 +25,9 @@
32
25
  @movedown="moveDown(subItem)"
33
26
  @remove="confirm($t('Remove this item?'), { onConfirm: () => { item.items.splice(index, 1);$emit('change') }})"
34
27
  @add="(items) => $emit('add', items)"
35
- @change="$emit('change')">
28
+ @change="$emit('change')"
29
+ @paste.stop.prevent="(e) => paste(e, subItem)"
30
+ @item-paste="(p1, p2, p3) => $emit('item-paste', p1, p2, p3)">
36
31
  <template #default="{ item }">
37
32
  <slot :item="item"></slot>
38
33
  </template>
@@ -50,13 +45,15 @@
50
45
 
51
46
  <script>
52
47
 
48
+ import {copyToClipboard} from "../utils/helpers.mjs";
49
+
53
50
  let dragged = null
54
51
 
55
52
  export default{
56
53
 
57
- emits: [ 'add', 'change', 'moveup', 'movedown', 'remove' ],
54
+ emits: [ 'add', 'change', 'moveup', 'movedown', 'remove', 'item-paste' ],
58
55
 
59
- inject: [ 'confirm' ],
56
+ inject: [ 'confirm', 'toast' ],
60
57
 
61
58
  props: {
62
59
  item: Object,
@@ -73,6 +70,24 @@ export default{
73
70
 
74
71
  methods: {
75
72
 
73
+ copy(){
74
+ copyToClipboard(JSON.stringify(this.item))
75
+ this.toast(this.item.type + ' copied')
76
+ },
77
+
78
+ paste(event, subItem){
79
+ const clipboardData = event.clipboardData || window.clipboardData;
80
+ const pastedText = clipboardData.getData('text');
81
+
82
+ try{
83
+ const newItem = JSON.parse(pastedText)
84
+ this.$emit('item-paste', this.item.items, subItem, newItem)
85
+ }
86
+ catch(e){
87
+
88
+ }
89
+ },
90
+
76
91
  moveDown(item){
77
92
  const idx = this.item.items.indexOf(item)
78
93
  if(idx < this.item.items.length - 1){
@@ -94,13 +109,16 @@ export default{
94
109
  },
95
110
 
96
111
  mouseDown(e){
97
-
98
112
  e.preventDefault()
99
113
 
114
+ const rect = this.$el.getBoundingClientRect()
115
+ const startX = e.clientX
116
+ const startY = e.clientY
117
+
100
118
  const cloned = this.$el.cloneNode(true)
101
119
  cloned.style.position = 'absolute'
102
- cloned.style.left = (e.clientX - 10) + "px"
103
- cloned.style.top = (e.clientY - 10) + "px"
120
+ cloned.style.left = rect.x + "px"
121
+ cloned.style.top = rect.y + "px"
104
122
  cloned.style.width = this.$el.clientWidth + "px"
105
123
  cloned.style.pointerEvents = 'none'
106
124
  document.body.appendChild(cloned)
@@ -111,8 +129,10 @@ export default{
111
129
  }
112
130
 
113
131
  const mouseMove = (e) => {
114
- cloned.style.left = (e.clientX - 10) + "px"
115
- cloned.style.top = (e.clientY - 10) + "px"
132
+ const distanceX = e.clientX - startX
133
+ const distanceY = e.clientY - startY
134
+ cloned.style.left = (rect.x + distanceX) + "px"
135
+ cloned.style.top = (rect.y + distanceY) + "px"
116
136
  }
117
137
 
118
138
  const mouseUp = (e) => {
@@ -170,7 +190,7 @@ export default{
170
190
  delete dragged.targetParent
171
191
  }
172
192
 
173
- }
193
+ },
174
194
 
175
195
  },
176
196
 
@@ -201,8 +221,8 @@ export default{
201
221
  <style module>
202
222
 
203
223
  .item{
204
- @apply bg-base-300 dark:bg-base-400 flex flex-row gap-2 px-3 items-center rounded-lg mb-1;
205
- @apply border-[1px] border-transparent;
224
+ @apply bg-base-300 dark:bg-base-400 flex flex-row gap-2 px-2 items-center rounded-lg mb-1;
225
+ @apply border-[1px] border-transparent cursor-pointer;
206
226
  }
207
227
  .item.active{
208
228
  @apply border-primary;
package/src/index.js CHANGED
@@ -354,6 +354,8 @@ export default{
354
354
  app.component('BlockSetting', defineAsyncComponent(() => import("./widgets/BlockSetting.vue")))
355
355
  app.component('BoxItem', defineAsyncComponent(() => import("./widgets/BoxItem.vue")))
356
356
  app.component('BoxSetting', defineAsyncComponent(() => import("./widgets/BoxSetting.vue")))
357
+ app.component('AhrefItem', defineAsyncComponent(() => import("./widgets/AhrefItem.vue")))
358
+ app.component('AhrefSetting', defineAsyncComponent(() => import("./widgets/AhrefSetting.vue")))
357
359
  app.component('ButtonItem', defineAsyncComponent(() => import("./widgets/ButtonItem.vue")))
358
360
  app.component('ButtonSetting', defineAsyncComponent(() => import("./widgets/ButtonSetting.vue")))
359
361
  app.component('CarouselItem', defineAsyncComponent(() => import("./widgets/CarouselItem.vue")))
@@ -177,6 +177,42 @@ const _applyClass = function(props, defaultClass = {}){
177
177
  return []
178
178
  }
179
179
 
180
+ const fallbackCopyTextToClipboard = function(obj){
181
+ return new Promise((resolve, reject) => {
182
+ var textArea = document.createElement("textarea");
183
+ textArea.value = obj;
184
+ textArea.style.top = "0";
185
+ textArea.style.left = "0";
186
+ textArea.style.position = "fixed";
187
+ document.body.appendChild(textArea);
188
+ textArea.focus();
189
+ textArea.select();
190
+
191
+ try {
192
+ document.execCommand('copy')
193
+ document.body.removeChild(textArea);
194
+ resolve()
195
+ } catch (err) {
196
+ reject(err)
197
+ }
198
+ })
199
+ }
200
+
201
+ const copyToClipboard = function(obj){
202
+
203
+ if (!navigator.clipboard) {
204
+ return fallbackCopyTextToClipboard(obj);
205
+ }
206
+
207
+ return new Promise((resolve, reject) => {
208
+ navigator.clipboard.writeText(obj).then(() => {
209
+ resolve()
210
+ }, (err) => {
211
+ reject(err)
212
+ });
213
+ })
214
+ }
215
+
180
216
  export {
181
217
  downsizeImage,
182
218
  uid,
@@ -187,7 +223,8 @@ export {
187
223
  getQueryString,
188
224
  accessNestedObject,
189
225
  mediaPrefixes,
190
- _applyClass
226
+ _applyClass,
227
+ copyToClipboard
191
228
  }
192
229
 
193
230
  function observeInit(){
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="flex-1 flex flex-row items-center gap-2">
3
+ {{ item.props.name ?? item.type }}
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+
9
+ export default{
10
+
11
+ props: {
12
+ item: Object
13
+ }
14
+
15
+ }
16
+
17
+ </script>
18
+
19
+ <style module>
20
+
21
+ .comp{
22
+
23
+ }
24
+
25
+ </style>
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+
4
+ <div>
5
+ <label class="flex-1 text-text-400">Text</label>
6
+ <Textbox v-model="item.props.text" @keyup.enter="$emit('change')"/>
7
+ </div>
8
+
9
+ <div>
10
+ <label class="flex-1 text-text-400">Href</label>
11
+ <Textbox v-model="item.props.href" @keyup.enter="$emit('change')"/>
12
+ </div>
13
+
14
+ <div>
15
+ <label class="flex-1 text-text-400">Target</label>
16
+ <Dropdown v-model="item.props.target" @change="$emit('change')">
17
+ <option value="_self">Self</option>
18
+ <option value="_blank">Blank</option>
19
+ <option value="_parent">Parent</option>
20
+ <option value="_top">Top</option>
21
+ </Dropdown>
22
+ </div>
23
+
24
+ <div>
25
+ <label class="flex-1 text-text-400">Variant</label>
26
+ <Dropdown v-model="item.props.variant"
27
+ @change="$emit('change')">
28
+ <option value="primary">Primary</option>
29
+ <option value="secondary">Secondary</option>
30
+ <option value="red">Red</option>
31
+ <option value="minimal">Minimal</option>
32
+ <option value="outline">Outline</option>
33
+ <option value="">Not Set</option>
34
+ </Dropdown>
35
+ </div>
36
+
37
+ <ComponentSetting :item="item"
38
+ :view-type="viewType"
39
+ :view-types="viewTypes"
40
+ defaultDisplay="flex"
41
+ @change="$emit('change')" />
42
+
43
+ </div>
44
+ </template>
45
+
46
+ <script>
47
+
48
+ export default{
49
+
50
+ emits: [ 'change' ],
51
+
52
+ props: {
53
+
54
+ item: {
55
+ type: Object,
56
+ required: true
57
+ },
58
+
59
+ viewType: String,
60
+
61
+ viewTypes: Array,
62
+
63
+ },
64
+
65
+ data(){
66
+ return {
67
+ }
68
+ },
69
+
70
+ inject: [ 'confirm', 'imageSrc' ],
71
+
72
+ methods: {
73
+
74
+
75
+ },
76
+
77
+ computed: {
78
+
79
+ }
80
+
81
+ }
82
+
83
+ </script>
84
+
85
+ <style module>
86
+
87
+ .comp{
88
+ @apply flex flex-col gap-4;
89
+ }
90
+
91
+ </style>
@@ -421,7 +421,7 @@
421
421
 
422
422
  <div class="flex flex-col gap-4">
423
423
 
424
- <label class="flex-1 text-text-400">Shadow</label>
424
+ <strong class="flex-1 text-text-400">Shadow</strong>
425
425
 
426
426
  <div class="flex flex-row items-center gap-4">
427
427
  <label class="flex-1 text-text-400">Box Shadow</label>
@@ -144,7 +144,8 @@
144
144
  v-model="layout.headers"
145
145
  :selected-item="currentItem"
146
146
  @add="openSelector"
147
- @change="saveLayout">
147
+ @change="saveLayout"
148
+ @item-paste="onLayoutItemPaste">
148
149
  <template #default="{ item }">
149
150
  <component :is="`${(item ?? {}).type}Item`" :item="item"
150
151
  @click="store.selectedComponent = [ item.uid ]"/>
@@ -161,7 +162,8 @@
161
162
  v-model="layout.footers"
162
163
  :selected-item="currentItem"
163
164
  @add="openSelector"
164
- @change="saveLayout">
165
+ @change="saveLayout"
166
+ @item-paste="onLayoutItemPaste">
165
167
  <template #default="{ item }">
166
168
  <component :is="`${(item ?? {}).type}Item`" :item="item"
167
169
  @click="store.selectedComponent = [ item.uid ]"/>
@@ -178,7 +180,8 @@
178
180
  v-model="page.components"
179
181
  :selected-item="currentItem"
180
182
  @add="openSelector"
181
- @change="save">
183
+ @change="save"
184
+ @item-paste="onItemPaste">
182
185
 
183
186
  <template #default="{ item }">
184
187
  <component :is="`${(item ?? {}).type}Item`" :item="item"
@@ -253,7 +256,7 @@
253
256
  </template>
254
257
  <template #end>
255
258
  <div class="flex flex-row gap-4">
256
- <CopyToClipboard :value="iframeSrc" @copied="toast('Nomor hp berhasil disalin')">
259
+ <CopyToClipboard :value="iframeSrc" @copied="toast($t('Copied'))">
257
260
  <svg width="14" height="14" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM352 32.491a15.88 15.88 0 0 1 7.431 4.195l51.882 51.883A15.885 15.885 0 0 1 415.508 96H352V32.491zM288 464c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V144c0-8.822 7.178-16 16-16h80v240c0 26.51 21.49 48 48 48h112v48zm128-96c0 8.822-7.178 16-16 16H176c-8.822 0-16-7.178-16-16V48c0-8.822 7.178-16 16-16h144v72c0 13.2 10.8 24 24 24h72v240z"/></svg>
258
261
  </CopyToClipboard>
259
262
  <a type="button" class="w-[21px]" :href="iframeSrc" target="_blank">
@@ -467,7 +470,7 @@ export default{
467
470
  enabled: true
468
471
  })
469
472
 
470
- obj.uid = md5(obj.type + new Date().getTime())
473
+ obj.uid = md5(obj.type + Math.random())
471
474
 
472
475
  if([ 'headers', 'footers' ].includes(to)){
473
476
  this.layout[to].push(obj)
@@ -777,6 +780,29 @@ export default{
777
780
  this.$refs.iframe.contentWindow.postMessage({ uid, data }, '*')
778
781
  },
779
782
 
783
+ onLayoutItemPaste(items, currentItem, newItem){
784
+ this.onItemPaste(items, currentItem, newItem, true)
785
+ },
786
+
787
+ onItemPaste(items, currentItem, newItem, isLayout = false){
788
+
789
+ const setUid = (item) => {
790
+ item.uid = md5(newItem.type + Math.random())
791
+
792
+ if(Array.isArray(item.items)){
793
+ for(let subItem of item.items){
794
+ setUid(subItem)
795
+ }
796
+ }
797
+ }
798
+
799
+ setUid(newItem)
800
+ items.splice(items.indexOf(currentItem) + 1, 0, newItem)
801
+ this.store.selectedComponent = [ newItem.uid ]
802
+
803
+ isLayout ? this.saveLayout() : this.save()
804
+ },
805
+
780
806
  findCompByUid(uid, components){
781
807
 
782
808
  for(let i in components){
@@ -971,6 +997,10 @@ export default{
971
997
  htmlText:'', images:{},
972
998
  }},
973
999
 
1000
+ { type:'Ahref', name:'Ahref', group:'Components', props:{
1001
+ name:'Ahref', text:'Ahref'
1002
+ }},
1003
+
974
1004
  { type:'Button', name:'Button', group:'Components', props:{
975
1005
  name:'Button', text:'Button'
976
1006
  }},