comand-component-library 3.1.66 → 3.1.69

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,17 +3,18 @@
3
3
  :class="[
4
4
  'cmd-form-element',
5
5
  validationStatus,
6
- {
7
- disabled: $attrs.disabled,
8
- inline : displayLabelInline,
9
- checked: isChecked,
10
- 'toggle-switch-label': toggleSwitch,
11
- colored: colored,
12
- on: colored && isChecked,
13
- off: colored && !isChecked,
14
- 'has-state': validationStatus
15
- }]"
16
- :for="id"
6
+ $attrs.class,
7
+ {
8
+ disabled: $attrs.disabled,
9
+ inline : displayLabelInline,
10
+ checked: isChecked,
11
+ 'toggle-switch': toggleSwitch,
12
+ colored: colored,
13
+ on: colored && isChecked,
14
+ off: colored && !isChecked,
15
+ 'has-state': validationStatus
16
+ }]"
17
+ :for="labelId"
17
18
  ref="label">
18
19
 
19
20
  <!-- begin label-text (+ required asterisk) -->
@@ -49,18 +50,18 @@
49
50
  <!-- begin inputfield -->
50
51
  <template
51
52
  v-if="element === 'input' && $attrs.type !== 'checkbox' && $attrs.type !== 'radio' && $attrs.type !== 'search'">
52
- <input v-bind="$attrs"
53
- :id="id"
54
- :class="htmlClass"
53
+ <input v-bind="elementAttributes"
54
+ :id="labelId"
55
+ :class="inputClass"
55
56
  @focus="tooltip = true"
56
57
  @blur="onBlur"
57
58
  @input="onInput"
58
59
  @mouseover="datalistFocus"
59
60
  @keyup="checkForCapsLock"
60
- :autocomplete="datalist ? 'off' : 'on'"
61
- :list="datalist ? datalist.id : false"
61
+ :autocomplete="autocomplete"
62
+ :list="datalist ? datalist.id : null"
62
63
  :value="modelValue"
63
- :maxlength="$attrs.maxlength > 0 ? $attrs.maxlength : 255"
64
+ :maxlength="getMaxLength()"
64
65
  ref="input"
65
66
  />
66
67
  </template>
@@ -88,41 +89,48 @@
88
89
 
89
90
  <!-- begin checkbox and radiobutton -->
90
91
  <template v-else-if="element === 'input' && ($attrs.type === 'checkbox' || $attrs.type === 'radio')">
91
- <input v-bind="$attrs"
92
- @change="onChange"
93
- @blur="onBlur"
94
- :checked="isChecked"
95
- :role="$attrs.type"
96
- :aria-checked="isChecked"
97
- :value="inputValue"
98
- :class="[htmlClass, validationStatus, { 'replace-input-type': replaceInputType, 'toggle-switch': toggleSwitch }]"
99
- :id="id"
100
- :aria-invalid="validationStatus === 'error'"
101
- :aria-describedby="`status-message-${id}`"
102
- />
103
-
104
- <!-- begin labels for toggle-switch -->
105
- <span v-if="!(onLabel && offLabel)" :class="{ hidden: !showLabel }">
92
+ <template v-if="!(onLabel && offLabel)">
93
+ <input v-bind="elementAttributes"
94
+ @change="onChange"
95
+ @blur="onBlur"
96
+ :checked="isChecked"
97
+ :value="inputValue"
98
+ :class="[inputClass, validationStatus, { 'replace-input-type': replaceInputType, 'toggle-switch': toggleSwitch }]"
99
+ :id="labelId"
100
+ :aria-invalid="validationStatus === 'error'"
101
+ />
102
+ <span :class="{ hidden: !showLabel }">
106
103
  <span v-if="labelText">{{ labelText }}<sup v-if="$attrs.required">*</sup></span>
107
104
  </span>
105
+ </template>
106
+ <!-- begin labels for toggle-switch with switch-label -->
108
107
  <template v-else-if="onLabel && offLabel">
108
+ <span class="switch-label-wrapper">
109
+ <input v-bind="elementAttributes"
110
+ @change="onChange"
111
+ @blur="onBlur"
112
+ :checked="isChecked"
113
+ :value="inputValue"
114
+ :class="{inputClass, validationStatus}"
115
+ :id="labelId"
116
+ :aria-invalid="validationStatus === 'error'"
117
+ />
118
+ <span class="label">{{ onLabel }}</span>
119
+ <span class="label">{{ offLabel }}</span>
120
+ </span>
109
121
  <span v-if="labelText">
110
122
  <span>{{ labelText }}<sup v-if="$attrs.required">*</sup></span>
111
123
  </span>
112
- <div class="toggle-switch switch-label">
113
- <span class="label">{{ onLabel }}</span>
114
- <span class="label">{{ offLabel }}</span>
115
- </div>
116
124
  </template>
117
125
  <slot v-else></slot>
118
- <!-- end labels for toggle-switch -->
126
+ <!-- end labels for toggle-switch with switch-label -->
119
127
  </template>
120
128
  <!-- end checkbox and radiobutton -->
121
129
 
122
130
  <!-- begin selectbox -->
123
131
  <select v-if="element === 'select'"
124
- v-bind="$attrs"
125
- :id="id"
132
+ v-bind="elementAttributes"
133
+ :id="labelId"
126
134
  @blur="onBlur"
127
135
  @change="$emit('update:modelValue', $event.target.value)">
128
136
  <option v-for="(option, index) in selectOptions" :key="index" :value="option.value"
@@ -133,10 +141,10 @@
133
141
 
134
142
  <!-- begin textarea -->
135
143
  <textarea v-if="element === 'textarea'"
136
- v-bind="$attrs"
137
- :id="id"
144
+ v-bind="elementAttributes"
145
+ :id="labelId"
138
146
  :value="modelValue"
139
- :maxlength="getMaxLength"
147
+ :maxlength="getMaxLength()"
140
148
  @input="onInput"
141
149
  @focus="tooltip = true"
142
150
  @blur="onBlur">
@@ -147,18 +155,18 @@
147
155
  <!-- begin searchfield -->
148
156
  <template v-else-if="element === 'input' && $attrs.type === 'search'">
149
157
  <div class="search-field-wrapper flex-container no-gap">
150
- <a v-if="iconDelete.show" href="#" @click.prevent="$emit('update:modelValue', '')" :class="iconDelete.iconClass" :title="iconDelete.tooltip"/>
151
158
  <input
152
- v-bind="$attrs"
153
- :id="id"
159
+ v-bind="elementAttributes"
160
+ :id="labelId"
154
161
  @input="onInput"
155
162
  :maxlength="$attrs.maxlength > 0 ? $attrs.maxlength : 255"
156
163
  :value="modelValue"
157
164
  />
165
+ <a v-if="showSearchButton" href="#" class="button no-flex" :title="iconSearch.tooltip" @click.prevent="executeSearch">
166
+ <span :class="iconSearch.iconClass"></span>
167
+ </a>
168
+ <a v-if="iconDelete.show" href="#" @click.prevent="$emit('update:modelValue', '')" :class="iconDelete.iconClass" :title="iconDelete.tooltip"></a>
158
169
  </div>
159
- <button v-if="showSearchButton" class="no-flex" type="button" :title="iconSearch.tooltip">
160
- <span :class="iconSearch.iconClass"></span>
161
- </button>
162
170
  </template>
163
171
  </label>
164
172
  <!-- end searchfield -->
@@ -215,6 +223,9 @@
215
223
  </template>
216
224
 
217
225
  <script>
226
+ // import utils
227
+ import {createUuid} from "../utils/common.js"
228
+
218
229
  // import mixins
219
230
  import I18n from "../mixins/I18n"
220
231
  import DefaultMessageProperties from "../mixins/CmdBox/DefaultMessageProperties"
@@ -349,7 +360,7 @@ export default {
349
360
  *
350
361
  * may not be named as 'class' because it is a reserved keyword in JavaScript
351
362
  */
352
- htmlClass: {
363
+ inputClass: {
353
364
  type: String,
354
365
  required: false
355
366
  },
@@ -582,6 +593,19 @@ export default {
582
593
  },
583
594
  },
584
595
  computed: {
596
+ elementAttributes() {
597
+ const commonAttributes = ["name", "required", "readonly", "disabled", "autofocus"]
598
+ const attributes = {
599
+ input: [...commonAttributes, "type", "minlength", "pattern", "min", "max", "multiple", "step", "autocomplete", "placeholder"],
600
+ select: [...commonAttributes, "multiple"],
601
+ textarea: [...commonAttributes, "placeholder"]
602
+ }
603
+ const attrs = {}
604
+ if(attributes[this.element]) {
605
+ Object.entries(this.$attrs).filter(([name]) => attributes[this.element].includes(name)).forEach(([name, value]) => attrs[name] = value)
606
+ }
607
+ return attrs
608
+ },
585
609
  buttonAttrs() {
586
610
  // copy all native attributes
587
611
  const allAttrs = {...this.$attrs}
@@ -631,6 +655,19 @@ export default {
631
655
  return this.getMessage("cmdformelement.validationTooltip.caps_lock_is_activated")
632
656
  }
633
657
  return this.getMessage("cmdformelement.validationTooltip.open_field_requirements")
658
+ },
659
+ autocomplete() {
660
+ if (this.$attrs.type !== 'file') {
661
+ return this.datalist ? 'off' : 'on'
662
+ }
663
+ return null
664
+ },
665
+ // get ID for accessibility
666
+ labelId() {
667
+ if (this.$attrs.id !== undefined) {
668
+ return this.$attrs.id
669
+ }
670
+ return "label-" + createUuid()
634
671
  }
635
672
  },
636
673
  methods: {
@@ -638,7 +675,15 @@ export default {
638
675
  return this.$refs.label
639
676
  },
640
677
  getMaxLength() {
641
- return this.$attrs.maxlength > 0 ? this.$attrs.maxlength : 5000
678
+ if (this.$attrs.element === 'textarea') {
679
+ return this.$attrs.maxlength > 0 ? this.$attrs.maxlength : 5000
680
+ }
681
+
682
+ if (this.$attrs.type !== 'file') {
683
+ return this.$attrs.maxlength > 0 ? this.$attrs.maxlength : 255
684
+ }
685
+
686
+ return null
642
687
  },
643
688
  onBlur(event) {
644
689
  // check if surrounding form with data-use-validation exists
@@ -711,6 +756,9 @@ export default {
711
756
  hidePassword() {
712
757
  this.$refs.input.nextElementSibling.classList.replace(this.iconPasswordInvisible.iconClass, this.iconPasswordVisible.iconClass)
713
758
  this.$refs.input.setAttribute("type", "password")
759
+ },
760
+ executeSearch() {
761
+ this.$emit("search", this.value)
714
762
  }
715
763
  },
716
764
  watch: {
@@ -776,15 +824,17 @@ export default {
776
824
  .search-field-wrapper {
777
825
  margin: 0;
778
826
 
779
- a {
827
+ a[class*="icon"] {
780
828
  position: absolute;
781
829
  top: 50%;
782
830
  right: 1rem;
783
831
  transform: translateY(-50%);
784
832
  z-index: 100;
833
+ }
785
834
 
786
- & + input {
787
- padding-right: calc(var(--default-padding) * 3);
835
+ a.button {
836
+ & + a[class*="icon"] {
837
+ right: 5rem;
788
838
  }
789
839
  }
790
840
  }
@@ -792,7 +842,6 @@ export default {
792
842
  .place-inside {
793
843
  + .search-field-wrapper {
794
844
 
795
-
796
845
  input {
797
846
  padding-left: calc(var(--default-padding) * 3);
798
847
  }
@@ -800,29 +849,11 @@ export default {
800
849
  }
801
850
 
802
851
  /* begin toggle-switch */
803
- /* no cmd-prefix-styling (class based on frontend-framework */
852
+ /* no cmd-prefix-styling (class based on frontend-framework) */
804
853
  &.toggle-switch {
805
- &.switch-label {
806
- input {
807
- & + .label {
808
- padding-right: calc(var(--default-padding) / 3 * 2);
809
-
810
- &::before {
811
- top: 0.2rem;
812
- }
813
-
814
- & + .label {
815
- padding-left: calc(var(--default-padding) / 3 * 2);
816
-
817
- &::before {
818
- top: 0.2rem;
819
- }
820
- }
821
- }
822
- }
823
-
824
- &.colored {
825
- &.off {
854
+ &.colored {
855
+ &.off {
856
+ .switch-label-wrapper {
826
857
  border-color: var(--error-color);
827
858
 
828
859
  span {
@@ -836,8 +867,10 @@ export default {
836
867
  }
837
868
  }
838
869
  }
870
+ }
839
871
 
840
- &.on {
872
+ &.on {
873
+ .switch-label-wrapper {
841
874
  border-color: var(--success-color);
842
875
 
843
876
  span {
@@ -25,7 +25,7 @@ export default {
25
25
  },
26
26
  props: {
27
27
  /**
28
- * list of all selected options
28
+ * model-value to support v-model
29
29
  */
30
30
  modelValue: {
31
31
  type: Array,
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <div :class="['cmd-input-group', {inline: labelInline}]">
3
+ <span :class="['label', { hidden: !showLabel}]">{{ labelText }}</span>
4
+ <div class="flex-container no-flex">
5
+ <slot></slot>
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <script>
11
+ export default {
12
+ props: {
13
+ /**
14
+ * toggle label-text visibility
15
+ */
16
+ showLabel: {
17
+ type: Boolean,
18
+ default: true
19
+ },
20
+ /**
21
+ * label-text for input-group
22
+ *
23
+ * @requiredForAccessibility: true
24
+ */
25
+ labelText: {
26
+ type: String,
27
+ required: true
28
+ },
29
+ /**
30
+ * toggle label-position (inline/left of input-elements or above input-elements)
31
+ */
32
+ labelInline: {
33
+ type: Boolean,
34
+ default: false
35
+ }
36
+ }
37
+ }
38
+ </script>
39
+
40
+ <style lang="scss">
41
+ .cmd-input-group {
42
+ &.inline {
43
+ display: flex;
44
+ gap: var(--default-gap);
45
+ }
46
+ }
47
+ </style>
@@ -137,6 +137,7 @@ export default {
137
137
  ul {
138
138
  flex-direction: column;
139
139
  gap: calc(var(--default-gap) / 2);
140
+ margin: 0;
140
141
 
141
142
  li {
142
143
  list-style: none;
@@ -145,23 +146,24 @@ export default {
145
146
  }
146
147
 
147
148
  &.horizontal {
149
+ flex-direction: row;
150
+
148
151
  ul {
149
- flex-direction: row;
150
152
  gap: var(--default-gap);
153
+ flex-direction: row;
151
154
 
152
155
  > li {
153
156
  flex: none;
154
157
  display: flex;
155
- align-items: center;
156
158
  }
157
159
  }
158
160
 
159
161
  &.align-center {
160
- align-items: center;
162
+ justify-content: center;
161
163
  }
162
164
 
163
165
  &.align-right {
164
- align-items: flex-end;
166
+ justify-content: flex-end;
165
167
  }
166
168
  }
167
169
  }
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <!-- begin login-form -->
3
- <fieldset v-if="!sendLogin" class="flex-container">
3
+ <fieldset v-if="!sendLogin" class="cmd-login-form flex-container">
4
4
  <legend :class="{hidden : !showLegend}">{{ textLegend }}</legend>
5
5
  <!-- begin CmdCustomHeadline -->
6
6
  <CmdCustomHeadline v-if="cmdCustomHeadlineLoginForm"
@@ -16,7 +16,7 @@
16
16
  :name="cmdFormElementUsername.name"
17
17
  :id="cmdFormElementUsername.id"
18
18
  v-model="username"
19
- :inner-icon-class="cmdFormElementUsername.innerIconClass"
19
+ :fieldIconClass="cmdFormElementUsername.innerIconClass"
20
20
  :labelText="cmdFormElementUsername.labelText"
21
21
  :placeholder="cmdFormElementUsername.placeholder"
22
22
  />
@@ -28,7 +28,7 @@
28
28
  type="password"
29
29
  :name="cmdFormElementPassword.name"
30
30
  :id="cmdFormElementPassword.id"
31
- :inner-icon-class="cmdFormElementPassword.innerIconClass"
31
+ :fieldIconClass="cmdFormElementPassword.innerIconClass"
32
32
  v-model="password"
33
33
  :labelText="cmdFormElementPassword.labelText"
34
34
  :placeholder="cmdFormElementPassword.placeholder"
@@ -119,7 +119,7 @@
119
119
  <!-- end login-form -->
120
120
 
121
121
  <!-- begin send-login-form -->
122
- <fieldset v-else class="flex-container">
122
+ <fieldset v-else class="cmd-login-form flex-container">
123
123
  <legend :class="{'hidden' : !legendSendLoginForm.show}">{{ legendSendLoginForm.text }}</legend>
124
124
  <!-- begin CmdCustomHeadline -->
125
125
  <CmdCustomHeadline v-if="cmdCustomHeadlineSendLoginForm"
@@ -133,7 +133,7 @@
133
133
  <CmdFormElement
134
134
  element="input"
135
135
  type="email"
136
- :inner-icon-class="cmdFormElementSendLogin.innerIconClass"
136
+ :fieldIconClass="cmdFormElementSendLogin.innerIconClass"
137
137
  :labelText="cmdFormElementSendLogin.labelText"
138
138
  :placeholder="cmdFormElementSendLogin.placeholder"
139
139
  :name="cmdFormElementSendLogin.name"
@@ -439,6 +439,7 @@ export default {
439
439
  </script>
440
440
 
441
441
  <style lang="scss">
442
+ .cmd-login-form {
442
443
  .option-wrapper {
443
444
  align-items: center;
444
445
 
@@ -458,4 +459,5 @@ export default {
458
459
  margin-left: auto;
459
460
  }
460
461
  }
462
+ }
461
463
  </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div :class="['label', 'multiple-switch', {disabled: status === 'disabled', error: status === 'error'}]">
3
- <span :class="{hidden: !showLabel}">{{ labelText }}</span>
2
+ <div :class="['cmd-multiple-switch multiple-switch label', {disabled: status === 'disabled', error: status === 'error'}]" :aria-labelledby="labelId">
3
+ <span :class="{hidden: !showLabel}" :id="labelId">{{ labelText }}</span>
4
4
  <span class="flex-container no-gap no-flex">
5
5
  <label :class="{disabled: status === 'disabled'}" :for="multipleswitch.id"
6
6
  v-for="(multipleswitch, index) in multipleSwitches" :key="index">
@@ -20,6 +20,9 @@
20
20
  </template>
21
21
 
22
22
  <script>
23
+ // import utils
24
+ import {createUuid} from "../utils/common.js"
25
+
23
26
  export default {
24
27
  name: "CmdMultipleSwitch",
25
28
  props: {
@@ -80,6 +83,15 @@ export default {
80
83
  required: false
81
84
  }
82
85
  },
86
+ computed: {
87
+ // get ID for accessibility
88
+ labelId() {
89
+ if(this.$attrs.id !== undefined) {
90
+ return this.$attrs.id
91
+ }
92
+ return "label-" + createUuid()
93
+ }
94
+ },
83
95
  methods: {
84
96
  onChange(e) {
85
97
  if (typeof this.value === "string") {
@@ -183,7 +183,7 @@ export default {
183
183
  background: var(--primary-color);
184
184
 
185
185
  span, span[class*='icon'] {
186
- color: var(--pure-white);
186
+ color: var(--color-scheme-text-color);
187
187
  }
188
188
  }
189
189
 
@@ -199,10 +199,10 @@ export default {
199
199
 
200
200
  a {
201
201
  background: none;
202
- color: var(--text-color);
202
+ color: var(--color-scheme-text-color);
203
203
 
204
204
  span, span[class*='color'] {
205
- color: var(--text-color);
205
+ color: inherit;
206
206
 
207
207
  & + span[class*="icon"] {
208
208
  &:last-child {
@@ -11,6 +11,7 @@
11
11
  <script>
12
12
  export default {
13
13
  name: "CmdProgressBar",
14
+ inheritAttrs: false,
14
15
  data() {
15
16
  return {
16
17
  loadingStatus: 30
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <div :class="['cmd-site-header', { sticky: sticky }]" role="banner">
3
3
  <!-- begin slot for elements above header -->
4
- <slot name="top-header"></slot>
4
+ <div class="top-header">
5
+ <slot name="top-header"></slot>
6
+ </div>
5
7
  <!-- end for elements above header -->
6
8
 
7
9
  <!-- begin header-wrapper with slots for logo and other header elements -->
@@ -72,26 +74,35 @@ export default {
72
74
  grid-area: site-header;
73
75
  display: flex;
74
76
  flex-direction: column;
75
- gap: var(--default-gap);
76
77
  border-bottom: var(--default-border);
77
- background: var(--pure-white);
78
+ background: var(--color-scheme-background-color);
78
79
 
79
80
  &.sticky {
80
81
  position: sticky;
81
82
  z-index: 300;
82
83
  }
83
84
 
84
- header, .cmd-main-navigation nav, .cmd-top-header-navigation > ul {
85
+ header, .cmd-main-navigation nav, .cmd-list-of-links {
85
86
  max-width: var(--max-width);
87
+ width: 100%; /* stretch flex-item */
86
88
  margin: 0 auto;
87
89
  padding: 0 var(--default-padding);
88
90
  }
89
91
 
92
+ .top-header {
93
+ .cmd-list-of-links {
94
+ padding-top: calc(var(--default-padding) / 2);
95
+ padding-bottom: calc(var(--default-padding) / 2);
96
+ }
97
+ }
98
+
90
99
  > .cmd-main-navigation:last-child {
91
100
  border-bottom: 0;
92
101
  }
93
102
 
94
103
  header {
104
+ padding: calc(var(--default-padding) * 2) 0;
105
+
95
106
  &.flex-container {
96
107
  width: 100%;
97
108
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="cmd-tabs">
3
- <ul :class="{'stretch-tabs' : stretchTabs}">
3
+ <ul :class="{'stretch-tabs' : stretchTabs}" role="tablist">
4
4
  <li :class="{active : showTab === index}" v-for="(tab, index) in tabs" :key="index" role="tab">
5
5
  <a @click.prevent="setActiveTab(index)" :title="!tab.name ? tab.tooltip : false">
6
6
  <span v-if="tab.iconClass">{{ tab.iconClass }}</span>
@@ -109,7 +109,7 @@ export default {
109
109
  border-top-left-radius: var(--border-radius);
110
110
  border-top-right-radius: var(--border-radius);
111
111
  list-style-type: none;
112
- background: var(--pure-white);
112
+ background: var(--color-scheme-background-color);
113
113
  border: var(--default-border);
114
114
 
115
115
  &.active {
@@ -125,7 +125,7 @@ export default {
125
125
  a {
126
126
  display: block;
127
127
  padding: var(--default-padding);
128
- color: var(--text-color);
128
+ color: var(--color-scheme-text-color);
129
129
 
130
130
  &:hover, &:active, &:focus {
131
131
  cursor: pointer;
@@ -154,7 +154,7 @@ export default {
154
154
  padding: var(--default-padding);
155
155
  border: var(--primary-border);
156
156
  border-radius: var(--border-radius);
157
- background: var(--pure-white);
157
+ background: var(--color-scheme-background-color);
158
158
  border-top-left-radius: 0;
159
159
 
160
160
  > div {
@@ -148,7 +148,7 @@ export default {
148
148
  display: table;
149
149
  margin: 0 auto calc(var(--default-margin) * 2) auto;
150
150
  border: var(--default-border);
151
- background: var(--pure-white);
151
+ background: var(--color-scheme-background-color);
152
152
 
153
153
  > ul {
154
154
  overflow: hidden;