comand-component-library 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/comand-component-library.js +1632 -1565
- package/dist/comand-component-library.umd.cjs +4 -4
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/{App.vue → ComponentLibrary.vue} +875 -413
- package/src/assets/data/main-navigation.json +2 -0
- package/src/assets/styles/global-styles.scss +1 -10
- package/src/components/CmdAddressData.vue +1 -1
- package/src/components/CmdBox.vue +40 -4
- package/src/components/CmdBoxWrapper.vue +15 -5
- package/src/components/CmdFormElement.vue +37 -33
- package/src/components/CmdHeadline.vue +97 -33
- package/src/components/CmdInputGroup.vue +6 -5
- package/src/components/CmdListOfLinks.vue +24 -24
- package/src/components/CmdLoginForm.vue +1 -0
- package/src/components/CmdMainNavigation.vue +20 -9
- package/src/components/CmdMultistepFormProgressBar.vue +43 -1
- package/src/components/CmdNewsletterSubscription.vue +7 -7
- package/src/components/CmdOpeningHours.vue +33 -18
- package/src/components/CmdOpeningHoursItem.vue +21 -13
- package/src/components/CmdPagination.vue +2 -2
- package/src/components/CmdSidebar.vue +6 -1
- package/src/components/CmdSiteHeader.vue +3 -0
- package/src/components/CmdSiteSearch.vue +12 -11
- package/src/components/CmdSystemMessage.vue +1 -0
- package/src/components/CmdTable.vue +9 -1
- package/src/components/CmdTextImageBlock.vue +2 -0
- package/src/components/CmdThumbnailScroller.vue +52 -32
- package/src/components/CmdTooltip.vue +5 -0
- package/src/components/CmdUploadForm.vue +67 -41
- package/src/components/ComponentSettings.vue +171 -0
- package/src/main.js +3 -3
@@ -24,22 +24,24 @@
|
|
24
24
|
</CmdSystemMessage>
|
25
25
|
<!-- end CmdSystemMessage -->
|
26
26
|
|
27
|
-
<div :class="['box
|
27
|
+
<div :class="['box flex-container vertical', { 'drop-area': enableDragAndDrop, 'allow-drop': allowDrop }]" v-on="dragAndDropHandler">
|
28
28
|
<template v-if="!listOfFiles.length">
|
29
|
+
<!-- begin CmdHeadline -->
|
29
30
|
<CmdHeadline v-if="allowMultipleFileUploads" v-bind="cmdHeadlineNoFilesToUpload" headlineLevel="4">
|
30
|
-
{{
|
31
|
-
</CmdHeadline>
|
32
|
-
<CmdHeadline v-else v-bind="cmdHeadlineNoFilesToUpload" headlineLevel="4">
|
33
|
-
{{ getMessage("cmduploadform.no_file_to_upload") }}
|
31
|
+
{{ headlineTextNoFilesToUpload }}
|
34
32
|
</CmdHeadline>
|
33
|
+
<!-- end CmdHeadline -->
|
35
34
|
</template>
|
36
35
|
|
37
36
|
<!-- begin total-upload information -->
|
38
37
|
<div v-else class="flex-container vertical">
|
39
38
|
<div v-if="showTotalUpload && listOfFiles.length !== 1" class="flex-container vertical list-files-wrapper">
|
39
|
+
<!-- begin CmdHeadline -->
|
40
40
|
<CmdHeadline v-bind="cmdHeadlineSummaryOfAllFiles" headlineLevel="4">
|
41
41
|
{{ getMessage("cmduploadform.headline.summary_of_all_files") }}
|
42
42
|
</CmdHeadline>
|
43
|
+
<!-- end CmdHeadline -->
|
44
|
+
|
43
45
|
<ul v-if="showTotalUpload && listOfFiles.length !== 1" class="list-of-files total-files">
|
44
46
|
<li class="flex-container no-flex">
|
45
47
|
<a
|
@@ -83,9 +85,12 @@
|
|
83
85
|
|
84
86
|
<div class="flex-container vertical list-files-wrapper">
|
85
87
|
<!-- begin list of selected files -->
|
88
|
+
<!-- begin CmdHeadline -->
|
86
89
|
<CmdHeadline v-bind="cmdHeadlineListOfSelectedFiles" headlineLevel="4">
|
87
90
|
{{ getMessage("cmduploadform.headline.list_of_selected_files") }}
|
88
91
|
</CmdHeadline>
|
92
|
+
<!-- end CmdHeadline -->
|
93
|
+
|
89
94
|
<ul class="list-of-files">
|
90
95
|
<li
|
91
96
|
v-for="(uploadFile, index) in listOfFiles"
|
@@ -108,20 +113,21 @@
|
|
108
113
|
{ error: uploadFile.error }
|
109
114
|
]">
|
110
115
|
{{ uploadFile.file.name }} <small>({{ formatSize(uploadFile.file.size) }})</small>
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
<span
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
</
|
116
|
+
</span>
|
117
|
+
|
118
|
+
<!-- begin progressbar -->
|
119
|
+
<span class="progressbar" v-if="uploadInitiated && !uploadFile.error">
|
120
|
+
<span>{{ getPercentage(uploadFile.progress) }}</span>
|
121
|
+
<!-- do not place inside progress-tag (will not be displayed then) -->
|
122
|
+
<progress
|
123
|
+
max="100"
|
124
|
+
:value="uploadFile.progress"
|
125
|
+
:title="
|
126
|
+
formatSize(uploadFile.uploadedBytes) + '/' + formatSize(uploadFile.file.size)
|
127
|
+
"
|
128
|
+
></progress>
|
129
|
+
</span>
|
130
|
+
<!-- end progressbar -->
|
125
131
|
</li>
|
126
132
|
</ul>
|
127
133
|
<a
|
@@ -137,12 +143,18 @@
|
|
137
143
|
<!-- end list of selected files -->
|
138
144
|
|
139
145
|
<!-- begin upload conditions -->
|
146
|
+
<!-- begin CmdHeadline -->
|
140
147
|
<CmdHeadline v-if="allowMultipleFileUploads && listOfFiles.length" v-bind="cmdHeadlineSelectAdditionalFiles" headlineLevel="4">
|
141
148
|
{{ getMessage("cmduploadform.headline.select_additional_files") }}
|
142
149
|
</CmdHeadline>
|
150
|
+
<!-- end CmdHeadline -->
|
151
|
+
|
152
|
+
<!-- begin CmdHeadline -->
|
143
153
|
<CmdHeadline v-if="!allowMultipleFileUploads && listOfFiles.length" v-bind="cmdHeadlineSelectNewFile" headlineLevel="4">
|
144
154
|
{{ getMessage("cmduploadform.headline.select_new_file") }}
|
145
155
|
</CmdHeadline>
|
156
|
+
<!-- end CmdHeadline -->
|
157
|
+
|
146
158
|
<dl class="small">
|
147
159
|
<template v-if="maxTotalUploadSize > 0">
|
148
160
|
<dt :class="{ error: totalSize > maxTotalUploadSize }">
|
@@ -188,7 +200,8 @@
|
|
188
200
|
</dl>
|
189
201
|
<!-- end upload conditions -->
|
190
202
|
|
191
|
-
|
203
|
+
<!-- begin upload-button and drag-and-drop-text -->
|
204
|
+
<div class="flex-container vertical no-gap">
|
192
205
|
<button
|
193
206
|
type="button"
|
194
207
|
:class="['button upload primary', { disabled: uploadInitiated }]"
|
@@ -217,7 +230,9 @@
|
|
217
230
|
</strong>
|
218
231
|
</p>
|
219
232
|
</div>
|
233
|
+
<!-- end upload-button and drag-and-drop-text -->
|
220
234
|
</div>
|
235
|
+
|
221
236
|
<!-- begin CmdFormElement -->
|
222
237
|
<CmdFormElement
|
223
238
|
v-if="enableComment"
|
@@ -268,7 +283,8 @@
|
|
268
283
|
<!-- end advanced mode -->
|
269
284
|
|
270
285
|
<!-- begin simple mode -->
|
271
|
-
<a v-else href="#" @click.prevent="selectFiles" :class="['cmd-upload-form
|
286
|
+
<a v-else href="#" @click.prevent="selectFiles" :class="['cmd-upload-form box', { 'drop-area': enableDragAndDrop, 'allow-drop': allowDrop }]" v-on="dragAndDropHandler">
|
287
|
+
<!-- begin progressbar -->
|
272
288
|
<span class="progressbar" v-if="uploadInitiated">
|
273
289
|
<span>{{ getPercentage(totalUploadProgress) }}</span>
|
274
290
|
<progress
|
@@ -277,10 +293,11 @@
|
|
277
293
|
:title="totalBytesUploaded">
|
278
294
|
</progress>
|
279
295
|
</span>
|
296
|
+
<!-- end progressbar -->
|
280
297
|
|
281
298
|
<!-- begin slot-content -->
|
282
299
|
<slot>
|
283
|
-
<template v-if="enableDragAndDrop">
|
300
|
+
<template v-if="!enableDragAndDrop">
|
284
301
|
<template v-if="fileTypeImage">
|
285
302
|
<span>{{ getMessage("cmduploadform.select_image") }}</span>
|
286
303
|
<!-- begin CmdIcon -->
|
@@ -455,6 +472,22 @@ export default {
|
|
455
472
|
type: Boolean,
|
456
473
|
default: true
|
457
474
|
},
|
475
|
+
/**
|
476
|
+
* toggle visibility for legend-text
|
477
|
+
*/
|
478
|
+
showLegend: {
|
479
|
+
type: Boolean,
|
480
|
+
default: true
|
481
|
+
},
|
482
|
+
/**
|
483
|
+
* text for legend
|
484
|
+
*
|
485
|
+
* @requiredForAccessibility: true
|
486
|
+
*/
|
487
|
+
textLegend: {
|
488
|
+
type: String,
|
489
|
+
required: false
|
490
|
+
},
|
458
491
|
/**
|
459
492
|
* set icon for delete-icons
|
460
493
|
*/
|
@@ -611,25 +644,12 @@ export default {
|
|
611
644
|
cmdHeadlineSelectNewFile: {
|
612
645
|
type: Object,
|
613
646
|
required: false
|
614
|
-
},
|
615
|
-
/**
|
616
|
-
* toggle visibility for legend-text
|
617
|
-
*/
|
618
|
-
showLegend: {
|
619
|
-
type: Boolean,
|
620
|
-
default: true
|
621
|
-
},
|
622
|
-
/**
|
623
|
-
* text for legend
|
624
|
-
*
|
625
|
-
* @requiredForAccessibility: true
|
626
|
-
*/
|
627
|
-
textLegend: {
|
628
|
-
type: String,
|
629
|
-
required: false
|
630
647
|
}
|
631
648
|
},
|
632
649
|
computed: {
|
650
|
+
headlineTextNoFilesToUpload() {
|
651
|
+
return this.allowMultipleFileUploads ? this.getMessage("cmduploadform.no_files_to_upload") : this.this.getMessage("cmduploadform.no_file_to_upload")
|
652
|
+
},
|
633
653
|
fileTypeImage() {
|
634
654
|
return this.allowedFileExtensions.some(extension => extension.includes('jpg'));
|
635
655
|
},
|
@@ -1076,7 +1096,7 @@ export default {
|
|
1076
1096
|
overflow-x: hidden;
|
1077
1097
|
overflow-y: auto;
|
1078
1098
|
border: var(--default-border);
|
1079
|
-
padding:
|
1099
|
+
padding: var(--default-padding);
|
1080
1100
|
margin: 0;
|
1081
1101
|
|
1082
1102
|
> li {
|
@@ -1187,14 +1207,20 @@ export default {
|
|
1187
1207
|
}
|
1188
1208
|
}
|
1189
1209
|
|
1190
|
-
&.
|
1210
|
+
&.box {
|
1191
1211
|
display: inline-flex;
|
1192
1212
|
flex-direction: column;
|
1193
|
-
text-decoration: none;
|
1194
1213
|
background: var(--default-background-color);
|
1214
|
+
text-decoration: none;
|
1215
|
+
text-align: center;
|
1216
|
+
padding: var(--default-padding);
|
1195
1217
|
|
1196
1218
|
span {
|
1197
1219
|
margin: 0;
|
1220
|
+
|
1221
|
+
&[class*="icon-"] {
|
1222
|
+
font-size: 5rem;
|
1223
|
+
}
|
1198
1224
|
}
|
1199
1225
|
}
|
1200
1226
|
}
|
@@ -0,0 +1,171 @@
|
|
1
|
+
<template>
|
2
|
+
<CmdBox
|
3
|
+
:use-slots="['body']"
|
4
|
+
:collapsible="true"
|
5
|
+
:cmdHeadline="{headlineText: readableName(componentName), headlineLevel: 4, headlineIcon: {iconClass: 'icon-settings-template'}}"
|
6
|
+
:openCollapsedBox="true"
|
7
|
+
boxBodyClass="settings-body"
|
8
|
+
>
|
9
|
+
<template v-slot:body>
|
10
|
+
<CmdFormElement
|
11
|
+
v-for="(prop) in filteredProps" :key="prop.name"
|
12
|
+
:element="formElement(prop)"
|
13
|
+
:type="formType(prop)"
|
14
|
+
:labelText="readableName(prop.name)"
|
15
|
+
:placeholder="readableName(prop.name)"
|
16
|
+
:toggleSwitch="prop.toggleSwitch"
|
17
|
+
:colored="true"
|
18
|
+
:selectOptions="selectOptions(prop)"
|
19
|
+
:modelValue="prop.value"
|
20
|
+
@update:modelValue="onUpdateProperty(prop.name, $event)"
|
21
|
+
/>
|
22
|
+
</template>
|
23
|
+
</CmdBox>
|
24
|
+
</template>
|
25
|
+
|
26
|
+
<script>
|
27
|
+
|
28
|
+
export default {
|
29
|
+
name: "ComponentSettings",
|
30
|
+
data() {
|
31
|
+
return {
|
32
|
+
currentComponentName: ""
|
33
|
+
}
|
34
|
+
},
|
35
|
+
props: {
|
36
|
+
componentName: {
|
37
|
+
type: String,
|
38
|
+
default: ""
|
39
|
+
},
|
40
|
+
componentProps: {
|
41
|
+
type: Object,
|
42
|
+
required: true
|
43
|
+
},
|
44
|
+
componentSettings: {
|
45
|
+
type: Object,
|
46
|
+
required: true
|
47
|
+
},
|
48
|
+
componentControls: {
|
49
|
+
type: Object
|
50
|
+
}
|
51
|
+
},
|
52
|
+
computed: {
|
53
|
+
filteredProps() {
|
54
|
+
return this.filterProperties(this.componentProps, [])
|
55
|
+
}
|
56
|
+
},
|
57
|
+
methods: {
|
58
|
+
filterProperties(properties, nameParts) {
|
59
|
+
const allProps = []
|
60
|
+
|
61
|
+
for (let key in properties) {
|
62
|
+
const propType = typeof properties[key]
|
63
|
+
if (propType === "boolean" || propType === "string" || propType === "number" || (propType === "object" && key.slice(0, 3) === "cmd")) {
|
64
|
+
|
65
|
+
if(propType === "object") {
|
66
|
+
this.filterProperties(properties[key], [...nameParts, key]).forEach((item) => allProps.push(item))
|
67
|
+
} else {
|
68
|
+
const prop = {}
|
69
|
+
|
70
|
+
if (propType === "boolean") {
|
71
|
+
prop.toggleSwitch = true
|
72
|
+
}
|
73
|
+
|
74
|
+
prop.name = [...nameParts, key].join(".")
|
75
|
+
prop.type = propType
|
76
|
+
prop.value = properties[key]
|
77
|
+
|
78
|
+
allProps.push(prop)
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
return allProps
|
84
|
+
},
|
85
|
+
onUpdateProperty(propName, value) {
|
86
|
+
const nameParts = propName.split(".")
|
87
|
+
let settings = this.componentSettings
|
88
|
+
for (let i = 0; i < nameParts.length - 1; i++) {
|
89
|
+
settings = settings[nameParts.shift()]
|
90
|
+
}
|
91
|
+
|
92
|
+
if (typeof settings[nameParts[0]] === "number") {
|
93
|
+
settings[nameParts[0]] = Number(value)
|
94
|
+
} else {
|
95
|
+
settings[nameParts[0]] = value
|
96
|
+
}
|
97
|
+
},
|
98
|
+
readableName(name) {
|
99
|
+
// remove prefix from current component-name
|
100
|
+
let nameWithoutPrefix = name.replace("Cmd", "")
|
101
|
+
|
102
|
+
// remove entire prefix (componentName) for inner-component-properties
|
103
|
+
nameWithoutPrefix = nameWithoutPrefix.replace(/^[^.]*\./, '');
|
104
|
+
|
105
|
+
// use a regular expression to find the positions of capital letters
|
106
|
+
let capitalPositions = []
|
107
|
+
nameWithoutPrefix.replace(/[A-Z]/g, function (match, index) {
|
108
|
+
capitalPositions.push(index)
|
109
|
+
return match
|
110
|
+
})
|
111
|
+
|
112
|
+
// add a space before each capital letter based on the found positions
|
113
|
+
for (let i = capitalPositions.length - 1; i >= 0; i--) {
|
114
|
+
nameWithoutPrefix = nameWithoutPrefix.slice(0, capitalPositions[i]) + ' ' + nameWithoutPrefix.slice(capitalPositions[i])
|
115
|
+
}
|
116
|
+
|
117
|
+
// capitalize first letter
|
118
|
+
nameWithoutPrefix = nameWithoutPrefix.charAt(0).toUpperCase() + nameWithoutPrefix.slice(1)
|
119
|
+
|
120
|
+
return nameWithoutPrefix
|
121
|
+
},
|
122
|
+
formType(prop) {
|
123
|
+
if (Array.isArray(this.componentControls?.[prop.name])) {
|
124
|
+
return null
|
125
|
+
}
|
126
|
+
|
127
|
+
switch (prop.type) {
|
128
|
+
case "boolean":
|
129
|
+
return "checkbox"
|
130
|
+
|
131
|
+
case "number":
|
132
|
+
return "number"
|
133
|
+
|
134
|
+
default:
|
135
|
+
return "text"
|
136
|
+
|
137
|
+
}
|
138
|
+
},
|
139
|
+
formElement(prop) {
|
140
|
+
if (Array.isArray(this.componentControls?.[prop.name])) {
|
141
|
+
return "select"
|
142
|
+
} else {
|
143
|
+
return "input"
|
144
|
+
}
|
145
|
+
},
|
146
|
+
selectOptions(prop) {
|
147
|
+
if (Array.isArray(this.componentControls?.[prop.name])) {
|
148
|
+
return this.componentControls[prop.name]
|
149
|
+
} else {
|
150
|
+
return null
|
151
|
+
}
|
152
|
+
}
|
153
|
+
},
|
154
|
+
watch: {
|
155
|
+
componentName: {
|
156
|
+
handler() {
|
157
|
+
this.currentComponentName = this.componentName
|
158
|
+
},
|
159
|
+
immediate: true
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
</script>
|
164
|
+
|
165
|
+
<style>
|
166
|
+
.cmd-box .settings-body > div {
|
167
|
+
display: flex;
|
168
|
+
flex-direction: column;
|
169
|
+
gap: calc(var(--default-gap) / 2);
|
170
|
+
}
|
171
|
+
</style>
|
package/src/main.js
CHANGED
@@ -4,8 +4,8 @@ import "comand-frontend-framework/styles"
|
|
4
4
|
|
5
5
|
import { createApp } from "vue"
|
6
6
|
|
7
|
-
// import
|
8
|
-
import
|
7
|
+
// import ComponentLibrary from "./ComponentLibrary.vue"
|
8
|
+
import ComponentLibrary from "./ComponentLibrary.vue"
|
9
9
|
//import { createRouter, createWebHistory } from "vue-router"
|
10
10
|
import "clickout-event"
|
11
11
|
|
@@ -53,4 +53,4 @@ import router from "./router"
|
|
53
53
|
// })
|
54
54
|
|
55
55
|
// createApp(App).use(router).directive('telephone', directiveTelephone).directive('focus', directiveFocus).mount('#app')
|
56
|
-
createApp(
|
56
|
+
createApp(ComponentLibrary).use(router).directive('telephone', directiveTelephone).directive('focus', directiveFocus).mount('#app')
|