@mixd-id/web-scaffold 0.2.240705 → 0.2.250801009

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.
Files changed (220) hide show
  1. package/docs/components/Dashboard.md +56 -0
  2. package/log.txt +7 -0
  3. package/package.json +27 -19
  4. package/src/components/404.vue +61 -0
  5. package/src/components/AccountIcon.vue +19 -0
  6. package/src/components/Ahref.vue +1 -1
  7. package/src/components/Alert.vue +4 -13
  8. package/src/components/ArrayList.vue +49 -0
  9. package/src/components/Article.vue +24 -30
  10. package/src/components/Button.vue +79 -167
  11. package/src/components/Card.vue +235 -0
  12. package/src/components/Carousel.vue +61 -60
  13. package/src/components/Cart.vue +192 -0
  14. package/src/components/CartIcon.vue +89 -0
  15. package/src/components/ChartBar.vue +2 -3
  16. package/src/components/Checkbox.vue +20 -11
  17. package/src/components/Checkout.vue +373 -0
  18. package/src/components/CheckoutDelivery.vue +267 -0
  19. package/src/components/CodeEditor.vue +5 -16
  20. package/src/components/CollapsiblePanel.vue +70 -0
  21. package/src/components/ColorPicker.vue +8 -3
  22. package/src/components/ColorPicker2.vue +41 -19
  23. package/src/components/ColorPicker3.vue +100 -0
  24. package/src/components/Confirm.vue +9 -7
  25. package/src/components/ContextMenu.vue +122 -206
  26. package/src/components/ContextMenuItem.vue +53 -0
  27. package/src/components/Dashboard.vue +243 -0
  28. package/src/components/Dashboard2.vue +118 -0
  29. package/src/components/DashboardComponentSelector.vue +96 -0
  30. package/src/components/DashboardConfigs.vue +202 -0
  31. package/src/components/Datepicker.vue +102 -41
  32. package/src/components/DayTimeRange.vue +3 -2
  33. package/src/components/Dropdown.vue +7 -4
  34. package/src/components/Flex.vue +14 -40
  35. package/src/components/GHeatMaps.vue +2 -2
  36. package/src/components/Grid.vue +6 -6
  37. package/src/components/HTMLEditor.vue +27 -14
  38. package/src/components/Image.vue +62 -108
  39. package/src/components/ImagePreview.vue +14 -4
  40. package/src/components/ImageUploader.vue +114 -0
  41. package/src/components/ImportModal.vue +3 -3
  42. package/src/components/Link.vue +62 -6
  43. package/src/components/List.vue +524 -402
  44. package/src/components/ListContextMenu.vue +88 -0
  45. package/src/components/ListItem.vue +5 -3
  46. package/src/components/ListPage1.vue +14 -15
  47. package/src/components/ListView.vue +5 -6
  48. package/src/components/ListViewSettings.vue +2 -2
  49. package/src/components/LogViewerItem.vue +1 -1
  50. package/src/components/MarkdownEdit.vue +128 -0
  51. package/src/components/MarkdownPreview.vue +102 -0
  52. package/src/components/MenuItem1.vue +36 -0
  53. package/src/components/Modal.vue +95 -43
  54. package/src/components/MultiDropdown.vue +124 -0
  55. package/src/components/MultilineText.vue +1 -4
  56. package/src/components/OTPField.vue +11 -17
  57. package/src/components/ObjectTree.vue +1 -1
  58. package/src/components/PageBuilder.vue +3 -3
  59. package/src/components/Paragraph.vue +1 -2
  60. package/src/components/PresetSelectorFilterItem.vue +107 -95
  61. package/src/components/Radio.vue +1 -1
  62. package/src/components/SearchModal.vue +153 -0
  63. package/src/components/Slider.vue +1 -1
  64. package/src/components/Svg.vue +1 -1
  65. package/src/components/SvgEditor.vue +173 -0
  66. package/src/components/Switch.vue +4 -5
  67. package/src/components/Table.vue +2 -2
  68. package/src/components/TableView.vue +2 -3
  69. package/src/components/TableViewHead.vue +2 -2
  70. package/src/components/Tabs.vue +1 -1
  71. package/src/components/Testimonial.vue +2 -2
  72. package/src/components/Text.vue +7 -22
  73. package/src/components/TextEditor.vue +3 -3
  74. package/src/components/TextWithTag.vue +61 -30
  75. package/src/components/Textarea.vue +11 -16
  76. package/src/components/Textbox.vue +9 -19
  77. package/src/components/Timepicker.vue +25 -15
  78. package/src/components/Toast.vue +5 -3
  79. package/src/components/TreeMenu.vue +122 -0
  80. package/src/components/TreeView.vue +15 -10
  81. package/src/components/TreeView2.vue +38 -0
  82. package/src/components/TreeViewItem.vue +58 -29
  83. package/src/components/TreeViewItem2.vue +55 -0
  84. package/src/components/Uploader.vue +45 -0
  85. package/src/components/Video.vue +119 -0
  86. package/src/components/VirtualGrid.vue +24 -7
  87. package/src/components/VirtualTable.vue +363 -128
  88. package/src/configs/dashboard/data-table.js +9 -0
  89. package/src/configs/web-page-builder.js +118 -0
  90. package/src/directives/intersect.js +26 -0
  91. package/src/hooks/device.js +14 -0
  92. package/src/index.js +62 -107
  93. package/src/mixin/component.js +147 -67
  94. package/src/themes/default/index.js +83 -155
  95. package/src/utils/dashboard.js +22 -962
  96. package/src/utils/helpers.cjs +635 -0
  97. package/src/utils/helpers.js +91 -60
  98. package/src/utils/helpers.mjs +245 -12
  99. package/src/utils/importer.js +22 -3
  100. package/src/utils/list.mjs +1509 -0
  101. package/src/utils/preset-selector.cjs +1455 -0
  102. package/src/utils/preset-selector.js +489 -95
  103. package/src/utils/preset-selector.mjs +59 -20
  104. package/src/utils/queue.js +63 -0
  105. package/src/utils/web.mjs +120 -0
  106. package/src/utils/wss.js +37 -29
  107. package/src/utils/wss.mjs +24 -19
  108. package/src/widgets/AhrefSetting.vue +16 -13
  109. package/src/widgets/ArticleSetting.vue +15 -27
  110. package/src/widgets/BackgroundColorSetting.vue +153 -0
  111. package/src/widgets/BorderColorSetting.vue +57 -0
  112. package/src/widgets/BotEditor/BotEditorActions.vue +3 -2
  113. package/src/widgets/BotEditor/BotEditorSettings.vue +21 -0
  114. package/src/widgets/BotEditor.vue +35 -15
  115. package/src/widgets/ButtonSetting.vue +12 -13
  116. package/src/widgets/CarouselSetting.vue +33 -45
  117. package/src/widgets/CartSetting.vue +46 -0
  118. package/src/widgets/CheckoutSetting.vue +46 -0
  119. package/src/widgets/CollapsiblePanelSetting.vue +46 -0
  120. package/src/widgets/ColumnSelector.vue +29 -5
  121. package/src/widgets/ComponentSetting.vue +1 -1
  122. package/src/widgets/ComponentSetting2.vue +112 -234
  123. package/src/widgets/ComponentSetting3.vue +1 -1
  124. package/src/widgets/ContactForm.vue +3 -3
  125. package/src/widgets/ContactFormSetting.vue +41 -30
  126. package/src/widgets/Dashboard/BarChart.vue +47 -11
  127. package/src/widgets/Dashboard/BarChartSetting.vue +1 -1
  128. package/src/widgets/Dashboard/DataTable.vue +125 -0
  129. package/src/widgets/Dashboard/DataTableSetting.vue +243 -0
  130. package/src/widgets/Dashboard/DatasourceSelector.vue +1 -1
  131. package/src/widgets/Dashboard/Doughnut.vue +49 -7
  132. package/src/widgets/Dashboard/DoughnutSetting.vue +2 -2
  133. package/src/widgets/Dashboard/Metric.vue +78 -19
  134. package/src/widgets/Dashboard/MetricSetting.vue +81 -28
  135. package/src/widgets/Dashboard/Pie.vue +55 -6
  136. package/src/widgets/Dashboard/PieSetting.vue +1 -1
  137. package/src/widgets/Dashboard/PolarArea.vue +49 -7
  138. package/src/widgets/Dashboard/PolarAreaSetting.vue +1 -1
  139. package/src/widgets/Dashboard/SharingModal.vue +4 -5
  140. package/src/widgets/Dashboard/ViewSelector.vue +2 -2
  141. package/src/widgets/Dashboard/VirtualTableSetting.vue +121 -184
  142. package/src/widgets/{Dashboard.vue → Dashboard0.vue} +426 -343
  143. package/src/widgets/EmbeddedVideoSetting.vue +7 -5
  144. package/src/widgets/FAQ.vue +16 -3
  145. package/src/widgets/FAQSetting.vue +53 -47
  146. package/src/widgets/FeatureList.vue +3 -0
  147. package/src/widgets/FeatureListSetting.vue +112 -102
  148. package/src/widgets/FlexSetting.vue +83 -106
  149. package/src/widgets/GridSetting.vue +71 -196
  150. package/src/widgets/Header2.vue +34 -71
  151. package/src/widgets/Header2Setting.vue +95 -179
  152. package/src/widgets/HeaderSetting.vue +16 -18
  153. package/src/widgets/IconListSetting.vue +69 -65
  154. package/src/widgets/ImageSetting.vue +33 -60
  155. package/src/widgets/LinkSetting.vue +60 -37
  156. package/src/widgets/LinkSettingModal.vue +173 -0
  157. package/src/widgets/LogViewer.vue +1 -1
  158. package/src/widgets/MarginSetting.vue +2 -2
  159. package/src/widgets/MenuEditor.vue +1 -1
  160. package/src/widgets/MenuItem1Setting.vue +78 -0
  161. package/src/widgets/ModalSetting.vue +42 -44
  162. package/src/widgets/MultiValueSetting.vue +2 -2
  163. package/src/widgets/MultiValueSetting2.vue +78 -45
  164. package/src/widgets/OGSettingModal.vue +103 -0
  165. package/src/widgets/PaddingSetting.vue +2 -2
  166. package/src/widgets/ParagraphSetting.vue +16 -13
  167. package/src/widgets/PositionSetting.vue +209 -0
  168. package/src/widgets/PresetBar.vue +359 -210
  169. package/src/widgets/PresetBarPivot.vue +31 -19
  170. package/src/widgets/PresetSelector.vue +29 -17
  171. package/src/widgets/SearchModalSetting.vue +70 -0
  172. package/src/widgets/Share.vue +1 -2
  173. package/src/widgets/ShareSetting.vue +67 -60
  174. package/src/widgets/StyleSetting.vue +227 -116
  175. package/src/widgets/TestimonialSetting.vue +97 -88
  176. package/src/widgets/TextBlockSetting.vue +16 -13
  177. package/src/widgets/UserActionBuilder/UserActionConsole.vue +30 -10
  178. package/src/widgets/UserActionBuilder/UserActionOutput.vue +2 -2
  179. package/src/widgets/UserActionBuilder/UserActionOutputReply.vue +64 -87
  180. package/src/widgets/UserActionBuilder/UserActionProps.vue +3 -3
  181. package/src/widgets/UserActionBuilder.vue +4 -16
  182. package/src/widgets/WebComponentSelector.vue +15 -11
  183. package/src/widgets/WebLayoutSelector.vue +41 -270
  184. package/src/widgets/WebPageBuilder.vue +693 -704
  185. package/src/widgets/WebPageBuilder2.vue +7 -7
  186. package/src/widgets/WebPageBuilder4/ButtonSetting.vue +0 -8
  187. package/src/widgets/WebPageBuilder4/CarouselSetting.vue +63 -7
  188. package/src/widgets/WebPageBuilder4/FlexAlignSetting.vue +3 -3
  189. package/src/widgets/WebPageBuilder4/FlexSetting.vue +1 -10
  190. package/src/widgets/WebPageBuilder4/MultiValueSetting.vue +2 -2
  191. package/src/widgets/WebPageBuilder4/PropertySetting.vue +0 -7
  192. package/src/widgets/WebPageBuilder4/WebPageComponentSelector.vue +1 -7
  193. package/src/widgets/WebPageBuilder4.vue +289 -575
  194. package/src/widgets/WebPageSelector.vue +1 -1
  195. package/src/widgets/YoutubeVideoSetting.vue +16 -13
  196. package/tailwind.config.js +3 -35
  197. package/docs/schema/user-action.json +0 -266
  198. package/src/App.vue +0 -25
  199. package/src/components/SearchButton.vue +0 -57
  200. package/src/entry-client.js +0 -27
  201. package/src/entry-server.js +0 -73
  202. package/src/events/event.js +0 -2
  203. package/src/main.js +0 -29
  204. package/src/mixin/website.js +0 -121
  205. package/src/router.js +0 -57
  206. package/src/widgets/MobileMenu.vue +0 -182
  207. package/src/widgets/WebPageBuilder4/ActionSetting.vue +0 -158
  208. package/src/widgets/WebPageBuilder4/ColorSetting.vue +0 -63
  209. package/src/widgets/WebPageBuilder4/DataSetting.vue +0 -92
  210. package/src/widgets/WebPageBuilder4/FontSizeSetting.vue +0 -76
  211. package/src/widgets/WebPageBuilder4/LinkSetting.vue +0 -68
  212. package/src/widgets/WebPageBuilder4/MobileMenuSetting.vue +0 -106
  213. package/src/widgets/WebPageBuilder4/Setting.vue +0 -73
  214. package/src/widgets/WebPageBuilder4/StyleSetting.vue +0 -77
  215. package/src/widgets/WebPageBuilder4/SvgSetting.vue +0 -207
  216. package/src/widgets/WebPageBuilder4/TextTransformSetting.vue +0 -70
  217. package/src/widgets/WebPageBuilder4/WebPageDataEdit.vue +0 -121
  218. package/test.json +0 -22
  219. /package/src/widgets/{Header1.vue → Header0.vue} +0 -0
  220. /package/src/widgets/{Header1Setting.vue → Header0Setting.vue} +0 -0
@@ -1,13 +1,15 @@
1
1
  <template>
2
- <div class="flex-1 flex items-center justify-center" v-if="state === 2">
2
+ <div class="flex-1 flex items-center justify-center" v-if="readyState === 2">
3
3
  <svg class="animate-spin aspect-square w-[48px]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
4
4
  </div>
5
5
  <div :class="$style.comp" v-else-if="page">
6
6
 
7
7
  <div class="flex-1 flex flex-col bg-base-400">
8
8
 
9
- <div class="p-3 pr-5 sticky top-0 flex justify-center gap-4 bg-base-400 dark:bg-base-300 border-b-[1px] border-text-50">
10
- <div class="flex-1 flex flex-row gap-4 items-center">
9
+ <div class="p-3 pr-5 flex justify-center gap-4 bg-base-400 border-b-[1px] border-border-50">
10
+ <div class="flex-1 flex flex-row gap-4 items-center"
11
+ @click.meta="log(page)"
12
+ @click.alt="log(layout)">
11
13
 
12
14
  <button ref="close" class=" p-1"
13
15
  @click="close">
@@ -23,27 +25,18 @@
23
25
  <template #start>
24
26
  <div class="flex flex-row items-center mr-2">
25
27
  <button @click="reloadIframe" class="p-3">
26
- <svg width="14" height="14" viewBox="0 0 24 24" class="fill-text" xmlns="http://www.w3.org/2000/svg">
28
+ <svg width="14" height="14" viewBox="0 0 24 24" class="fill-primary" xmlns="http://www.w3.org/2000/svg">
27
29
  <path d="M3.75 12C3.75 7.44365 7.44365 3.75 12 3.75C14.7802 3.75 17.1982 5.12612 18.6816 7.24467L16.5022 7.23828C16.088 7.23707 15.7512 7.57187 15.75 7.98608C15.7488 8.4003 16.0836 8.73706 16.4978 8.73828L19.9491 8.74839C19.9817 8.75065 20.0147 8.75076 20.0477 8.74868L20.4978 8.75C20.6971 8.75058 20.8884 8.67182 21.0296 8.53111C21.1707 8.39039 21.25 8.19929 21.25 8L21.25 4C21.25 3.58579 20.9142 3.25 20.5 3.25C20.0858 3.25 19.75 3.58579 19.75 4L19.75 6.16237C17.9894 3.79113 15.2004 2.25 12 2.25C6.61522 2.25 2.25 6.61522 2.25 12C2.25 17.3848 6.61522 21.75 12 21.75C15.8354 21.75 19.0799 19.5367 20.6716 16.3338C20.856 15.9628 20.7047 15.5127 20.3338 15.3284C19.9628 15.144 19.5127 15.2953 19.3284 15.6662C17.9747 18.3902 15.2321 20.25 12 20.25C7.44365 20.25 3.75 16.5563 3.75 12Z"/>
28
30
  </svg>
29
31
  </button>
30
- <select v-model="store.zoomLevel" :class="$style.zoomLevel" @change="resize()">
31
- <optgroup label="Zoom Level">
32
- <option value="fit">Fit</option>
33
- <option value="125%">125%</option>
34
- <option value="100%">100%</option>
35
- <option value="75%">75%</option>
36
- <option value="50%">50%</option>
37
- </optgroup>
38
- </select>
39
32
  </div>
40
33
  </template>
41
34
  <template #end>
42
- <div class="flex flex-row gap-4 ml-3">
43
- <CopyToClipboard :value="computedIframeSrc" @copied="toast($t('Copied'))">
35
+ <div class="flex flex-row gap-5 ml-3">
36
+ <CopyToClipboard :value="computedIframeSrc" @copied="toast($t('URL Copied'))">
44
37
  <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>
45
38
  </CopyToClipboard>
46
- <a type="button" class="w-[21px]" :href="computedIframeSrc" target="_blank">
39
+ <a type="button" class="w-[24px]" :href="computedIframeSrc" target="_blank">
47
40
  <svg width="12" height="12" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M497.6,0,334.4.17A14.4,14.4,0,0,0,320,14.57V47.88a14.4,14.4,0,0,0,14.69,14.4l73.63-2.72,2.06,2.06L131.52,340.49a12,12,0,0,0,0,17l23,23a12,12,0,0,0,17,0L450.38,101.62l2.06,2.06-2.72,73.63A14.4,14.4,0,0,0,464.12,192h33.31a14.4,14.4,0,0,0,14.4-14.4L512,14.4A14.4,14.4,0,0,0,497.6,0ZM432,288H416a16,16,0,0,0-16,16V458a6,6,0,0,1-6,6H54a6,6,0,0,1-6-6V118a6,6,0,0,1,6-6H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V304A16,16,0,0,0,432,288Z"/></svg>
48
41
  </a>
49
42
  </div>
@@ -51,9 +44,9 @@
51
44
  </Textbox>
52
45
  </div>
53
46
 
54
- <Tabs :items="previewModes" v-model="store.previewMode" variant="button" @change="updateIframe" />
47
+ <Tabs :items="previewModes" v-model="store.previewMode" variant="button" @change="updateIframe" class="rounded-full" />
55
48
 
56
- <div v-if="pageHistory" class="flex flex-row border-[1px] border-text-200 bg-text-50 rounded-full overflow-hidden">
49
+ <div v-if="pageHistory" class="flex flex-row border-[1px] border-border-200 bg-text-50 rounded-full overflow-hidden">
57
50
  <button type="button" class="p-2 px-3 group"
58
51
  :class="pageHistory.canUndo.value && pageHistory.history.value.length > 2 ? 'hover:bg-primary' : 'opacity-50 cursor-not-allowed'"
59
52
  :disabled="!(pageHistory.canUndo.value && pageHistory.history.value.length > 2)"
@@ -76,27 +69,27 @@
76
69
  class="w-[70px] rounded-full p-1"
77
70
  :state="canSave ? 1 : -1"
78
71
  @click="save">{{ $t('Save') }}</Button>
72
+
79
73
  </div>
80
74
  </div>
81
75
 
82
76
  <div class="flex-1 flex flex-row">
83
77
 
84
- <div class="flex flex-row bg-base-400 dark:bg-base-300 border-r-[1px] border-text-50"
78
+ <div ref="leftPanel" class="flex flex-row"
85
79
  :style="section1Style">
86
80
 
87
81
  <div class="flex-1 flex flex-col overflow-y-auto">
88
82
 
89
- <div class="flex flex-col items-center border-b-[1px] border-text-100" @click="currentArea = 'header'">
83
+ <div class="flex flex-col px-3 border-b-[1px] border-border-100 overflow-x-auto no-scrollbar pt-1">
90
84
  <Tabs :items="tabItems"
91
- class="pt-1"
92
85
  v-model="store.tabIndex" />
93
86
  </div>
94
87
 
95
- <div v-if="store.tabIndex === 1" class="flex-1 overflow-y-auto flex flex-col gap-4 p-6" @click="currentArea = 'pageinfo'">
88
+ <div v-if="store.tabIndex === 1" class="flex-1 overflow-y-auto flex flex-col gap-4 p-6">
96
89
 
97
90
  <div class="flex flex-row gap-4" v-if="!page.isSystem">
98
91
  <div class="flex flex-col gap-1">
99
- <label class="text-text-400 w-[40px]">{{ $t('Status') }}</label>
92
+ <small class="text-text-400 w-[40px]">{{ $t('Status') }}</small>
100
93
  <Dropdown v-model="page.status">
101
94
  <option :value="-1">{{ $t('Disabled') }}</option>
102
95
  <option :value="0">{{ $t('Draft') }}</option>
@@ -105,52 +98,55 @@
105
98
  </div>
106
99
 
107
100
  <div class="flex-1">
108
- <label class="text-text-400">{{ $t('Path')}}</label>
101
+ <small class="text-text-400">{{ $t('Path')}}</small>
109
102
  <Textbox v-model="page.path" />
110
103
  </div>
111
104
  </div>
112
105
 
113
106
  <div class="flex flex-col gap-1">
114
- <label class="text-text-400">{{ $t('Title')}}</label>
107
+ <small class="text-text-400">{{ $t('Title')}}</small>
115
108
  <Textbox v-model="page.title" />
116
109
  </div>
117
110
 
118
111
  <div class="flex flex-col gap-1">
119
- <label class="text-text-400">{{ $t('Description')}}</label>
112
+ <small class="text-text-400">{{ $t('Description')}}</small>
120
113
  <Textarea v-model="page.description" />
121
114
  </div>
122
115
 
123
116
  <div class="flex flex-col gap-1">
124
- <label class="text-text-400">{{ $t('Keywords')}}</label>
117
+ <small class="text-text-400">{{ $t('Keywords')}}</small>
125
118
  <Textbox v-model="page.keywords" />
126
119
  </div>
127
120
 
128
121
  <div>
129
122
  <div class="mb-1">
130
- <label class="text-text-400">{{ $t('OG')}}</label>
123
+ <small class="text-text-400">{{ $t('OG')}}</small>
131
124
  </div>
132
- <div class="flex flex-row gap-4 bg-base-500 rounded-lg p-2 cursor-pointer"
133
- @click="$refs.ogModal.open({ ...this.page.og })"
125
+ <div class="flex flex-row gap-2 bg-base-500 border-[1px] border-border-200 rounded-lg p-1 cursor-pointer"
126
+ @click="$refs.ogModal.open({ ...this.page.og }, og => page.og = og)"
134
127
  v-if="page.og && Object.keys(page.og).length > 0">
135
128
  <div>
136
- <Image :src="imageUrl(page.og)" class="w-[40px] aspect-square bg-text-50" />
129
+ <Image :src="page.og.image" class="w-[40px] aspect-square bg-text-50" />
137
130
  </div>
138
131
  <div class="flex-1 flex flex-col">
139
- <small class="text-text-400 pointer-events-none">{{ page.og.type }}</small>
140
- <label class="pointer-events-none line-clamp-2">{{ page.og.title }}</label>
141
- <small class="text-text-400 pointer-events-none line-clamp-1">{{ page.og.url }}</small>
132
+ <label class="text-sm text-ellipsis-nowrap">
133
+ {{ page.og.title }}
134
+ </label>
135
+ <small class="text-sm text-ellipsis-nowrap">
136
+ {{ page.og.description }}
137
+ </small>
142
138
  </div>
143
139
  </div>
144
- <div v-else class="bg-base-500 p-2 text-center border-[1px] border-text-100 rounded-lg cursor-pointer"
145
- @click="$refs.ogModal.open({ ...this.page.og })">
140
+ <div v-else class="bg-base-500 p-2 text-center border-[1px] border-border-100 rounded-lg cursor-pointer"
141
+ @click="$refs.ogModal.open(page.og, og => page.og = og)">
146
142
  Set OG
147
143
  </div>
148
144
  </div>
149
145
 
150
146
  <div>
151
147
  <div class="flex flex-row gap-2 mb-1">
152
- <label class="flex-1 text-text-400">{{ $t('JSON-LD')}}</label>
153
- <button type="button" class="text-primary" @click="$refs.ldjsonModal.open({})">
148
+ <small class="flex-1 text-text-400">{{ $t('JSON-LD')}}</small>
149
+ <button type="button" class="text-primary text-sm" @click="$refs.ldjsonModal.open({})">
154
150
  {{ $t('Add') }}
155
151
  </button>
156
152
  </div>
@@ -166,46 +162,97 @@
166
162
  </div>
167
163
  </template>
168
164
  </ListItem>
169
- <div v-else class="bg-base-500 p-2 text-center border-[1px] border-text-100 rounded-lg cursor-pointer text-text-300">
165
+ <div v-else class="bg-base-500 p-2 text-center border-[1px] border-border-100 rounded-lg cursor-pointer text-text-300">
170
166
  No JSON-LD set
171
167
  </div>
172
168
  </div>
173
169
 
174
170
  <div>
175
- <label class="flex-1 text-text-400">{{ $t('No Index')}}</label>
176
- <Dropdown v-model="page.noIndex" class="max-w-[150px]">
171
+ <small class="flex-1 text-text-400">{{ $t('No Index')}}</small>
172
+ <Dropdown v-model="page.noIndex" class="w-full max-w-[300px]">
177
173
  <option value="none">None</option>
178
174
  <option value="robots">All</option>
179
175
  <option value="googlebot">Google Bot</option>
180
176
  </Dropdown>
181
177
  </div>
182
178
 
183
- <div class="h-[1px] bg-text-100 my-3"></div>
179
+ </div>
180
+
181
+ <div v-else-if="store.tabIndex === 2" class="flex-1 overflow-y-auto p-6 flex flex-col gap-6">
184
182
 
185
183
  <div>
186
- <label class="flex-1 text-text-400">{{ $t('Require Login')}}</label>
187
- <Dropdown v-model="page.requireLogin" class="max-w-[150px]">
188
- <option value="none">None</option>
189
- <option value="popup">Popup</option>
190
- <option value="page">Page</option>
191
- </Dropdown>
184
+ <div class="flex flex-row gap-1 items-end cursor-pointer">
185
+ <small class="flex-1 text-text-400 text-overflow-ellipsis">{{ $t('Components')}}</small>
186
+ <button type="button" class="text-primary text-sm flex flex-row items-center gap-1"
187
+ @click="openComponentSelector({ callback:(component) => page.components.push(component) })">
188
+ {{ $t('Add')}}
189
+ </button>
190
+ </div>
191
+
192
+ <div class="flex flex-col gap-10">
193
+ <TreeView class="mt-2"
194
+ v-model="page.components"
195
+ :selected-item="currentItem"
196
+ @add="(items) => openComponentSelector({ callback:(component) => items.push(component) })"
197
+ @duplicate="duplicate"
198
+ @change="pageHistory.commit()">
199
+ <template #default="{ item }">
200
+ <div class="flex-1 text-ellipsis whitespace-nowrap overflow-hidden"
201
+ :class="!item.props.enabled ? 'line-through' : ''"
202
+ @click="select(item.uid)">
203
+ {{ item.props.name ?? item.type }}
204
+ </div>
205
+ </template>
206
+ </TreeView>
207
+ </div>
208
+ </div>
209
+
210
+ </div>
211
+
212
+ <div v-else-if="store.tabIndex === 3" class="flex-1 overflow-y-auto p-6">
213
+
214
+ <div class="p-6 flex flex-col gap-2" v-if="Array.isArray(page.datasource) && page.datasource.length > 0">
215
+ <div v-for="(ds, index) in page.datasource"
216
+ class="p-3 border-[1px] border-border-50 rounded-lg flex flex-row items-start gap-2">
217
+ <div class="flex-1 flex flex-col" @click="$refs.webDatasourceSelector.open({ _index:index, ...ds })">
218
+ <label>{{ ds.name }}</label>
219
+ </div>
220
+ <button type="button"
221
+ @click="removeDatasource(index)">
222
+ <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
223
+ </button>
224
+ </div>
225
+ </div>
226
+ <div v-else>
227
+ <div class="p-6 text-center">
228
+ <small class="text-text-400">{{ $t('No Datasource')}}</small>
229
+ </div>
192
230
  </div>
193
231
 
194
- <div class="h-[1px] my-3"></div>
232
+ <div class="text-center" v-if="useDatasource">
233
+ <button type="button" class="text-primary"
234
+ @click="$refs.webDatasourceSelector.create()">
235
+ Add Datasource
236
+ </button>
237
+ </div>
238
+
239
+ <WebDatasourceSelector ref="webDatasourceSelector"
240
+ @apply="addDatasource"
241
+ :use-datasource="useDatasource" />
195
242
 
196
243
  </div>
197
244
 
198
- <div v-else-if="store.tabIndex === 2" class="flex-1 overflow-y-auto p-6 flex flex-col gap-6">
245
+ <div v-else-if="store.tabIndex === 4" class="flex-1 overflow-y-auto flex flex-col">
199
246
 
200
- <div>
201
- <div class="flex flex-row gap-2" @click="currentArea = 'layout'">
202
- <label class="flex-1 text-text-400">{{ $t('Layout')}}</label>
203
- <button type="button" class="text-primary" @click="store.layoutMode = !store.layoutMode">
204
- {{ store.layoutMode ? 'Hide Layout' : 'Show Layout' }}
247
+ <div class="p-6">
248
+ <div class="flex flex-row items-end gap-2">
249
+ <small class="flex-1 text-text-400">{{ $t('Layout')}}</small>
250
+ <button type="button" class="text-primary text-sm" @click="store.layoutMode = !store.layoutMode">
251
+ {{ store.layoutMode ? 'Hide Layout' : 'Edit Layout' }}
205
252
  </button>
206
253
  </div>
207
- <div class="mt-2">
208
- <div class="cursor-pointer bg-text-50 p-2 border-[1px] border-text-200 flex flex-row items-center rounded-lg"
254
+ <div class="mt-1">
255
+ <div class="cursor-pointer bg-text-50 p-2 border-[1px] border-border-200 flex flex-row items-center rounded-lg"
209
256
  @click="(e) => $refs.layoutSelector.open(e.target)">
210
257
  <div class="flex-1 pointer-events-none">
211
258
  {{ layout ? (layout.name ?? layout.title) : 'None' }}
@@ -218,19 +265,21 @@
218
265
  </div>
219
266
 
220
267
  <ContextMenu ref="layoutSelector">
221
- <div class="flex flex-col min-w-[260px] divide-y divide-text-50">
268
+ <div class="flex flex-col min-w-[260px] divide-y divide-border-50">
222
269
 
223
- <div class="p-3" @click="page.layoutId = null;">None</div>
270
+ <div @click="page.layoutId = null;" :class="appStyle.menuItem">None</div>
224
271
 
225
272
  <div v-for="layout in layouts"
226
- class="p-3"
273
+ :class="appStyle.menuItem"
227
274
  @click="page.layoutId = layout.id;pageHistory.commit()">
228
275
  {{ layout.name ?? layout.title }}
229
276
  </div>
230
277
 
231
- <div class="flex flex-row" v-if="canManageLayout">
232
- <div class="p-3 flex-1 cursor-pointer text-primary" @click="$refs.webLayoutSelector.create()">Create New</div>
233
- <div class="p-3 cursor-pointer text-primary" @click="$refs.webLayoutSelector.open(layout.id)">Manage</div>
278
+ <div class="flex flex-row">
279
+ <button type="button" class="p-3 flex-1 cursor-pointer text-primary"
280
+ @click="$refs.webLayoutSelector.create({})">
281
+ Create New...
282
+ </button>
234
283
  </div>
235
284
 
236
285
  </div>
@@ -238,57 +287,26 @@
238
287
  </div>
239
288
  </div>
240
289
 
241
- <div v-if="store.layoutMode && layout" class="flex flex-row gap-2"
242
- @click="currentArea = 'style'">
243
- <label class="flex-1 text-text-400">{{ $t('Style')}}</label>
244
- <button type="button" class="text-primary"
245
- @click="store.selectedComponent = [ 'style' ]">Edit Style</button>
246
- </div>
247
-
248
- <div v-if="store.layoutMode && layout" @click="currentArea = 'headers'">
249
- <div class="flex flex-row gap-1 items-center cursor-pointer">
250
- <svg v-if="!expanded['headers']" width="12" height="12" @click="expanded['headers'] = !expanded['headers']" class="fill-text" 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>
251
- <svg v-else width="12" height="12" @click="expanded['headers'] = !expanded['headers']" class="fill-text" 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>
252
- <strong class="flex-1 text-text-400 line-clamp-1" @click="expanded['headers'] = !expanded['headers']">{{ $t('Header')}}</strong>
253
- <button type="button" class="text-primary flex flex-row items-center gap-1"
254
- @click="openComponentSelector({ items:layout.headers, isLayout:true })">
255
- <svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z"/></svg>
256
- {{ $t('Add')}}
257
- </button>
258
- </div>
259
- <div v-if="expanded['headers']" class="flex flex-col gap-10">
260
- <TreeView class="mt-2"
261
- v-model="layoutHeaders"
262
- :selected-item="currentItem"
263
- @add="(items) => openComponentSelector({ items })">
264
- <template #default="{ item }">
265
- <div class="flex-1 text-ellipsis whitespace-nowrap overflow-hidden"
266
- :class="!item.props.enabled ? 'line-through' : ''"
267
- @click="select(item.uid)">
268
- {{ item.name ?? item.type }}
269
- </div>
270
- </template>
271
- </TreeView>
290
+ <div v-if="layout" class="flex flex-col gap-6 p-6 border-t-[1px] border-border-50">
291
+ <div>
292
+ <small class="text-text-400">Name</small>
293
+ <Textbox v-model="layout.title"/>
272
294
  </div>
273
- </div>
274
295
 
275
- <div @click="currentArea = 'components'">
276
- <div class="flex flex-row gap-1 items-center cursor-pointer">
277
- <svg v-if="!expanded['components']" width="12" height="12" @click="expanded['components'] = !expanded['components']" class="fill-text" 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>
278
- <svg v-else width="12" height="12" @click="expanded['components'] = !expanded['components']" class="fill-text" 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>
279
- <strong class="flex-1 text-text-400 line-clamp-1" @click="expanded['components'] = !expanded['components']">{{ $t('Components')}}</strong>
280
- <button type="button" class="text-primary flex flex-row items-center gap-1"
281
- @click="openComponentSelector({ items:page.components }); currentArea = ''">
282
- <svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z"/></svg>
283
- {{ $t('Add')}}
284
- </button>
285
- </div>
296
+ <div>
297
+ <div class="flex flex-row gap-1 items-end cursor-pointer">
298
+ <small class="flex-1 text-text-400 text-overflow-ellipsis">{{ $t('Headers')}}</small>
299
+ <button type="button" class="text-primary text-sm flex flex-row items-center gap-1"
300
+ @click="openComponentSelector({ callback:(component) => layout.headers.push(component) })">
301
+ {{ $t('Add')}}
302
+ </button>
303
+ </div>
286
304
 
287
- <div v-if="expanded['components']" class="flex flex-col gap-10">
288
305
  <TreeView class="mt-2"
289
- v-model="page.components"
306
+ v-model="layout.headers"
290
307
  :selected-item="currentItem"
291
- @add="(items) => openComponentSelector({ items })"
308
+ @add="(items) => openComponentSelector({ callback:(component) => items.push(component) })"
309
+ @duplicate="duplicate"
292
310
  @change="pageHistory.commit()">
293
311
  <template #default="{ item }">
294
312
  <div class="flex-1 text-ellipsis whitespace-nowrap overflow-hidden"
@@ -300,25 +318,21 @@
300
318
  </TreeView>
301
319
  </div>
302
320
 
303
- </div>
304
-
305
- <div v-if="store.layoutMode && layout" @click="currentArea = 'footers'">
306
- <div class="flex flex-row gap-1 items-center cursor-pointer">
307
- <svg v-if="!expanded['footers']" width="12" height="12" @click="expanded['footers'] = !expanded['footers']" class="fill-text" 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>
308
- <svg v-else width="12" height="12" @click="expanded['footers'] = !expanded['footers']" class="fill-text" 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>
309
- <strong class="flex-1 text-text-400 line-clamp-1" @click="expanded['footers'] = !expanded['footers']">{{ $t('Footer')}}</strong>
310
- <button type="button" class="text-primary flex flex-row items-center gap-1"
311
- @click="openComponentSelector({ items:layoutFooters, isLayout:true })">
312
- <svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z"/></svg>
313
- {{ $t('Add')}}
314
- </button>
315
- </div>
321
+ <div>
322
+ <div class="flex flex-row gap-1 items-end cursor-pointer">
323
+ <small class="flex-1 text-text-400 text-overflow-ellipsis">{{ $t('Footers')}}</small>
324
+ <button type="button" class="text-primary text-sm flex flex-row items-center gap-1"
325
+ @click="openComponentSelector({ callback:(component) => layout.footers.push(component) })">
326
+ {{ $t('Add')}}
327
+ </button>
328
+ </div>
316
329
 
317
- <div v-if="expanded['footers']" class="flex flex-col gap-10">
318
330
  <TreeView class="mt-2"
319
- v-model="layoutFooters"
331
+ v-model="layout.footers"
320
332
  :selected-item="currentItem"
321
- @add="(items) => openComponentSelector({ items })">
333
+ @add="(items) => openComponentSelector({ callback:(component) => items.push(component) })"
334
+ @duplicate="duplicate"
335
+ @change="pageHistory.commit()">
322
336
  <template #default="{ item }">
323
337
  <div class="flex-1 text-ellipsis whitespace-nowrap overflow-hidden"
324
338
  :class="!item.props.enabled ? 'line-through' : ''"
@@ -328,51 +342,17 @@
328
342
  </template>
329
343
  </TreeView>
330
344
  </div>
331
- </div>
332
345
 
333
- <div v-if="store.layoutMode && layout">
334
- <small class="text-text-400">{{ $t('Updating layout will affect all pages using this layout, proceed with caution.') }}</small>
335
- </div>
336
-
337
- <WebComponentSelector ref="webPageComponentSelector"
338
- component-src="template.load"
339
- :components="availableComponents"
340
- @apply="addComponent"
341
- dismissable="true"
342
- @dismiss="$refs.webPageComponentSelector.close()" />
343
-
344
- </div>
345
-
346
- <div v-else-if="store.tabIndex === 3" class="flex-1 overflow-y-auto p-6" @click="currentArea = 'datasource'">
347
-
348
- <div class="p-6 flex flex-col gap-2" v-if="Array.isArray(page.datasource) && page.datasource.length > 0">
349
- <div v-for="(ds, index) in page.datasource"
350
- class="p-3 border-[1px] border-text-50 rounded-lg flex flex-row items-start gap-2">
351
- <div class="flex-1 flex flex-col" @click="$refs.webDatasourceSelector.open({ _index:index, ...ds })">
352
- <label>{{ ds.name }}</label>
346
+ <div>
347
+ <div class="flex flex-row gap-1 items-end cursor-pointer">
348
+ <small class="flex-1 text-text-400 text-overflow-ellipsis">{{ $t('Styles')}}</small>
349
+ <button type="button" class="text-primary text-sm flex flex-row items-center gap-1"
350
+ @click="select('style')">
351
+ {{ $t('Edit Style')}}
352
+ </button>
353
353
  </div>
354
- <button type="button"
355
- @click="removeDatasource(index)">
356
- <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
357
- </button>
358
354
  </div>
359
355
  </div>
360
- <div v-else>
361
- <div class="p-6 text-center">
362
- <label class="text-text-400">{{ $t('No Datasource')}}</label>
363
- </div>
364
- </div>
365
-
366
- <div class="text-center" v-if="useDatasource">
367
- <button type="button" class="text-primary"
368
- @click="$refs.webDatasourceSelector.create()">
369
- Add Datasource
370
- </button>
371
- </div>
372
-
373
- <WebDatasourceSelector ref="webDatasourceSelector"
374
- @apply="addDatasource"
375
- :use-datasource="useDatasource" />
376
356
 
377
357
  </div>
378
358
 
@@ -381,107 +361,41 @@
381
361
  <div :class="$style.resize1"
382
362
  @mousedown="(e) => $util.dragResize(e, resize1)"></div>
383
363
 
384
- <Modal ref="ogModal" width="480" height="520">
385
- <template v-slot:head>
386
- <div class="relative p-5">
387
- <h3>{{ $t('Edit OG')}}</h3>
388
- <div class="absolute top-0 right-0 p-2">
389
- <button type="button" class="p-2" @click="$refs.ogModal.close()">
390
- <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">
391
- <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"/>
392
- </svg>
393
- </button>
394
- </div>
395
- </div>
396
- </template>
397
- <template v-slot:foot="{ context }">
398
- <div class="p-5">
399
- <Button type="button" class="w-[100px]"
400
- @click="Object.assign(this.page.og, context);$refs.ogModal.close()">
401
- OK
402
- </Button>
403
- </div>
404
- </template>
405
- <template #default="{ context }">
406
- <div class="flex-1 p-5 flex flex-col gap-4">
407
- <div class="flex flex-row gap-4">
408
- <div>
409
- <label class="text-text-400">Type</label>
410
- <Dropdown v-model="context.type" class="mt-1 w-[120px]">
411
- <option value="website">Website</option>
412
- <option value="article">Article</option>
413
- </Dropdown>
414
- </div>
415
- <div class="flex-1">
416
- <label class="text-text-400">Title</label>
417
- <Textbox v-model="context.title" class="mt-1" />
418
- </div>
419
- </div>
364
+ </div>
420
365
 
421
- <div>
422
- <label class="text-text-400">Description</label>
423
- <Textarea v-model="context.description" class="mt-1" rows="3" />
424
- </div>
366
+ <div ref="preview" class="flex-1 relative" :class="previewClass">
367
+
368
+ <div class="flex flex-row gap-4 items-center p-2">
369
+ <select v-model="store.zoomLevel" :class="$style.zoomLevel">
370
+ <optgroup label="Zoom Level">
371
+ <option value="fit">Fit</option>
372
+ <option value="125%">125%</option>
373
+ <option value="100%">100%</option>
374
+ <option value="75%">75%</option>
375
+ <option value="50%">50%</option>
376
+ </optgroup>
377
+ </select>
425
378
 
426
- <div>
427
- <label class="text-text-400">Url</label>
428
- <Textbox v-model="context.url" class="mt-1" />
429
- </div>
379
+ <div class="flex-1"></div>
430
380
 
431
- <div>
432
- <div class="flex flex-row">
433
- <label class="text-text-400 flex-1">Image</label>
434
- <button type="button" class="text-primary"
435
- @click="$refs.ogImage.edit()">Change Image</button>
436
- </div>
437
- <div class="mt-1">
438
- <Image ref="ogImage" :src="imageUrl(context)" class="w-[80px] aspect-square"
439
- :editable="true"
440
- @change="(base64, file) => { uploadImage(file).then((res) => context.imageUrl = res.name); }" />
441
- </div>
442
- </div>
443
- </div>
444
- </template>
445
- </Modal>
446
-
447
- <Modal ref="ldjsonModal" width="600" height="480">
448
- <template v-slot:head>
449
- <div class="relative p-5">
450
- <h3>JSON-LD</h3>
451
- <div class="absolute top-0 right-0 p-2">
452
- <button type="button" class="p-2" @click="$refs.ldjsonModal.close()">
453
- <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">
454
- <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"/>
455
- </svg>
381
+ <div class="flex flex-row border-[1px] border-border-200 divide-x divide-border-50" :class="$style.zoomLevel">
382
+ <Radio v-for="_type in viewTypes" :value="_type.value" v-model="store.viewType" :custom="true">
383
+ <template #default="">
384
+ <button v-if="_type.value === ''" type="button" class="w-[30px] flex items-center justify-center">
385
+ <svg width="14" height="14" :class="_type.value === store.viewType ? 'fill-primary' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M320 0H64C37.5 0 16 21.5 16 48v416C16 490.5 37.5 512 64 512h256c26.5 0 48-21.5 48-48v-416C368 21.5 346.5 0 320 0zM240 447.1C240 456.8 232.8 464 224 464H159.1C151.2 464 144 456.8 144 448S151.2 432 160 432h64C232.8 432 240 439.2 240 447.1zM304 384h-224V64h224V384z"/></svg>
456
386
  </button>
457
- </div>
458
- </div>
459
- </template>
460
- <template #foot="{ context }">
461
- <div class="p-5">
462
- <Button type="button" class="w-[100px]"
463
- @click="saveLdjson(context)">
464
- Save
465
- </Button>
466
- </div>
467
- </template>
468
- <template #default="{ context }">
469
- <div class="flex-1 p-5">
470
- <Textarea v-model="context.code" rows="10" ></Textarea>
471
- </div>
472
- </template>
473
- </Modal>
474
-
475
- </div>
476
-
477
- <div class="flex-1 bg-base-300 dark:bg-base-400 relative" :class="previewClass" ref="preview"
478
- @click="currentArea = 'preview'">
479
- <div class="flex flex-row p-2">
480
- <select class="p-1 text-sm cursor-pointer bg-text-50 outline-none"
481
- v-model="store.previewViewType"
482
- @change="resize">
483
- <option v-for="_type in previewViewTypes" :value="_type.value">{{ _type.text }}</option>
484
- </select>
387
+ <button v-if="_type.value === 'md:'" type="button" class="w-[30px] flex items-center justify-center">
388
+ <svg width="14" height="14" :class="_type.value === store.viewType ? 'fill-primary' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M384 .0001H64c-35.35 0-64 28.65-64 64v384c0 35.35 28.65 63.1 64 63.1h320c35.35 0 64-28.65 64-63.1v-384C448 28.65 419.3 .0001 384 .0001zM288 448c0 8.837-7.163 16-15.1 16H175.1c-8.837 0-15.1-7.163-15.1-16s7.163-16 15.1-16h96C280.8 432 288 439.2 288 448zM384 384H64v-320h320V384z"/></svg>
389
+ </button>
390
+ <button v-if="_type.value === 'xl:'" type="button" class="w-[30px] flex items-center justify-center">
391
+ <svg width="14" height="14" :class="_type.value === store.viewType ? 'fill-primary' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M528 0h-480C21.5 0 0 21.5 0 48v320C0 394.5 21.5 416 48 416h192L224 464H152C138.8 464 128 474.8 128 488S138.8 512 152 512h272c13.25 0 24-10.75 24-24s-10.75-24-24-24H352L336 416h192c26.5 0 48-21.5 48-48v-320C576 21.5 554.5 0 528 0zM512 288H64V64h448V288z"/></svg>
392
+ </button>
393
+ <button v-if="_type.value === '2xl:'" type="button" class="w-[30px] flex items-center justify-center">
394
+ <svg width="14" height="14" :class="_type.value === store.viewType ? 'fill-primary' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M512 448H127.1C110.3 448 96 462.3 96 479.1S110.3 512 127.1 512h384C529.7 512 544 497.7 544 480S529.7 448 512 448zM592 0h-544C21.5 0 0 21.5 0 48v320C0 394.5 21.5 416 48 416h544c26.5 0 48-21.5 48-48v-320C640 21.5 618.5 0 592 0zM576 352H64v-288h512V352z"/></svg>
395
+ </button>
396
+ </template>
397
+ </Radio>
398
+ </div>
485
399
  </div>
486
400
 
487
401
  <div class="p-6">
@@ -490,74 +404,97 @@
490
404
  </div>
491
405
  </div>
492
406
 
493
- <div v-if="currentItem" class="flex flex-col bg-base-400 dark:bg-base-300 border-l-[1px] border-text-50"
494
- @click="currentArea = 'properties'"
495
- :style="section3Style">
496
-
497
- <div class="px-6 py-4 flex flex-row items-center gap-2">
498
- <Textbox v-if="currentItem.type !== 'Style'"
499
- v-model="currentItem.props.name"
500
- :placeholder="currentItem.type"
501
- class="bg-base-500 rounded-full flex-1"
502
- item-class="p-0 px-2"
503
- variant="minimal">
504
- <template #start>
505
- <div class="pl-3">
506
- <Switch v-model="currentItem.props.enabled"/>
507
- </div>
508
- </template>
509
- </Textbox>
407
+ <div v-if="currentItem" ref="rightPanel" class="flex flex-row"
408
+ :style="section3Style" @click.meta="log(currentItem)">
510
409
 
511
- <div v-else class="px-3">
512
- <h4>Style</h4>
513
- </div>
514
- </div>
410
+ <div :class="$style.resize3"
411
+ @mousedown="(e) => $util.dragResize(e, resize3)"></div>
515
412
 
516
- <div v-if="false" class="px-4 bg-base-300 pt-2 relative">
517
- <Tabs v-model="store.viewType" :items="viewTypes" variant="minimal">
518
- <template #tab="{ item }">
519
- <div v-if="item.value === ''" class="px-6 p-2 border-[1px] border-b-0 relative top-[1px] rounded-t-md overflow-hidden"
520
- :class="store.viewType === item.value ? 'bg-base-400 border-text-50' : 'border-transparent'">
521
- <div v-if="store.viewType === item.value" class="absolute top-0 left-0 right-0 h-[2px] bg-primary"></div>
522
- Mobile
523
- </div>
524
- <div v-else-if="item.value === 'md:'" class="px-6 p-2 border-[1px] border-b-0 relative top-[1px] rounded-t-md overflow-hidden"
525
- :class="store.viewType === item.value ? 'bg-base-400 border-text-50' : 'border-transparent'">
526
- <div v-if="store.viewType === item.value" class="absolute top-0 left-0 right-0 h-[2px] bg-primary"></div>
527
- Tablet
528
- </div>
529
- </template>
530
- </Tabs>
531
- </div>
413
+ <div class="flex-1 flex flex-col relative"
414
+ ref="rightPane">
532
415
 
533
- <div class="flex-1 flex flex-row border-t-[1px] border-text-50">
416
+ <TransitionGroup name="openltr" tag="div" class="flex-1 flex flex-col">
534
417
 
535
- <div :class="$style.resize3"
536
- @mousedown="(e) => $util.dragResize(e, resize3)"></div>
418
+ <div v-if="extRightPane === null" class="flex-1 flex flex-col divide-y divide-border-50 overflow-y-auto">
537
419
 
538
- <div class="flex-1 flex flex-col gap-6 overflow-y-auto p-6">
420
+ <div v-if="currentItem.type !== 'Style'" class="px-6 py-4 flex flex-row gap-5 items-start">
421
+ <div>
422
+ <small class="text-text-400">Enabled</small>
423
+ <Switch v-model="currentItem.props.enabled"/>
424
+ </div>
425
+ <div v-if="currentItem.type !== 'Style'" class="flex-1">
426
+ <small class="text-text-400">Name</small>
427
+ <Textbox v-model="currentItem.props.name"
428
+ :placeholder="currentItem.type"
429
+ class="bg-base-500 flex-1"
430
+ variant="minimal" />
431
+ </div>
432
+ <div v-else class="px-3">
433
+ <h4>Style</h4>
434
+ </div>
435
+ </div>
539
436
 
540
- <div>
541
437
  <component :is="`${currentItem.type}Setting`"
542
438
  :item="currentItem"
543
439
  :view-type="store.viewType"
440
+ :view-index="viewIndex"
544
441
  :view-types="viewTypes"
545
442
  ref="settingComponent"
546
443
  @change="pageHistory.commit()"
547
444
  @postMessageToIframe="onPostMessageToIframe"/>
445
+
446
+ <div class="p-6 py-4" v-if="debugMode">
447
+ <small class="text-text-400">Debug ID</small>
448
+ <Textbox v-model="currentItem.props.debugId" maxlength="5" />
449
+ </div>
450
+
451
+ <br />
452
+ <br />
453
+ <br />
454
+
548
455
  </div>
549
456
 
550
- <div class="p-5 text-center" v-if="useTemplateCreator && currentItem.type !== 'Style'">
551
- <button type="button" class="text-primary"
552
- @click="openTemplateCreator">
553
- Save as Template
554
- </button>
457
+ <div v-else-if="extRightPane?.type" class="flex-1 flex flex-col divide-y divide-border-50 overflow-y-auto">
458
+
459
+ <div class="px-6 py-4 flex flex-row gap-5 items-center">
460
+ <div class="flex flex-col">
461
+ <small class="text-text-400">&nbsp;</small>
462
+ <button type="button" @click="extRightPane = null">
463
+ <svg width="14" height="14" class="fill-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M447.1 256C447.1 273.7 433.7 288 416 288H109.3l105.4 105.4c12.5 12.5 12.5 32.75 0 45.25C208.4 444.9 200.2 448 192 448s-16.38-3.125-22.62-9.375l-160-160c-12.5-12.5-12.5-32.75 0-45.25l160-160c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25L109.3 224H416C433.7 224 447.1 238.3 447.1 256z"/></svg>
464
+ </button>
465
+ </div>
466
+ <div>
467
+ <small class="text-text-400">Enabled</small>
468
+ <Switch v-model="extRightPane.props.enabled"/>
469
+ </div>
470
+ <div v-if="extRightPane.type !== 'Style'" class="flex-1">
471
+ <small class="text-text-400">Name</small>
472
+ <Textbox v-model="currentItem.props.name"
473
+ :placeholder="extRightPane.type"
474
+ class="bg-base-500 flex-1"
475
+ variant="minimal" />
476
+ </div>
477
+ <div v-else class="px-3">
478
+ <h4>Style</h4>
479
+ </div>
480
+ </div>
481
+
482
+ <component :is="`${extRightPane.type}Setting`"
483
+ :item="extRightPane"
484
+ :view-type="store.viewType"
485
+ :view-index="viewIndex"
486
+ :view-types="viewTypes"
487
+ ref="settingComponent"
488
+ @change="pageHistory.commit()"
489
+ @postMessageToIframe="onPostMessageToIframe"/>
490
+
491
+ <br />
492
+ <br />
493
+ <br />
555
494
 
556
- <WebTemplateCreator :="useTemplateCreator"
557
- ref="templateCreator"
558
- @save="templateCreatorAfterSave" />
559
495
  </div>
560
- </div>
496
+
497
+ </TransitionGroup>
561
498
 
562
499
  </div>
563
500
 
@@ -568,7 +505,44 @@
568
505
  </div>
569
506
 
570
507
  <div class="absolute">
571
- <WebLayoutSelector ref="webLayoutSelector" :layouts="layouts" @apply="createLayout"/>
508
+ <WebLayoutSelector ref="webLayoutSelector" @apply="createLayout"/>
509
+
510
+ <WebComponentSelector ref="webPageComponentSelector"
511
+ :component-src="`${this.controller}.load-templates`"
512
+ :components="availableComponents"
513
+ @apply="addComponent"
514
+ dismissable="true"
515
+ @dismiss="$refs.webPageComponentSelector.close()" />
516
+
517
+ <OGSettingModal ref="ogModal" />
518
+
519
+ <Modal ref="ldjsonModal" width="600" height="480">
520
+ <template v-slot:head>
521
+ <div class="relative p-5">
522
+ <h3>JSON-LD</h3>
523
+ <div class="absolute top-0 right-0 p-2">
524
+ <button type="button" class="p-2" @click="$refs.ldjsonModal.close()">
525
+ <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">
526
+ <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"/>
527
+ </svg>
528
+ </button>
529
+ </div>
530
+ </div>
531
+ </template>
532
+ <template #foot="{ context }">
533
+ <div class="p-5">
534
+ <Button type="button" class="w-[100px]"
535
+ @click="saveLdjson(context)">
536
+ Save
537
+ </Button>
538
+ </div>
539
+ </template>
540
+ <template #default="{ context }">
541
+ <div class="flex-1 p-5">
542
+ <Textarea v-model="context.code" rows="10" ></Textarea>
543
+ </div>
544
+ </template>
545
+ </Modal>
572
546
  </div>
573
547
 
574
548
  </div>
@@ -578,14 +552,22 @@
578
552
 
579
553
  import throttle from "lodash/throttle";
580
554
  import md5 from "md5";
581
- import {copyToClipboard, createFormData, getClipboardData} from "../utils/helpers.mjs";
555
+ import {createFormData, invokeAfterIdle} from "../utils/helpers.mjs";
582
556
  import {ref} from 'vue'
583
557
  import {useManualRefHistory} from "@vueuse/core";
584
558
  import axios from "axios";
585
559
  import {useRouter} from "vue-router";
560
+ import WebLayoutSelector from "./WebLayoutSelector.vue";
561
+ import WebComponentSelector from "./WebComponentSelector.vue";
562
+ import defaultConfig from "../configs/web-page-builder.js"
563
+ import OGSettingModal from "./OGSettingModal.vue";
564
+
565
+ const patchPageCache = {}
586
566
 
587
567
  export default{
588
568
 
569
+ components: {OGSettingModal, WebComponentSelector, WebLayoutSelector},
570
+
589
571
  setup(){
590
572
  const page = ref({})
591
573
  const pageHistory = useManualRefHistory(page, { clone:true })
@@ -598,78 +580,34 @@ export default{
598
580
 
599
581
  props: {
600
582
 
601
- /**
602
- * Can add/remove layout
603
- */
604
583
  canManageLayout: {
605
584
  type: Boolean,
606
585
  default: false
607
586
  },
608
587
 
609
- /**
610
- * Exclude components
611
- * @param {Array} components
612
- * @param {String} components[]
613
- */
588
+ controller: String,
589
+
614
590
  excludeComponents: {
615
591
  type: Array,
616
592
  default: []
617
593
  },
618
594
 
619
- /**
620
- * Add more components
621
- * @param {Array} components
622
- * @param {String} components[]
623
- */
624
595
  moreComponents: {
625
596
  type: Array,
626
597
  default: []
627
598
  },
628
599
 
629
- /**
630
- * Persist web page builder state
631
- */
632
- store: {
633
- type: Object,
634
- default: {
635
- version: '0.0.999',
636
- layoutMode: false,
637
- selectedComponent: null, // [ 'uid|style', 'style|headers|components|footers' ]
638
- tabIndex: 2,
639
- viewType: '',
640
- zoomLevel: 'fit',
641
- width: [ 320, 320 ]
642
- }
643
- },
644
-
645
- src:{
646
- type: [ String, Array ],
647
- default: 'page.open'
648
- },
649
-
650
- saveSrc:{
651
- type: [ String, Array ],
652
- default: 'page.save'
653
- },
654
-
655
- /**
656
- * @param {Object} config
657
- * @param {String} config.method
658
- * @param {String} config.url
659
- */
660
600
  uploadConfig: {
661
- type: Object,
662
- required: true
601
+ type: Object
663
602
  },
664
603
 
665
- /**
666
- * Enable dynamic datasource for page
667
- * @param {Object} config
668
- * @param {String} config.url
669
- */
604
+ uploadImageFn: Function,
605
+
670
606
  useDatasource: undefined,
671
607
 
672
- useTemplateCreator: undefined
608
+ useTemplateCreator: undefined,
609
+
610
+ presetKey: String
673
611
 
674
612
  },
675
613
 
@@ -708,27 +646,6 @@ export default{
708
646
  this.loadDatasource()
709
647
  },
710
648
 
711
- cleanItem(item){
712
-
713
- delete item.uid
714
- delete item.enabled
715
-
716
- if(item.props){
717
- for(let key in item.props){
718
- if(Array.isArray(item.props[key]) &&
719
- (item.props[key].length === 0 || [ '[{},{}]', '[]', '["",""]', '[""]' ].includes(JSON.stringify(item.props[key])))){
720
- delete item.props[key]
721
- }
722
- }
723
- }
724
-
725
- if(Array.isArray(item.items)){
726
- for(let i in item.items){
727
- this.cleanItem(item.items[i])
728
- }
729
- }
730
- },
731
-
732
649
  close(){
733
650
  this.$emit('close')
734
651
  },
@@ -760,7 +677,13 @@ export default{
760
677
  return Array.isArray(component.props[key]) ? component.props[key].join(' ') : ''
761
678
  })
762
679
  .filter(_ => _)
763
- .join(' ')
680
+ .join(' '),
681
+
682
+ style: this.styleClasses.reduce((res, cur) => {
683
+ if(Array.isArray(component.props[cur]))
684
+ res[cur] = component.props[cur]
685
+ return res
686
+ }, {})
764
687
  }
765
688
 
766
689
  for(let key in component.props){
@@ -783,40 +706,111 @@ export default{
783
706
  instance.items = component.items.map((_) => this.createComponentInstance(_)).filter(_=>_)
784
707
  }
785
708
 
709
+ if(Array.isArray(component.items2)){
710
+ instance.items2 = component.items2.map((_) => this.createComponentInstance(_)).filter(_=>_)
711
+ }
712
+
713
+ if(Array.isArray(component.items3)){
714
+ instance.items3 = component.items3.map((_) => this.createComponentInstance(_)).filter(_=>_)
715
+ }
716
+
786
717
  if(component.props && Array.isArray(component.props.items)){
787
718
  instance.items = component.props.items
788
719
  }
789
720
 
721
+ if(component.slots){
722
+ const slots = {}
723
+ for(let key in component.slots){
724
+ slots[key] = component.slots[key].map((_) => this.createComponentInstance(_)).filter(_=>_)
725
+ }
726
+ instance.slots = slots
727
+ }
728
+
790
729
  return instance
791
730
  },
792
731
 
793
732
  createLayout(layout){
794
- this.page.layoutId = layout.id
733
+ this.socket.send(`${this.controller}.create-layout`, layout)
734
+ .then(layout => {
735
+ this.loadLayouts()
736
+ this.page.layoutId = layout.id
737
+ this.$refs.webLayoutSelector.close()
738
+ })
739
+ .catch(err => this.alert(err))
795
740
  },
796
741
 
797
- copy(){
798
- if(!this.currentItem) return
799
- if(![ 'components', 'headers', 'footers' ].includes(this.currentArea)) return
742
+ createStyleSheet(styles){
743
+
744
+ const mediaQueries = {
745
+ '': '@media screen',
746
+ 'md:': '@media screen and (min-width: 640px)',
747
+ }
800
748
 
801
- const copyItem = JSON.parse(JSON.stringify(this.currentItem))
802
- this.cleanItem(copyItem)
749
+ const fontFamilies = {
750
+ '"Anton", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Anton&display=swap');`,
751
+ 'Dosis, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Dosis:wght@400;700&display=swap');`,
752
+ 'Lato, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap');`,
753
+ 'Merriweather, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap');`,
754
+ 'Montserrat, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');`,
755
+ '"Noto Sans", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;800&display=swap');`,
756
+ 'Oswald, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&display=swap');`,
757
+ 'Oxygen, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@400;700&display=swap');`,
758
+ 'Poppins, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');`,
759
+ 'Quantico, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Quantico:wght@400;700&display=swap"');`,
760
+ 'Raleway, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;700&display=swap');`,
761
+ '"Reddit Sans", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Reddit+Sans:wght@400;700&display=swap');`,
762
+ '"Roboto", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');`,
763
+ '"Roboto Slab", serif': `@import url('https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;800&display=swap');`,
764
+ 'Volkhov, serif': `@import url('https://fonts.googleapis.com/css2?family=Volkhov:wght@400;700&display=swap');`,
765
+ '"Work Sans", serif': `@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;700&display=swap');`,
766
+ }
803
767
 
804
- copyToClipboard(JSON.stringify(copyItem))
805
- .then(() => this.toast('Copied to clipboard'))
768
+ let text = ''
769
+ const usedFonts = {}
770
+ for(let mediaKey in styles?.media){
771
+ const css = styles.media[mediaKey]
772
+
773
+ text += mediaQueries[mediaKey] + '{ '
774
+ for(let key in css){
775
+ text += key + '{ '
776
+ for(let selector in css[key]){
777
+ text += selector + ':' + css[key][selector] + ';'
778
+
779
+ if(selector === 'font-family' && fontFamilies[css[key][selector]]){
780
+ usedFonts[css[key][selector]] = fontFamilies[css[key][selector]]
781
+ }
782
+ }
783
+ text += '}'
784
+ }
785
+ text += '}\n'
786
+ }
787
+
788
+ return [
789
+ ...Object.values(usedFonts),
790
+ text
791
+ ]
792
+ .join("\n")
806
793
  },
807
794
 
808
- openTemplateCreator(){
809
- const copyItem = JSON.parse(JSON.stringify(this.currentItem))
810
- this.cleanItem(copyItem)
795
+ createInstances(){
796
+ if(!this.page.instances || typeof this.page.instances !== 'object' || Array.isArray(this.page.instances))
797
+ this.page.instances = {}
798
+ this.page.instances.components = (this.page.components ?? []).map((_) => this.createComponentInstance(_)).filter(_=>_)
811
799
 
812
- this.$refs.templateCreator.open({
813
- data: copyItem
814
- })
800
+ if(this.layout){
801
+ if(!this.layout.instances || typeof this.layout.instances !== 'object' || Array.isArray(this.layout.instances))
802
+ this.layout.instances = {}
803
+ this.layout.instances.headers = (this.layout.headers ?? []).map((_) => this.createComponentInstance(_)).filter(_=>_)
804
+ this.layout.instances.footers = (this.layout.footers ?? []).map((_) => this.createComponentInstance(_)).filter(_=>_)
805
+
806
+ this.layout.instances.stylesheet = this.createStyleSheet(this.layout.styles)
807
+ }
815
808
  },
816
809
 
817
- templateCreatorAfterSave(){
818
- this.toast('Template created')
819
- this.$refs.webPageComponentSelector.load()
810
+ duplicate(parent, item){
811
+ const newItem = JSON.parse(JSON.stringify(item))
812
+ this.setUid(newItem)
813
+ parent.push(newItem)
820
814
  },
821
815
 
822
816
  findCompByUid(uid, components){
@@ -893,7 +887,7 @@ export default{
893
887
  },
894
888
 
895
889
  listen(){
896
- this.socketEmit2('page.subscribe', { name:'page' })
890
+ this.socket.send(`${this.controller}.subscribe`, { name:'page' })
897
891
  .then(() => {
898
892
 
899
893
  })
@@ -901,11 +895,15 @@ export default{
901
895
  },
902
896
 
903
897
  async load(){
904
- return this.socketEmit2(this.src, { uid:this.$route.params.uid })
905
- .then(({ page, layouts, host }) => {
906
- if(page) this.page = page
907
- if(layouts) this.layouts = layouts
898
+ return this.socket.send(`${this.controller}.open`, { uid:this.$route.params.uid })
899
+ .then(({ page, host, previewHost, debugMode }) => {
900
+
901
+ this.patchPage(page)
902
+
903
+ if(page) Object.assign(this.page, page)
908
904
  if(host) this.host = host
905
+ this.debugMode = debugMode ?? this.debugMode
906
+ if(previewHost) this.previewHost = previewHost
909
907
 
910
908
  this.prevData = {
911
909
  page: JSON.stringify(this.page),
@@ -914,27 +912,129 @@ export default{
914
912
 
915
913
  this.pageHistory.commit()
916
914
 
917
- this.$nextTick(() => {
918
- this.resize()
919
- this.iframeSrc = (this.host ?? import.meta.env.VITE_WEB_HOST) + '/' + (this.page.path ?? '') +
920
- `?edit-mode=${this.store.previewMode}`
921
- window.setTimeout(() => {
922
- this.updateIframe()
923
- }, 500)
924
- })
915
+ host = this.host ?? import.meta.env.VITE_WEB_HOST
925
916
 
926
- this.state = 1
917
+ if(host){
918
+ this.$nextTick(() => {
919
+ this.resize()
927
920
 
921
+ if(this.previewHost){
922
+ this.iframeSrc = this.previewHost + `?edit-mode=${this.store.previewMode}`
923
+ }
924
+ else{
925
+ this.iframeSrc = host + '/' + (this.page.path ?? '') +
926
+ `?edit-mode=${this.store.previewMode}`
927
+ }
928
+ })
929
+ }
930
+
931
+ this.readyState = 1
928
932
  })
929
933
  .catch((err) => {
930
934
  this.toast(err)
931
- this.$emit('close')
935
+ this.readyState = -1
936
+ console.error(err)
937
+ })
938
+ },
939
+
940
+ patchBgColors(bgColors){
941
+ if (!Array.isArray(bgColors)) return
942
+
943
+ for(let i = 0 ; i < bgColors.length ; i++){
944
+ let value = bgColors[i]
945
+ if(`${bgColors[i]}`.startsWith('bg-')){
946
+ if(patchPageCache[bgColors[i]]){
947
+ value = patchPageCache[bgColors[i]]
948
+ }
949
+ else{
950
+ const el = document.createElement('div')
951
+ el.classList.add(bgColors[i])
952
+ el.style.position = 'fixed'
953
+ el.style.top = '-100000px'
954
+ document.body.appendChild(el)
955
+
956
+ const rgbText = window.getComputedStyle(el)['background-color']
957
+
958
+ const rgb = rgbText.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*\d+)?\)/)
959
+ if(rgb){
960
+ value = `#${((1 << 24) + (parseInt(rgb[1]) << 16) +
961
+ (parseInt(rgb[2]) << 8) +
962
+ parseInt(rgb[3])).toString(16).slice(1)}`
963
+ }
964
+ document.body.removeChild(el)
965
+
966
+ patchPageCache[bgColors[i]] = value
967
+ }
968
+
969
+ bgColors[i] = value
970
+ }
971
+ }
972
+ },
973
+
974
+ patchBdColor(bdColor){
975
+ if (!Array.isArray(bdColor)) return
976
+
977
+ for(let i = 0 ; i < bdColor.length ; i++){
978
+ let value = bdColor[i]
979
+ if(`${bdColor[i]}`.startsWith('border-')){
980
+ if(patchPageCache[bdColor[i]]){
981
+ value = patchPageCache[bdColor[i]]
982
+ }
983
+ else{
984
+ const el = document.createElement('div')
985
+ el.classList.add(bdColor[i])
986
+ el.style.position = 'fixed'
987
+ el.style.borderStyle = "solid"
988
+ el.style.borderWidth = "5px"
989
+ el.style.width = '100px'
990
+ el.style.height = '100px'
991
+ el.style.top = '10px'
992
+ el.style.left = '10px'
993
+ document.body.appendChild(el)
994
+
995
+ const rgbText = window.getComputedStyle(el)['border-color']
996
+ const rgb = rgbText.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*\d+)?\)/)
997
+ if(rgb){
998
+ value = `#${((1 << 24) + (parseInt(rgb[1]) << 16) +
999
+ (parseInt(rgb[2]) << 8) +
1000
+ parseInt(rgb[3])).toString(16).slice(1)}`
1001
+ }
1002
+ document.body.removeChild(el)
1003
+
1004
+ patchPageCache[bdColor[i]] = value
1005
+ }
1006
+
1007
+ bdColor[i] = value
1008
+ }
1009
+ }
1010
+ },
1011
+
1012
+ patchPage(page){
1013
+
1014
+ const recursePatch = (components) => {
1015
+ for(let component of components){
1016
+ this.patchBgColors(component.props.bgColors)
1017
+ this.patchBdColor(component.props.bdColor)
1018
+
1019
+ if(Array.isArray(component.items)){
1020
+ recursePatch(component.items)
1021
+ }
1022
+ }
1023
+ }
1024
+
1025
+ recursePatch(page.components)
1026
+ },
1027
+
1028
+ async loadLayouts(){
1029
+ this.socket.send(`${this.controller}.load-layouts`, {})
1030
+ .then(_ => {
1031
+ this.layouts = _
932
1032
  })
933
1033
  },
934
1034
 
935
1035
  loadDatasource(){
936
1036
  if(this.useDatasource && this.useDatasource[1]){
937
- this.socketEmit2(this.useDatasource[1], this.page.datasource)
1037
+ this.socket.send(this.useDatasource[1], this.page.datasource)
938
1038
  .then((data) => {
939
1039
  Object.assign(this.page, { data })
940
1040
  this.updateIframe()
@@ -942,32 +1042,40 @@ export default{
942
1042
  }
943
1043
  },
944
1044
 
945
- openComponentSelector(params){
946
- this.$refs.webPageComponentSelector.open(params)
947
- },
948
-
949
- paste(){
950
- if(!this.currentItem) return
951
- if(![ 'components', 'headers', 'footers' ].includes(this.currentArea)) return
1045
+ loadPreset(){
1046
+ if(!Object.keys(this.$route.query).map(_ => _.toLowerCase()).includes('reset')){
1047
+ if(this.presetKey){
1048
+ return this.socket.send(this.presetSrc, { key:this.presetKey })
1049
+ .then(config => {
1050
+ if(config){
1051
+ Object.assign(this.config.params, config.params)
952
1052
 
953
- getClipboardData().then(text => {
954
- try{
955
- const item = JSON.parse(text)
956
- Object.assign(item.props, {
957
- enabled: true
1053
+ if(this.$route.query?.search)
1054
+ this.preset.search = this.$route.query.search
1055
+ }
1056
+ })
1057
+ }
1058
+ return new Promise(resolve => resolve())
1059
+ }
1060
+ else{
1061
+ return new Promise((resolve) => {
1062
+ const query = {}
1063
+ for(let key in this.$route.query){
1064
+ if(key.toLowerCase() !== 'reset')
1065
+ query[key] = this.$route.query[key]
1066
+ }
1067
+ this.$router.replace({
1068
+ ...this.$route,
1069
+ query
958
1070
  })
959
- this.setUid(item)
960
1071
 
961
- const comp = this.findItemByUid(this.currentItem.uid)
962
- comp.items.splice(comp.items.indexOf(comp.item) + 1, 0, item)
963
- this.select(item.uid)
1072
+ resolve()
1073
+ })
1074
+ }
1075
+ },
964
1076
 
965
- this.pageHistory.commit()
966
- }
967
- catch(e){
968
- console.error(e)
969
- }
970
- })
1077
+ openComponentSelector(params, callback){
1078
+ this.$refs.webPageComponentSelector.open(params, callback)
971
1079
  },
972
1080
 
973
1081
  onHooks(model, event, items){
@@ -983,25 +1091,19 @@ export default{
983
1091
 
984
1092
  onKeyDown(e){
985
1093
 
986
- if(e.keyCode === 67 && (e.metaKey || e.ctrlKey)){
987
- this.copy()
988
- }
989
- else if(e.keyCode === 86 && (e.metaKey || e.ctrlKey)){
990
- this.paste(e)
991
- }
992
- else if(e.altKey){
1094
+ if(e.altKey){
993
1095
  if([ 49, 50, 51, 52 ].includes(e.keyCode)){
994
1096
  if(e.keyCode === 49){
995
- this.store.previewViewType = ''
1097
+ this.store.viewType = ''
996
1098
  }
997
1099
  else if(e.keyCode === 50){
998
- this.store.previewViewType = 'md:'
1100
+ this.store.viewType = 'md:'
999
1101
  }
1000
1102
  else if(e.keyCode === 51){
1001
- this.store.previewViewType = 'xl:'
1103
+ this.store.viewType = 'xl:'
1002
1104
  }
1003
1105
  else if(e.keyCode === 52){
1004
- this.store.previewViewType = '2xl:'
1106
+ this.store.viewType = '2xl:'
1005
1107
  }
1006
1108
  this.resize()
1007
1109
  }
@@ -1049,11 +1151,26 @@ export default{
1049
1151
  break
1050
1152
 
1051
1153
  case 'component-click':
1052
- const pageComp = this.findCompByUid(uid, this.page.components)
1053
- if((pageComp && !this.store.layoutMode) || (!pageComp && this.store.layoutMode)){
1154
+ let component = this.findCompByUid(uid, this.page.components)
1155
+ if(component){
1156
+ this.store.tabIndex = 2
1054
1157
  this.store.selectedComponent = [ uid ]
1158
+ return
1159
+ }
1160
+
1161
+ component = this.findCompByUid(uid, this.layout.headers)
1162
+ if(!component)
1163
+ component = this.findCompByUid(uid, this.layout.footers)
1164
+ if(component){
1165
+ this.store.tabIndex = 4
1166
+ this.store.selectedComponent = [ uid ]
1167
+ return
1055
1168
  }
1056
1169
  break
1170
+
1171
+ case 'mounted':
1172
+ this.updateIframe()
1173
+ break
1057
1174
  }
1058
1175
  },
1059
1176
 
@@ -1072,6 +1189,10 @@ export default{
1072
1189
  }
1073
1190
  },
1074
1191
 
1192
+ openRightPane2(component){
1193
+ this.extRightPane = component
1194
+ },
1195
+
1075
1196
  postIframe(data){
1076
1197
  return new Promise((resolve, reject) => {
1077
1198
  const handleResponse = (e) => {
@@ -1115,7 +1236,7 @@ export default{
1115
1236
 
1116
1237
  resize(){
1117
1238
 
1118
- const transformOrigin = this.computedPreviewViewType === '' ? 'center top' : '0 0'
1239
+ const transformOrigin = this.store.viewType === '' ? 'center top' : '0 0'
1119
1240
 
1120
1241
  switch(this.store.zoomLevel){
1121
1242
 
@@ -1125,7 +1246,7 @@ export default{
1125
1246
  const previewHeight = this.$refs.preview.clientHeight - 70
1126
1247
 
1127
1248
  let scale = 1
1128
- switch(this.computedPreviewViewType){
1249
+ switch(this.store.viewType){
1129
1250
 
1130
1251
  case 'md:':
1131
1252
  scale = (previewWidth / 1024).toFixed(2)
@@ -1209,80 +1330,16 @@ export default{
1209
1330
  },
1210
1331
 
1211
1332
  resize3(w){
1212
- if(this.store.width[1] - w >= 270){
1333
+ if(this.store.width[1] - w >= 270 && this.store.width[1] - w <= 480){
1213
1334
  this.store.width[1] -= w
1214
1335
  }
1215
1336
  },
1216
1337
 
1217
- createStyleSheet(styles){
1218
-
1219
- const mediaQueries = {
1220
- '': '@media screen',
1221
- 'md:': '@media screen and (min-width: 640px)',
1222
- }
1223
-
1224
- const fontFamilies = {
1225
- '"Anton", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Anton&display=swap');`,
1226
- 'Dosis, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Dosis:wght@400;700&display=swap');`,
1227
- 'Lato, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap');`,
1228
- 'Merriweather, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap');`,
1229
- 'Montserrat, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');`,
1230
- '"Noto Sans", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;800&display=swap');`,
1231
- 'Oswald, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&display=swap');`,
1232
- 'Poppins, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');`,
1233
- 'Raleway, sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;700&display=swap');`,
1234
- '"Roboto", sans-serif': `@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');`,
1235
- '"Roboto Slab", serif': `@import url('https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;800&display=swap');`,
1236
- '"Work Sans", serif': `@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;700&display=swap');`,
1237
- }
1238
-
1239
- let text = ''
1240
- const usedFonts = {}
1241
- for(let mediaKey in styles.media){
1242
- const css = styles.media[mediaKey]
1243
-
1244
- text += mediaQueries[mediaKey] + '{ '
1245
- for(let key in css){
1246
- text += key + '{ '
1247
- for(let selector in css[key]){
1248
- text += selector + ':' + css[key][selector] + ';'
1249
-
1250
- if(selector === 'font-family' && fontFamilies[css[key][selector]]){
1251
- usedFonts[css[key][selector]] = fontFamilies[css[key][selector]]
1252
- }
1253
- }
1254
- text += '}'
1255
- }
1256
- text += '}\n'
1257
- }
1258
-
1259
- return [
1260
- ...Object.values(usedFonts),
1261
- text
1262
- ]
1263
- .join("\n")
1264
- },
1265
-
1266
- createInstances(){
1267
- if(!this.page.instances || typeof this.page.instances !== 'object' || Array.isArray(this.page.instances))
1268
- this.page.instances = {}
1269
- this.page.instances.components = (this.page.components ?? []).map((_) => this.createComponentInstance(_)).filter(_=>_)
1270
-
1271
- if(this.layout){
1272
- if(!this.layout.instances || typeof this.layout.instances !== 'object' || Array.isArray(this.layout.instances))
1273
- this.layout.instances = {}
1274
- this.layout.instances.headers = (this.layout.headers ?? []).map((_) => this.createComponentInstance(_)).filter(_=>_)
1275
- this.layout.instances.footers = (this.layout.footers ?? []).map((_) => this.createComponentInstance(_)).filter(_=>_)
1276
-
1277
- this.layout.instances.stylesheet = this.createStyleSheet(this.layout.styles)
1278
- }
1279
- },
1280
-
1281
1338
  save(){
1282
1339
  this.createInstances()
1283
1340
 
1284
1341
  this.$refs.saveBtn.setState(2)
1285
- this.socketEmit2(this.saveSrc, { ...this.page, layout:this.layout })
1342
+ this.socket.send(`${this.controller}.save`, { ...this.page, layout:this.layout })
1286
1343
  .then((_) => {
1287
1344
  this.prevData = {
1288
1345
  page: JSON.stringify(this.page),
@@ -1290,8 +1347,8 @@ export default{
1290
1347
  }
1291
1348
  })
1292
1349
  .catch((err) => {
1350
+ console.log('ERR', err)
1293
1351
  this.toast(err)
1294
- this.load()
1295
1352
  })
1296
1353
  .finally(_ => this.$refs.saveBtn.resetState())
1297
1354
  },
@@ -1312,8 +1369,16 @@ export default{
1312
1369
  this.$refs.ldjsonModal.close()
1313
1370
  },
1314
1371
 
1372
+ savePreset: invokeAfterIdle(function() {
1373
+ if(this.presetKey) {
1374
+ this.socket.send(this.presetSrc,
1375
+ {key: this.presetKey, config: this.config})
1376
+ }
1377
+ }),
1378
+
1315
1379
  select(uid){
1316
1380
  this.store.selectedComponent = [ uid ]
1381
+ this.extRightPane = null
1317
1382
 
1318
1383
  this.$refs.iframe.contentWindow.postMessage({
1319
1384
  action: 'select',
@@ -1332,10 +1397,7 @@ export default{
1332
1397
  },
1333
1398
 
1334
1399
  stopListen(){
1335
- this.socketEmit2('page.unsubscribe', { name:'page' })
1336
- .then(() => {
1337
-
1338
- })
1400
+ this.socket.send(`${this.controller}.unsubscribe`, { name:'page' })
1339
1401
  .catch((err) => this.toast(err))
1340
1402
  },
1341
1403
 
@@ -1375,23 +1437,31 @@ export default{
1375
1437
 
1376
1438
  async uploadImage(image, extra = {}, opt = {}){
1377
1439
 
1378
- return axios({
1379
- method: this.uploadConfig.method,
1380
- url: this.uploadConfig.url,
1381
- data: createFormData({ image, ...extra }),
1382
- onUploadProgress: function (progressEvent) {
1383
- if(opt.onUploadProgress)
1384
- opt.onUploadProgress(progressEvent)
1385
- },
1386
- })
1387
- .then((res) => {
1388
- return res.data
1389
- })
1390
- .catch((err) => {
1391
- this.$refs.image.value = ''
1392
- this.$refs.imageModal.close()
1393
- this.toast(err)
1440
+ if(typeof this.uploadImageFn === 'function'){
1441
+ return this.uploadImageFn(image, extra, opt)
1442
+ }
1443
+ else if(this.uploadConfig?.method){
1444
+ return axios({
1445
+ method: this.uploadConfig.method,
1446
+ url: this.uploadConfig.url,
1447
+ data: createFormData({ image, ...extra, ...(this.uploadConfig.data ?? {}) }),
1448
+ onUploadProgress: function (progressEvent) {
1449
+ if(opt.onUploadProgress)
1450
+ opt.onUploadProgress(progressEvent)
1451
+ },
1394
1452
  })
1453
+ .then((res) => {
1454
+ return res.data
1455
+ })
1456
+ .catch((err) => {
1457
+ this.$refs.image.value = ''
1458
+ this.$refs.imageModal.close()
1459
+ this.toast(err)
1460
+ })
1461
+ }
1462
+ else{
1463
+ throw 'Unable to upload image'
1464
+ }
1395
1465
  },
1396
1466
 
1397
1467
  },
@@ -1399,13 +1469,13 @@ export default{
1399
1469
  computed: {
1400
1470
 
1401
1471
  componentStore(){
1402
- if(this.store && this.store.components){
1403
- if(!this.store.components.compsetting)
1404
- this.store.components.compsetting = {}
1472
+ if(!this.store.components)
1473
+ this.store.components = {}
1405
1474
 
1406
- return this.store.components.compsetting
1407
- }
1408
- return {}
1475
+ if(!this.store.components.compsetting)
1476
+ this.store.components.compsetting = {}
1477
+
1478
+ return this.store.components.compsetting
1409
1479
  },
1410
1480
 
1411
1481
  expanded(){
@@ -1430,14 +1500,8 @@ export default{
1430
1500
  this.prevData.layout !== JSON.stringify(this.layout))
1431
1501
  },
1432
1502
 
1433
- computedPreviewViewType(){
1434
- if(this.store.previewViewType === 'auto')
1435
- return this.store.viewType
1436
- return this.store.previewViewType
1437
- },
1438
-
1439
1503
  computedIframeSrc(){
1440
- return this.iframeSrc.substring(0, this.iframeSrc.indexOf('?'))
1504
+ return `${this.iframeSrc}`.substring(0, `${this.iframeSrc}`.indexOf('?'))
1441
1505
  },
1442
1506
 
1443
1507
  currentItem(){
@@ -1483,7 +1547,7 @@ export default{
1483
1547
  iframeSize(){
1484
1548
 
1485
1549
  let width, height
1486
- switch(this.computedPreviewViewType){
1550
+ switch(this.store.viewType){
1487
1551
  case '':
1488
1552
  width = 390
1489
1553
  height = 844
@@ -1517,24 +1581,6 @@ export default{
1517
1581
  }
1518
1582
  },
1519
1583
 
1520
- layoutHeaders(){
1521
- if(!this.layout) return []
1522
-
1523
- if(!Array.isArray(this.layout.headers))
1524
- this.layout.headers = []
1525
-
1526
- return this.layout.headers
1527
- },
1528
-
1529
- layoutFooters(){
1530
- if(!this.layout) return []
1531
-
1532
- if(!Array.isArray(this.layout.footers))
1533
- this.layout.footers = []
1534
-
1535
- return this.layout.footers
1536
- },
1537
-
1538
1584
  previewClass(){
1539
1585
  return {
1540
1586
  'overflow-auto': this.store.zoomLevel !== 'fit',
@@ -1554,125 +1600,46 @@ export default{
1554
1600
  }
1555
1601
  },
1556
1602
 
1557
- },
1558
-
1559
- data(){
1560
- return {
1561
- components: [
1562
-
1563
- {"type":"Flex","name":"3 Column Layout","group":"Layouts","thumbnailUrl":"/images/templates/3-column-layout1.png", "items":[{"type":"Flex","name":"Flex","group":"Components","items":[{"type":"Article","name":"Article","group":"Components","props":{"htmlText":"Left","padding":["p-6",""],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"name":"Dummy Text"}}],"props":{"direction":["flex-col"],"enabled":true,"width":["","md:w-2/12"],"name":"Left"}},{"type":"Flex","name":"Flex","group":"Components","items":[{"type":"Article","name":"Article","group":"Components","props":{"htmlText":"Middle","padding":["p-6",""],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"name":"Dummy Text"}}],"props":{"direction":["flex-col"],"enabled":true,"width":[null,"md:flex-1"],"name":"Middle"}},{"type":"Flex","name":"Flex","group":"Components","items":[{"type":"Article","name":"Article","group":"Components","props":{"htmlText":"Right","padding":["p-6",""],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"name":"Dummy Text"}}],"props":{"direction":["flex-col"],"enabled":true,"width":[null,"md:w-3/12"],"name":"RIght"}}],"props":{"direction":["flex-col","md:flex-row"],"gap":[null,"md:gap-4"],"enabled":true,"name":"3 Column Layout"}},
1564
-
1565
-
1566
- {"type":"Flex","name":"Thumbnails","group":"Sections","thumbnailUrl":"/images/templates/thumbnails.gif","items":[{"type":"Flex","name":"Flex","group":"Components","items":[{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Thumbnails","tagName":"h5","flex":["flex-1"]}},{"type":"Link","name":"Link","group":"Components","props":{"enabled":true,"textColor":["text-primary-500"],"direction":["flex-column"],"gap":["gap-2"],"text":"More Thumbnails"},"items":[]}],"props":{"direction":["flex-row"],"enabled":true,"flexAlign":["items-center"]}},{"type":"Flex","name":"Flex","group":"Components","items":[{"type":"Thumbnail","name":"Thumbnail","group":"Components","props":{"enabled":true,"width":["w-5/12"],"direction":["flex-column"],"gap":["gap-2"],"flex":["flex-0"]},"items":[]},{"type":"Thumbnail","name":"Thumbnail","group":"Components","props":{"enabled":true,"width":["w-5/12"],"direction":["flex-column"],"gap":["gap-2"],"flex":["flex-0"]},"items":[]},{"type":"Thumbnail","name":"Thumbnail","group":"Components","props":{"enabled":true,"width":["w-5/12"],"direction":["flex-column"],"gap":["gap-2"],"flex":["flex-0"]},"items":[]}],"props":{"direction":["flex-row"],"gap":["gap-4"],"enabled":true,"overflow":["overflow-x-scroll"],"width":["w-full"],"minWidth":["min-w-0"]}}],"props":{"direction":["flex-col"],"gap":["gap-2"],"enabled":true,"padding":["p-5",""],"name":"Thumbnails"}},
1567
-
1568
- {"type":"Carousel","name":"Image Carousel","group":"Sections","thumbnailUrl":"/images/templates/carousel1.png","items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["/assets/web/banner1.jpg"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"bdRadius":["rounded-xl"],"aspectRatio":["aspect-[2/1]"],"bgColors":["bg-amber-300"]}},{"type":"Image","name":"Image","group":"Components","props":{"src":["/assets/web/banner1.jpg"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"bdRadius":["rounded-xl"],"aspectRatio":["aspect-[2/1]"],"bgColors":["bg-lime-300"]}},{"type":"Image","name":"Image","group":"Components","props":{"src":["/assets/web/banner1.jpg"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"bdRadius":["rounded-xl"],"aspectRatio":["aspect-[2/1]"],"bgColors":["bg-blue-300"]}}],"props":{"enabled":true,"containerGap":["gap-2"],"padding":["p-4",""],"direction":["flex-column"],"gap":["gap-2"],"useLegend":true}},
1569
-
1570
- {"type":"Link","name":"Icon Link","group":"Sections","thumbnailUrl":"/images/templates/icon-link.png","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["059bae0eadf1b650f3acff7dd1e7433a.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","fontWeight":["font-normal"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Icon Link 1"}}]},
1571
-
1572
- {"type":"Carousel","name":"Icon Link Carousel","group":"Sections","thumbnailUrl":"/images/templates/icon-link-carousel.gif", "items":[{"type":"Grid","name":"Grid","group":"Components","thumbnailUrl":"/images/templates/grid1.png","items":[{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["059bae0eadf1b650f3acff7dd1e7433a.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 1","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["c7e8d12c0dcda3b9d3b2862559a8bc2e.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 2","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["47bcb782edfc426c8506664cc3b320e7.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 3","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["01e5a3e8f0e89562a7f6a5449be44614.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 4","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["7d7cc53e346aa5abf1f1ada463ed0e5c.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 5","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["4a1d1808e9f1be18f0ba18322647a11e.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 6","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["1b7f9673aeee9ebdf305658a54c63dc8.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 7","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["71828d52f682527cd500c1ba6c2a9ef0.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 8","fontSize":["text-sm"]}}]}],"gap":[],"props":{"columns":["grid-cols-4"],"gap":["gap-5"],"enabled":true,"direction":["flex-column"],"padding":["p-5",""]}},{"type":"Grid","name":"Grid","group":"Components","thumbnailUrl":"/images/templates/grid1.png","items":[{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["71828d52f682527cd500c1ba6c2a9ef0.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 9","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["059bae0eadf1b650f3acff7dd1e7433a.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 10","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["4a1d1808e9f1be18f0ba18322647a11e.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 11","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["7d7cc53e346aa5abf1f1ada463ed0e5c.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 12","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["47bcb782edfc426c8506664cc3b320e7.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 13","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["c7e8d12c0dcda3b9d3b2862559a8bc2e.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 14","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["4a1d1808e9f1be18f0ba18322647a11e.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 15","fontSize":["text-sm"]}}]},{"type":"Link","name":"Icon Link","group":"Sections","props":{"enabled":true,"display":["flex"],"direction":["flex-col"],"gap":["gap-2"],"flexAlign":["items-center"],"name":"Icon Link"},"items":[{"type":"Image","name":"Image","group":"Components","props":{"src":["059bae0eadf1b650f3acff7dd1e7433a.png"],"enabled":true,"direction":["flex-column"],"gap":["gap-2"]}},{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","enabled":true,"direction":["flex-column"],"gap":["gap-2"],"text":"Link 16","fontSize":["text-sm"]}}]}],"gap":[],"props":{"columns":["grid-cols-4"],"gap":["gap-5"],"enabled":true,"direction":["flex-column"],"padding":["p-5",""]}}],"props":{"enabled":true,"containerGap":["gap-2"],"padding":["p-4",""],"direction":["flex-column"],"gap":["gap-2"],"useLegend":true,"bgColors":["bg-gray-200"],"name":"Icon Link Carousel"}},
1573
-
1574
- { type:'ContactForm', name:'Contact Form', group:'Widgets', thumbnailUrl:"/images/templates/contact-form1.png", props:{
1575
- fields:[
1576
- { type:'name', label:'Nama', required:true },
1577
- { type:'mobileNumber', label:'Nomor HP', required:true },
1578
- { type:'remark', label:'Pertanyaan' },
1579
- ],
1580
- title: 'Contact Us',
1581
- description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent gravida erat eget nisi',
1582
- submitMethod: 'post',
1583
- submitUrl: '/inquiry',
1584
- onSubmit:[],
1585
- }},
1586
-
1587
- { type:'IconList', name:'Icon List', group:'Widgets', props:{
1588
- icons:[], columns:[ 'grid-cols-4' ],
1589
- }},
1590
-
1591
- { type:'DataList', name:'Data List', group:'Widgets', props:{}, items:[] },
1592
-
1593
- { type:'FAQ', name:'FAQ', group:'Widgets', thumbnailUrl:"/images/templates/faq.gif", props:{ items:[] }},
1594
- { type:'FAQ', name:'FAQ', group:'Widgets', thumbnailUrl:"/images/templates/faq.gif", props:{ items:[] }},
1595
-
1596
- { type:'FeatureList', name:'Feature List', group:'Widgets', props:{ items:[], columns:[], variant:['variant1'] }},
1597
-
1598
- { type:'Review', name:'Review', group:'Widgets', props:{} },
1599
-
1600
- { type:'Share', name:'Share To', group:'Widgets', props:{ channels:[] }},
1601
-
1602
- { type:'Testimonial', name:'Testimonial', group:'Widgets', props:{} },
1603
-
1604
- { type:'Header', name:'Header', group:'Widgets', props:{}, items:[] },
1605
-
1606
- { type:'ThumbnailList', name:'Thumbnail List', group:'Widgets', props:{ items:[] } },
1607
-
1608
- { type:'ProductDetail', name:'Product Detail', props:{}, group:'Widgets' },
1609
-
1610
-
1611
- {"type":"Flex","name":"Section 1","group":"Components","items":[{"type":"TextBlock","name":"TextBlock","group":"Components","props":{"htmlText":"","fontSize":["text-4xl"],"enabled":true,"text":"Drive more revenue at lower costs\n","fontWeight":["font-semibold"]}},{"type":"Paragraph","name":"Paragraph","group":"Components","props":{"name":"Paragraph","enabled":true,"text":"Reach every customer on their own preferred channel with dynamic, omnichannel campaigns, using channel-responsive templates.","html":"Reach every customer on their own preferred channel with dynamic, omnichannel campaigns, using channel-responsive templates.","maxWidth":["max-w-lg"],"fontSize":["text-xl"]}},{"type":"Flex","name":"Flex","group":"Components","items":[{"type":"Button","name":"Button","group":"Components","props":{"name":"Button","text":"Start Now","enabled":true,"padding":["px-3 py-0"],"fontSize":["text-xl"]}},{"type":"Button","name":"Button","group":"Components","props":{"name":"Button","text":"Contact Sales","enabled":true,"variant":"outline","padding":["p-3"],"fontSize":["text-xl"]}}],"props":{"direction":["flex-row"],"gap":["gap-3"],"enabled":true}}],"props":{"direction":["flex-col"],"gap":["gap-4"],"enabled":true,"padding":["p-8"],"margin":["mx-auto"],"width":["w-full"],"maxWidth":["max-w-screen-xl"]}},
1612
-
1613
- {"type":"Carousel","name":"Carousel","group":"Components","items":[],"props":{"enabled":true,"containerGap":["gap-2"],"padding":["p-4",""],"direction":["flex-column"],"gap":["gap-2"],"useLegend":true}},
1614
-
1615
- { type:'Link', name:'Link', group:'Components', props:{}, items:[] },
1616
-
1617
- { type:'Modal', name:'Modal', group:'Components', props:{}, items:[] },
1618
-
1619
- { type:'Thumbnail', name:'Thumbnail', group:'Components', props:{}, items:[] },
1620
-
1621
- { type:'Ahref', name:'Ahref', group:'Components', props:{ name:'Ahref', text:'Ahref' } },
1622
-
1623
- { type:'Paragraph', name:'Paragraph', group:'Components', thumbnailUrl:"/images/templates/paragraph1.png", props:{ name:'Paragraph' } },
1624
-
1625
- { type:'Button', name:'Button', group:'Components', props:{
1626
- name:'Button', text:'Button'
1627
- }},
1628
-
1629
- { type:'Flex', name:'Flex', group:'Components', items:[], props:{ direction: [ 'flex-row' ] }},
1630
-
1631
- { type:'Grid', name:'Grid', group:'Components', thumbnailUrl:"/images/templates/grid1.png", items:[], gap:[], props:{
1632
- columns: [], gap: [],
1633
- }},
1634
-
1635
- { type:'Image', name:'Image', group:'Components', props:{
1636
- src:[],
1637
- }},
1638
-
1639
- { type:'EmbeddedVideo', name:'Video', group:'Components', props:{
1640
- src:[],
1641
- }},
1642
-
1643
- { type:'Article', name:'Article', group:'Components', props:{
1644
- htmlText:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed convallis odio at odio dapibus, " +
1645
- "eget molestie risus semper. Aenean magna tellus, aliquet nec tristique eget, lacinia nec urna.",
1646
- }},
1647
-
1648
- { type:'Table', name:'Table', group:'Components', props:{
1649
- items: []
1650
- }},
1651
-
1652
- { type:'TextBlock', name:'TextBlock', group:'Components', props:{
1653
- htmlText:'',
1654
- fontFamily:[], fontSize:[], fontWeight:[], textColor:[]
1655
- }},
1656
-
1657
- { type:'Block', name:'Block', group:'Components', items:[], props:{}},
1603
+ store(){
1604
+ return this.config.params
1605
+ },
1658
1606
 
1659
- { type:'Countdown', name:'Countdown', group:'Components', props:{}},
1607
+ presetSrc(){
1608
+ return this.controller ?
1609
+ `${this.controller}.preset` :
1610
+ 'user.preset'
1611
+ },
1660
1612
 
1661
- { type:'YoutubeVideo', name:'Youtube Video', group:'Components', props:{ name:"Youtube Video", delay:"0" }},
1613
+ viewIndex(){
1614
+ return this.viewTypes.findIndex(_ => _.value === this.store.viewType)
1615
+ }
1662
1616
 
1663
- { type:'Svg', name:'Svg', group:'Components', props:{}},
1617
+ },
1664
1618
 
1665
- ],
1619
+ data(){
1620
+ return {
1621
+ components: defaultConfig.components,
1622
+ config: {
1623
+ params: {
1624
+ version: '0.0.999',
1625
+ layoutMode: false,
1626
+ selectedComponent: null, // [ 'uid|style', 'style|headers|components|footers' ]
1627
+ tabIndex: 2,
1628
+ viewType: '',
1629
+ zoomLevel: 'fit',
1630
+ width: [ 320, 320 ],
1631
+ previewMode: 1,
1632
+ }
1633
+ },
1666
1634
 
1667
- currentArea: null,
1668
1635
  currentComponentItems: null,
1669
1636
  compClasses: [
1670
1637
  'aspectRatio', 'position', 'left', 'top', 'right', 'bottom',
1671
1638
 
1672
- 'bdSize', 'bdColor', 'bdRadius', 'bdStyle',
1639
+ 'bdSize', 'bdRadius', 'bdStyle',
1673
1640
  'divideSize', 'divideColor', 'divideStyle',
1674
1641
  'outlineWidth', 'outlineColor', 'outlineStyle',
1675
- 'bgColors', 'bgSize', 'bgPosition', 'bgRepeat',
1642
+ 'bgSize', 'bgPosition', 'bgRepeat',
1676
1643
  'textAlign', 'verticalAlign',
1677
1644
  'gap',
1678
1645
  'padding', 'margin',
@@ -1700,7 +1667,11 @@ export default{
1700
1667
  itemClasses: [
1701
1668
  'itemMinWidth', 'itemRatio', 'itemVariant'
1702
1669
  ],
1670
+ styleClasses: [
1671
+ 'bgColors', 'bgImages', 'bdColor'
1672
+ ],
1703
1673
  host: null,
1674
+ previewHost: null,
1704
1675
  iframeStyle: {},
1705
1676
  iframeSrc: '',
1706
1677
  layouts: [],
@@ -1709,38 +1680,39 @@ export default{
1709
1680
  { text:"Design", value:1 },
1710
1681
  { text:"Preview", value:2 },
1711
1682
  ],
1712
- previewViewTypes: [
1683
+ viewTypes: [
1713
1684
  { text:'Mobile', value:'' },
1714
1685
  { text:'Tablet', value:'md:' },
1715
- { text:'Desktop', value:'xl:' },
1716
- { text:'TV', value:'2xl:' },
1686
+ { text:'Desktop', value:'xl:' }
1717
1687
  ],
1718
1688
  routerBeforeEach: null,
1719
- state: 2,
1689
+ readyState: 2,
1720
1690
  tabItems: [
1721
1691
  { text:"Page Info", value:1 },
1692
+ { text:"Layout", value:4 },
1722
1693
  { text:"Components", value:2 },
1723
- { text:"Datasource", value:3 },
1724
- ],
1725
- viewTypes: [
1726
- { text:'Mobile', value:'' },
1727
- { text:'Tablet', value:'md:' },
1728
- /*{ text:'Desktop', value:'xl:' },
1729
- { text:'TV', value:'2xl:' },*/
1694
+ /*{ text:"Datasource", value:3 },*/
1730
1695
  ],
1731
1696
  stylesheets: [
1732
1697
  'font-family'
1733
1698
  ],
1734
1699
  updating: true,
1700
+
1701
+ extRightPane: null,
1702
+ debugMode: false,
1735
1703
  }
1736
1704
  },
1737
1705
 
1738
1706
  emits: [ 'close', 'unmount' ],
1739
1707
 
1740
- inject: [ 'alert', 'confirm', 'socket', 'socketEmit2', 'toast' ],
1708
+ inject: [ 'alert', 'appStyle', 'confirm', 'socket', 'toast' ],
1741
1709
 
1742
1710
  mounted() {
1743
- this.load()
1711
+ this.loadPreset()
1712
+ .then(_ => {
1713
+ this.loadLayouts()
1714
+ this.load()
1715
+ })
1744
1716
 
1745
1717
  window.addEventListener('message', this.onMessage)
1746
1718
  window.addEventListener('resize', this.onResize)
@@ -1780,11 +1752,23 @@ export default{
1780
1752
  getPage: this.getPage,
1781
1753
  uploadImage: this.uploadImage,
1782
1754
  openComponentSelector: this.openComponentSelector,
1755
+ setUid: this.setUid,
1756
+ openRightPane2: this.openRightPane2,
1757
+ getRightPane: () => this.$refs.rightPane,
1758
+ pageHistory: this.pageHistory,
1759
+ duplicate: this.duplicate
1783
1760
  }
1784
1761
  },
1785
1762
 
1786
1763
  watch: {
1787
1764
 
1765
+ config: {
1766
+ deep: true,
1767
+ handler(){
1768
+ this.savePreset()
1769
+ }
1770
+ },
1771
+
1788
1772
  currentItem(){
1789
1773
  this.$nextTick(() => this.resize())
1790
1774
  },
@@ -1793,6 +1777,11 @@ export default{
1793
1777
  if(to === 'fit'){
1794
1778
  this.$refs.preview.scrollTop = 0
1795
1779
  }
1780
+ this.resize()
1781
+ },
1782
+
1783
+ "store.viewType"(to){
1784
+ this.resize()
1796
1785
  },
1797
1786
 
1798
1787
  page: {
@@ -1820,20 +1809,20 @@ export default{
1820
1809
  .comp{
1821
1810
  @apply flex-1;
1822
1811
  @apply hidden md:flex flex-row;
1823
- @apply divide-x divide-text-50;
1812
+ @apply divide-x divide-border-50;
1824
1813
  }
1825
1814
 
1826
1815
  .resize1{
1827
- @apply w-[3px] cursor-ew-resize;
1816
+ @apply w-[3px] cursor-ew-resize border-l-[1px] border-border-50;
1828
1817
  }
1829
1818
 
1830
1819
  .resize3{
1831
- @apply w-[3px] cursor-ew-resize;
1820
+ @apply w-[3px] cursor-ew-resize border-r-[1px] border-border-50;
1832
1821
  }
1833
1822
 
1834
1823
  .zoomLevel{
1835
1824
  @apply appearance-none bg-base-400 text-center outline-none h-[24px] rounded-md;
1836
- @apply border-[1px] border-text-100;
1825
+ @apply border-[1px] border-border-200;
1837
1826
  }
1838
1827
 
1839
1828
  </style>