comand-component-library 3.3.84 → 3.3.85

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/dist/comand-component-library.js +5630 -3936
  2. package/dist/comand-component-library.umd.cjs +4 -4
  3. package/dist/style.css +1 -1
  4. package/package.json +2 -2
  5. package/src/App.vue +215 -112
  6. package/src/assets/data/component-structure.json +228 -0
  7. package/src/assets/data/form-elements.json +156 -0
  8. package/src/assets/data/opening-hours.json +79 -37
  9. package/src/components/CmdAddressData.vue +187 -201
  10. package/src/components/CmdAddressDataItem.vue +306 -0
  11. package/src/components/CmdBox.vue +1 -0
  12. package/src/components/CmdBoxWrapper.vue +53 -5
  13. package/src/components/CmdFancyBox.vue +31 -4
  14. package/src/components/CmdForm.vue +98 -4
  15. package/src/components/CmdFormElement.vue +5 -1
  16. package/src/components/CmdHeadline.vue +179 -52
  17. package/src/components/CmdImage.vue +150 -21
  18. package/src/components/CmdImageGallery.vue +88 -85
  19. package/src/components/CmdListOfLinks.vue +63 -43
  20. package/src/components/CmdListOfLinksItem.vue +97 -0
  21. package/src/components/CmdLoginForm.vue +3 -6
  22. package/src/components/CmdMultistepFormProgressBar.vue +37 -8
  23. package/src/components/CmdOpeningHours.vue +280 -63
  24. package/src/components/CmdOpeningHoursItem.vue +264 -0
  25. package/src/components/{CmdPager.vue → CmdPagination.vue} +2 -2
  26. package/src/components/CmdSlideshow.vue +137 -10
  27. package/src/components/CmdSocialNetworks.vue +115 -28
  28. package/src/components/CmdSocialNetworksItem.vue +184 -0
  29. package/src/components/CmdTable.vue +0 -1
  30. package/src/components/CmdTextImageBlock.vue +158 -0
  31. package/src/components/CmdThumbnailScroller.vue +132 -12
  32. package/src/components/CmdToggleDarkMode.vue +58 -1
  33. package/src/components/EditComponentWrapper.vue +553 -0
  34. package/src/index.js +2 -2
  35. package/src/mixins/EditMode.vue +28 -9
  36. package/src/utils/editmode.js +30 -5
  37. package/src/components/CmdTextBlock.vue +0 -73
  38. package/src/editmode/editModeContext.js +0 -50
@@ -1,7 +1,10 @@
1
1
  <template>
2
- <div :class="['cmd-social-networks', {'stretch': stretchButtons, 'align-right': align === 'right'}]">
2
+ <div :class="['cmd-social-networks', {'stretch': stretchButtons}, alignment]">
3
3
  <!-- begin CmdHeadline -->
4
- <CmdHeadline v-if="cmdHeadline" v-bind="cmdHeadline"/>
4
+ <CmdHeadline
5
+ v-if="cmdHeadline?.headlineText || editModeContext?.editing"
6
+ v-bind="cmdHeadline || {}"
7
+ />
5
8
  <!-- end CmdHeadline -->
6
9
 
7
10
  <!-- begin CmdFormElement -->
@@ -17,34 +20,49 @@
17
20
  <!-- end CmdFormElement -->
18
21
 
19
22
  <!-- begin list of networks -->
20
- <ul :class="['button-wrapper no-flex', {'no-gap': !useGap}]">
21
- <li v-for="network in validNetworks">
22
- <a
23
- :key="network.path"
24
- :class="['button', {disabled: userMustAcceptDataPrivacy && !dataPrivacyAccepted}, {'text-align-left': textAlign === 'left'}]"
25
- :id="network.id"
26
- :href="getUrl(network)"
27
- @click="preventOnDisabled"
28
- target="_blank"
29
- :title="tooltip(network.tooltip)">
30
- <!-- begin CmdIcon -->
31
- <CmdIcon
32
- v-if="network.iconClass"
33
- :iconClass="network.iconClass"
34
- :type="network.iconType"
23
+ <ul v-if="validNetworks.length > 0" :class="['button-wrapper', {'no-gap': !useGap}]">
24
+ <CmdSocialNetworksItem
25
+ v-if="!editModeContext"
26
+ v-for="(entry, index) in validNetworks"
27
+ :key="index"
28
+ :network="entry"
35
29
  />
36
- <!-- end CmdIcon -->
37
- <span v-if="network.linkText">{{ network.linkText }}</span>
38
- </a>
39
- </li>
30
+
31
+ <!-- begin edit-mode -->
32
+ <EditComponentWrapper
33
+ v-else
34
+ v-for="(entry, index) in validNetworks"
35
+ :key="'x' + index"
36
+ class="edit-items"
37
+ componentName="CmdSocialNetworksItem"
38
+ :componentProps="entry"
39
+ :componentPath="['props', 'networks', index]"
40
+ :showComponentName="false"
41
+ >
42
+ <CmdSocialNetworksItem
43
+ :network="entry"
44
+ :userMustAcceptDataPrivacy="userMustAcceptDataPrivacy"
45
+ :buttonTextAlign="buttonTextAlign"
46
+ :dataPrivacyAccepted="dataPrivacyAccepted"
47
+ />
48
+ </EditComponentWrapper>
49
+ <!-- end edit-mode -->
40
50
  </ul>
51
+
52
+ <button v-else class="button small" title="Add new item" @click="onAddItem">
53
+ <span class="icon-plus"></span>
54
+ </button>
41
55
  <!-- end list of networks -->
42
56
  </div>
43
57
  </template>
44
58
 
45
59
  <script>
60
+ import {buildComponentPath} from "../utils/editmode.js"
61
+ import EditMode from "../mixins/EditMode.vue"
62
+
46
63
  export default {
47
64
  name: "CmdSocialNetworks",
65
+ mixins: [EditMode],
48
66
  data() {
49
67
  return {
50
68
  dataPrivacyAccepted: false
@@ -61,11 +79,11 @@ export default {
61
79
  /**
62
80
  * set horizontal alignment
63
81
  *
64
- * @allowedValues: left, right
82
+ * @allowedValues: left, center, right
65
83
  */
66
84
  align: {
67
85
  type: String,
68
- required: false
86
+ default: "left"
69
87
  },
70
88
  /**
71
89
  * activate if gap between buttons should appear
@@ -121,11 +139,13 @@ export default {
121
139
  default: "You must accept data privacy conditions!"
122
140
  },
123
141
  /**
124
- * text-alignment for buttons
142
+ * alignment for buttons
143
+ *
144
+ * @allowedValues: "left", "right"
125
145
  */
126
- textAlign: {
146
+ buttonTextAlign: {
127
147
  type: String,
128
- default: "right"
148
+ default: "left"
129
149
  },
130
150
  /**
131
151
  * properties for cmdFormElement
@@ -153,6 +173,9 @@ export default {
153
173
  computed: {
154
174
  validNetworks() {
155
175
  return this.networks.filter(item => item.path)
176
+ },
177
+ alignment() {
178
+ return "align-" + this.align
156
179
  }
157
180
  },
158
181
  methods: {
@@ -174,6 +197,20 @@ export default {
174
197
  }
175
198
  return "#"
176
199
  },
200
+ onAddItem() {
201
+ this.editModeContext.content.addContent(
202
+ buildComponentPath(this, 'props', 'networks', -1),
203
+ this.itemProvider)
204
+ },
205
+ itemProvider() {
206
+ return {
207
+ "id": "social-network-facebook",
208
+ "path": "https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fdevelopment.comand-cms.com%2Fmodule%2Fteam.html",
209
+ "tooltip": "Share this page on facebook",
210
+ "iconClass": "icon-facebook",
211
+ "linkText": "Share"
212
+ }
213
+ },
177
214
  preventOnDisabled(event) {
178
215
  let clickedElement = event.target
179
216
 
@@ -195,6 +232,21 @@ export default {
195
232
  return this.tooltipAcceptDataPrivacy
196
233
  }
197
234
  return tooltip
235
+ },
236
+ onPersist(data) {
237
+ return {
238
+ editModeContextData: {
239
+ ...(this.editModeContextData || {})
240
+ },
241
+ update(props) {
242
+ props.cmdHeadline = {
243
+ ...(props.cmdHeadline || {}),
244
+ }
245
+ if (Array.isArray(data) && data.length > 0) {
246
+ props.cmdHeadline.headlineText = data[0].headlineText
247
+ }
248
+ }
249
+ }
198
250
  }
199
251
  }
200
252
  }
@@ -216,6 +268,7 @@ export default {
216
268
 
217
269
  .button-wrapper {
218
270
  flex-direction: row;
271
+ flex: none;
219
272
  margin: 0;
220
273
  gap: calc(var(--default-gap) / 2);
221
274
 
@@ -232,7 +285,7 @@ export default {
232
285
  margin: 0;
233
286
  }
234
287
 
235
- &.text-align-left {
288
+ &.text-align-right {
236
289
  flex-direction: row-reverse;
237
290
  }
238
291
  }
@@ -271,9 +324,43 @@ export default {
271
324
  }
272
325
  }
273
326
 
327
+ &.align-center {
328
+ .cmd-headline > * {
329
+ text-align: center;
330
+ }
331
+
332
+ .toggle-switch {
333
+ margin: auto;
334
+ }
335
+
336
+ .share-button-wrapper {
337
+ justify-content: center;
338
+ }
339
+ }
340
+
341
+ &.align-right {
342
+ .cmd-headline > * {
343
+ text-align: right;
344
+ }
345
+
346
+ .toggle-switch {
347
+ margin-left: auto;
348
+ }
349
+
350
+ .button-wrapper {
351
+ justify-content: flex-end;
352
+ }
353
+ }
354
+
274
355
  &.stretch {
356
+ .button-wrapper {
357
+ li {
358
+ flex: 1;
359
+
275
360
  .button {
276
- flex: 1;
361
+ display: flex;
362
+ }
363
+ }
277
364
  }
278
365
  }
279
366
 
@@ -0,0 +1,184 @@
1
+ <template>
2
+ <li class="cmd-social-networks-item">
3
+ <a
4
+ :key="network.path"
5
+ :class="['button', {disabled: userMustAcceptDataPrivacy && !dataPrivacyAccepted}, {'text-align-right': buttonTextAlign === 'right'}]"
6
+ :id="network.id"
7
+ :href="getUrl(network)"
8
+ @click="preventOnDisabled"
9
+ target="_blank"
10
+ :title="tooltip(network.tooltip)">
11
+
12
+ <!-- begin CmdIcon -->
13
+ <CmdIcon
14
+ v-if="network.iconClass"
15
+ :iconClass="network.iconClass"
16
+ :type="network.iconType"
17
+ />
18
+ <!-- end CmdIcon -->
19
+
20
+ <span v-if="network.linkText">{{ network.linkText }}</span>
21
+ </a>
22
+ </li>
23
+ </template>
24
+
25
+ <script>
26
+ import {createUuid} from "../utils/common.js"
27
+ import {updateHandlerProvider} from "../utils/editmode.js"
28
+ import EditMode from "../mixins/EditMode.vue"
29
+
30
+ export default {
31
+ name: "CmdSocialNetworksItem",
32
+ data() {
33
+ return {
34
+ editablePath: null,
35
+ editableTooltip: null,
36
+ editableLinktext: null
37
+ }
38
+ },
39
+ mixins: [EditMode],
40
+ props: {
41
+ network: {
42
+ type: Object,
43
+ required: true
44
+ },
45
+ /**
46
+ * toggle if user has to accept that anonymous data will be send while sharing
47
+ */
48
+ userMustAcceptDataPrivacy: {
49
+ type: Boolean,
50
+ default: true
51
+ },
52
+ /**
53
+ * alignment for buttons
54
+ *
55
+ * @allowedValues: "left", "right"
56
+ */
57
+ buttonTextAlign: {
58
+ type: String,
59
+ default: "left"
60
+ },
61
+ /**
62
+ * information if the user has accepted the data privacy (by checking the checkbox)
63
+ */
64
+ dataPrivacyAccepted: {
65
+ type: Boolean
66
+ },
67
+ /**
68
+ * tooltip shown on hovering disabled buttons
69
+ *
70
+ * userMustAcceptDataPrivacy-property must be activated
71
+ */
72
+ tooltipAcceptDataPrivacy: {
73
+ type: String,
74
+ default: "You must accept data privacy conditions!"
75
+ }
76
+ },
77
+ methods: {
78
+ getUrl(network) {
79
+ if (this.userMustAcceptDataPrivacy && this.dataPrivacyAccepted) {
80
+ // if path is not given completely by json-data
81
+ if (this.appendPage) {
82
+ // if page to share is given by property
83
+ if (this.page) {
84
+ return network.path + encodeURIComponent(this.page)
85
+ }
86
+
87
+ // if current page should be appended to url
88
+ return network.path + encodeURIComponent(location.href)
89
+ }
90
+
91
+ // if path is given completely by json-data
92
+ return network.path
93
+ }
94
+ return "#"
95
+ },
96
+ preventOnDisabled(event) {
97
+ let clickedElement = event.target
98
+
99
+ if (clickedElement.tagName !== "A") {
100
+ // get surrounding <a> if inner <span> is clicked
101
+ clickedElement = clickedElement.closest("a")
102
+ }
103
+
104
+ // href must be set due to html-validity, so click must be prevented if href contains "#" only (equals button is styled as disabled)
105
+ if (clickedElement.getAttribute("href") === "#") {
106
+ event.preventDefault()
107
+ }
108
+ },
109
+ tooltip(tooltip) {
110
+ if (this.userMustAcceptDataPrivacy) {
111
+ if (this.dataPrivacyAccepted) {
112
+ return tooltip
113
+ }
114
+ return this.tooltipAcceptDataPrivacy
115
+ }
116
+ return tooltip
117
+ },
118
+ addHandlerProvider() {
119
+ const itemStructure = {
120
+ "id": "social-network-facebook",
121
+ "path": "https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fdevelopment.comand-cms.com%2Fmodule%2Fteam.html",
122
+ "tooltip": "Share this page on facebook",
123
+ "iconClass": "icon-facebook",
124
+ "linkText": "Share"
125
+ }
126
+ return updateHandlerProvider(this, {
127
+ item() {
128
+ return itemStructure
129
+ }
130
+ })
131
+ },
132
+ updateHandlerProvider() {
133
+ const id = createUuid()
134
+ const path = this.editablePath
135
+ const tooltip = this.editableTooltip
136
+ const linkText = this.editableLinktext
137
+ return updateHandlerProvider(this, {
138
+ update(props) {
139
+ props.id = id
140
+ props.path = path
141
+ props.tooltip = tooltip
142
+ props.linkText = linkText
143
+ }
144
+ })
145
+ }
146
+ }
147
+ }
148
+ </script>
149
+
150
+ <style lang="scss">
151
+
152
+ [id^="social-network"] {
153
+ background: var(--social-network-color);
154
+ border-color: var(--social-network-color);
155
+
156
+ > span {
157
+ color: var(--pure-white);
158
+ }
159
+
160
+ &:hover, &:active, &:focus {
161
+ color: var(--pure-white);
162
+
163
+ > span {
164
+ color: var(--social-network-color);
165
+ }
166
+ }
167
+ }
168
+
169
+ #social-network-facebook {
170
+ --social-network-color: #3c5a99;
171
+ }
172
+
173
+ #social-network-twitter {
174
+ --social-network-color: #6bacde;
175
+ }
176
+
177
+ #social-network-xing {
178
+ --social-network-color: #007575;
179
+ }
180
+
181
+ #social-network-linkedin {
182
+ --social-network-color: #0077b5;
183
+ }
184
+ </style>
@@ -255,7 +255,6 @@ export default {
255
255
  overflow-x: auto;
256
256
  width: 100%;
257
257
  padding: 0 4rem;
258
- border: 1px solid red;
259
258
 
260
259
  .cmd-slide-button {
261
260
  left: 0;
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <div class="cmd-text-image-block flex-container vertical">
3
+ <!-- begin cmdHeadline -->
4
+ <CmdHeadline
5
+ v-if="(cmdHeadline?.headlineText || editModeContext) && headlinePosition === 'aboveImage'"
6
+ v-bind="cmdHeadline"
7
+ />
8
+ <!-- end cmdHeadline -->
9
+
10
+ <!-- begin cmdImage -->
11
+ <CmdImage
12
+ v-if="cmdImage"
13
+ :image="cmdImage?.image"
14
+ :figcaption="cmdImage?.figcaption"
15
+ :editModeConfig="{allowAddItem: false}"
16
+ />
17
+ <!-- end cmdImage -->
18
+
19
+ <!-- begin cmdHeadline -->
20
+ <CmdHeadline
21
+ v-if="(cmdHeadline?.headlineText || editModeContext) && headlinePosition === 'belowImage'"
22
+ v-bind="cmdHeadline"
23
+ />
24
+ <!-- end cmdHeadline -->
25
+
26
+ <!-- begin continuous text -->
27
+ <!-- begin edit-mode -->
28
+ <EditComponentWrapper
29
+ v-if="editModeContext"
30
+ ref="editComponentWrapper"
31
+ class="edit-items"
32
+ :showComponentName="false"
33
+ :allowedComponentTypes="[]"
34
+ :componentProps="{htmlContent, textAlign}"
35
+ :componentPath="paragraphComponentPath"
36
+ :allowDeleteComponent="!!htmlContent"
37
+ >
38
+ <template v-slot="slotProps">
39
+ <textarea v-if="slotProps.editing" :class="['edit-mode', textAlign]" v-model="editableHtmlContent" placeholder="Paragraph"></textarea>
40
+ <div v-else-if="htmlContent" v-html="htmlContent" :class="textAlign"></div>
41
+ <!-- begin show placeholder if no image exists (and component is not edited) -->
42
+ <button v-else type="button" class="button confirm" @click="onAddItem">
43
+ <span class="icon-plus"></span>
44
+ <span>Add new paragraph</span>
45
+ </button>
46
+ <!-- end show placeholder if no image exists (and component is not edited) -->
47
+ </template>
48
+ </EditComponentWrapper>
49
+ <!-- end edit-mode -->
50
+
51
+ <div v-else-if="htmlContent" v-html="htmlContent" :class="textAlign"></div>
52
+ <!-- end continuous text -->
53
+ </div>
54
+ </template>
55
+
56
+ <script>
57
+ import EditMode from "../mixins/EditMode.vue"
58
+ import {updateHandlerProvider} from "../utils/editmode.js"
59
+
60
+ export default {
61
+ name: "CmdTextImageBlock",
62
+ mixins: [EditMode],
63
+ data() {
64
+ return {
65
+ editableHtmlContent: null
66
+ }
67
+ },
68
+ props: {
69
+ editModeContextData: {
70
+ type: Object
71
+ },
72
+ /**
73
+ * content for continuous text (can contain html-tags)
74
+ */
75
+ htmlContent: {
76
+ type: String,
77
+ required: true
78
+ },
79
+ /**
80
+ * text-alignment for paragraph/continuous text
81
+ */
82
+ paragraphTextAlign: {
83
+ type: String,
84
+ required: false
85
+ },
86
+ /**
87
+ * position for headline
88
+ *
89
+ * @allowedValues: "aboveImage", "belowImage"
90
+ */
91
+ headlinePosition: {
92
+ type: String,
93
+ default: "aboveImage"
94
+ },
95
+ /**
96
+ * properties for CmdHeadline-component
97
+ */
98
+ cmdHeadline: {
99
+ type: Object,
100
+ required: false
101
+ },
102
+ /**
103
+ * property for CmdImage-component
104
+ */
105
+ cmdImage: {
106
+ type: Object,
107
+ required: false
108
+ }
109
+ },
110
+ computed: {
111
+ paragraphComponentPath() {
112
+ /* because paragraph is no component on its own, besides the componentPath (for the property),
113
+ an additional flag (here $isComponent-key) is used to let the store identify this edge case.
114
+ the '$'-prefix is set to ensure that the flag is not a generic name, that potentially is used as a property-name in any component */
115
+ return [{componentPath: ["props", "htmlContent"], $isComponent: false}]
116
+ },
117
+ textAlign() {
118
+ if (this.paragraphTextAlign) {
119
+ return "text-align-" + this.paragraphTextAlign
120
+ }
121
+ return ""
122
+ }
123
+ },
124
+ methods: {
125
+ onAddItem() {
126
+ // execute editComponent-function from editComponentWrapper to enter editMode directly on "add"
127
+ this.$refs.editComponentWrapper.editComponent()
128
+ },
129
+ addHandlerProvider() {
130
+ return ""
131
+ },
132
+ updateHandlerProvider() {
133
+ const htmlContent = this.editableHtmlContent
134
+ this.editableHtmlContent = null // reset data-property
135
+
136
+ return updateHandlerProvider(this, {
137
+ update(props) {
138
+ props.htmlContent = htmlContent
139
+ }
140
+ })
141
+ }
142
+ },
143
+ watch: {
144
+ htmlContent: {
145
+ handler() {
146
+ this.editableHtmlContent = this.htmlContent
147
+ },
148
+ immediate: true
149
+ }
150
+ }
151
+ }
152
+ </script>
153
+
154
+ <style lang="scss">
155
+ .edit-mode .cmd-text-image-block textarea {
156
+ width: 100%;
157
+ }
158
+ </style>