comand-component-library 3.3.84 → 3.3.86

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 (38) hide show
  1. package/dist/comand-component-library.js +5623 -3929
  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 +205 -76
  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>