wave-ui 3.7.2 → 3.8.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-ui",
3
- "version": "3.7.2",
3
+ "version": "3.8.0",
4
4
  "description": "An emerging UI framework for Vue.js (2 & 3) with only the bright side. :sunny:",
5
5
  "author": "Antoni Andre <antoniandre.web@gmail.com>",
6
6
  "homepage": "https://antoniandre.github.io/wave-ui",
@@ -50,16 +50,16 @@
50
50
  "publish-doc": "npm run build && npm run build-bundle && git add . && git commit -m 'Publish documentation on Github.' && git push && git push --tag"
51
51
  },
52
52
  "devDependencies": {
53
- "@babel/core": "^7.23.0",
53
+ "@babel/core": "^7.23.2",
54
54
  "@babel/eslint-parser": "^7.22.15",
55
- "@faker-js/faker": "^8.1.0",
55
+ "@faker-js/faker": "^8.2.0",
56
56
  "@mdi/font": "^6.9.96",
57
57
  "@vitejs/plugin-vue": "^3.2.0",
58
58
  "@vue/compiler-sfc": "3.3.4",
59
59
  "autoprefixer": "^10.4.16",
60
- "axios": "^1.5.1",
61
- "eslint": "^8.51.0",
62
- "eslint-plugin-vue": "^9.17.0",
60
+ "axios": "^1.6.0",
61
+ "eslint": "^8.52.0",
62
+ "eslint-plugin-vue": "^9.18.1",
63
63
  "font-awesome": "^4.7.0",
64
64
  "gsap": "^3.12.2",
65
65
  "ionicons": "^4.6.3",
@@ -67,13 +67,13 @@
67
67
  "postcss": "^8.4.31",
68
68
  "pug": "^3.0.2",
69
69
  "rollup-plugin-delete": "^2.0.0",
70
- "sass": "^1.69.0",
70
+ "sass": "^1.69.5",
71
71
  "simple-syntax-highlighter": "^3.0.2",
72
72
  "splitpanes": "^3.1.5",
73
73
  "standard": "^17.1.0",
74
74
  "vite": "^3.2.7",
75
75
  "vite-svg-loader": "^4.0.0",
76
- "vue": "^3.3.4",
76
+ "vue": "^3.3.7",
77
77
  "vue-router": "^4.2.5",
78
78
  "vueperslides": "^3.5.1",
79
79
  "vuex": "^4.1.0"
@@ -1,5 +1,6 @@
1
1
  export { default as WAccordion } from './w-accordion.vue'
2
2
  export { default as WAlert } from './w-alert.vue'
3
+ export { default as WAutocomplete } from './w-autocomplete.vue'
3
4
  export { default as WApp } from './w-app.vue'
4
5
  export { default as WBadge } from './w-badge.vue'
5
6
  export { default as WBreadcrumbs } from './w-breadcrumbs.vue'
@@ -0,0 +1,309 @@
1
+ <template lang="pug">
2
+ .w-autocomplete(:class="classes" @click="onClick")
3
+ template(v-if="selection.length")
4
+ .w-autocomplete__selection(v-for="(item, i) in selection")
5
+ span(v-html="item[itemLabelKey]")
6
+ w-button(@click.stop="unselectItem(i)" icon="i-cross" xs text color="currentColor")
7
+ .w-autocomplete__placeholder(v-if="!selection.length && !keywords && placeholder" v-html="placeholder")
8
+ input.w-autocomplete__input(
9
+ ref="input"
10
+ v-model="keywords"
11
+ @focus="onFocus"
12
+ @keydown="onKeydown"
13
+ @drop="onDrop"
14
+ @compositionstart="onCompositionStart"
15
+ @compositionupdate="onCompositionUpdate"
16
+ v-on="$listeners")
17
+ w-transition-slide-fade(y)
18
+ ul.w-autocomplete__menu(v-if="menuOpen" ref="menu")
19
+ li(
20
+ v-for="(item, i) in filteredItems"
21
+ :key="i"
22
+ @click.stop="selectItem(item)"
23
+ :class="{ highlighted: highlightedItem === item.uid }")
24
+ span(v-html="item[itemLabelKey]")
25
+ li.w-autocomplete__no-match(
26
+ v-if="!filteredItems.length"
27
+ :class="{ 'w-autocomplete__no-match--default': !$slots.noMatch }")
28
+ slot(name="no-match")
29
+ .caption(v-html="noMatch ?? 'No match.'")
30
+ </template>
31
+
32
+ <script>
33
+ export default {
34
+ name: 'w-autocomplete',
35
+
36
+ props: {
37
+ items: { type: Array, required: true },
38
+ modelValue: { type: [String, Number, Array] }, // String or Number if single selections, Array if multiple.
39
+ placeholder: { type: String },
40
+ openOnKeydown: { type: Boolean }, // By default the menu is always open for selection.
41
+ multiple: { type: Boolean },
42
+ // When multiple is on, prevents duplicate items selections by default, unless this is set to true.
43
+ allowDuplicates: { type: Boolean },
44
+ noMatch: { type: String },
45
+ // Contains the unique selection value for each item.
46
+ // Can be a numeric ID, a slug, etc. (outside of Wave UI)
47
+ itemValueKey: { type: String, default: 'value' },
48
+ // Contains the string to display for each item.
49
+ itemLabelKey: { type: String, default: 'label' },
50
+ // Contains the string to search keywords into for each item.
51
+ // This can for instance be an aggregation of multiple fields (outside of Wave UI).
52
+ itemSearchableKey: { type: String, default: 'searchable' }
53
+ },
54
+
55
+ emits: ['input'],
56
+
57
+ data: () => ({
58
+ keywords: '',
59
+ selection: [],
60
+ menuOpen: false,
61
+ highlightedItem: null
62
+ }),
63
+
64
+ computed: {
65
+ // Keep the autocomplete matching as fast as possible by caching optimized search strings.
66
+ normalizedKeywords () {
67
+ return this.normalize(this.keywords)
68
+ },
69
+
70
+ // Keep the autocomplete matching as fast as possible by caching optimized search strings.
71
+ // An array of optimized strings.
72
+ normalizedSelection () {
73
+ return this.selection.map(item => this.normalize(item?.searchable))
74
+ },
75
+
76
+ // Keep the autocomplete matching as fast as possible by caching optimized search strings.
77
+ optimizedItemsForSearch () {
78
+ return this.items.map((item, i) => ({
79
+ ...item,
80
+ uid: i,
81
+ searchable: this.normalize(item[this.itemSearchableKey] || '')
82
+ }))
83
+ },
84
+
85
+ filteredItems () {
86
+ let items = this.optimizedItemsForSearch // Array of objects.
87
+ const selection = this.normalizedSelection.join(',') // Optimized string of coma separated words.
88
+ const isItemNotSelected = item => !selection.includes(item.searchable)
89
+
90
+ if (this.keywords) {
91
+ items = items.filter(item => {
92
+ if (!item.searchable.includes(this.normalizedKeywords)) return false
93
+ else if (this.multiple && !this.allowDuplicates) return isItemNotSelected(item)
94
+ else return true
95
+ })
96
+ }
97
+
98
+ else if (this.multiple && !this.allowDuplicates) items = items.filter(isItemNotSelected)
99
+
100
+ return items
101
+ },
102
+
103
+ highlightedItemIndex () {
104
+ if (this.highlightedItem === null) return -1
105
+ return this.filteredItems.findIndex(item => item.uid === this.highlightedItem)
106
+ },
107
+
108
+ classes () {
109
+ return {
110
+ 'w-autocomplete--open': this.menuOpen
111
+ }
112
+ }
113
+ },
114
+
115
+ methods: {
116
+ // Replace all the accents and non-latin characters with equivalent letters. E.g. é -> e.
117
+ normalize (string) {
118
+ return string.toLowerCase().normalize('NFKD').replace(/\p{Diacritic}/gu, '').replace(/œ/g, 'oe')
119
+ },
120
+
121
+ // Selection can be made from click or enter key.
122
+ selectItem (item) {
123
+ if (!this.multiple) this.selection = []
124
+ this.selection.push(item)
125
+ this.highlightedItem = item.uid
126
+ this.keywords = ''
127
+ this.$emit('input', this.multiple ? this.selection.map(item => item[this.itemValueKey]) : item[this.itemValueKey])
128
+ this.$refs.input.focus()
129
+ if (!this.multiple) this.closeMenu()
130
+ },
131
+
132
+ unselectItem (i) {
133
+ this.selection.splice(i ?? this.selection.length - 1, 1)
134
+ this.highlightedItem = null
135
+ this.$emit('input', null)
136
+ this.$refs.input.focus()
137
+ },
138
+
139
+ onClick () {
140
+ if (!this.openOnKeydown) this.openMenu()
141
+ this.$refs.input.focus()
142
+ },
143
+
144
+ onFocus () {
145
+ if (!this.openOnKeydown) this.openMenu()
146
+ },
147
+
148
+ onKeydown (e) {
149
+ const itemsCount = this.filteredItems.length
150
+ // `e.key.length === 1`: is all the keyboard keys that generate a character.
151
+ if (!this.openOnKeydown || ((this.keywords || e.key.length === 1) && !this.menuOpen)) this.openMenu()
152
+
153
+ // Delete key.
154
+ if (e.keyCode === 8 && (!this.keywords || (!e.target.selectionStart && !e.target.selectionEnd))) {
155
+ this.unselectItem()
156
+ }
157
+
158
+ // Enter key.
159
+ else if (e.keyCode === 13) {
160
+ e.preventDefault() // Prevent form submissions.
161
+ if (this.highlightedItemIndex >= 0) this.selectItem(this.filteredItems[this.highlightedItemIndex])
162
+ }
163
+
164
+ // Up & down arrow keys.
165
+ else if ([38, 40].includes(e.keyCode)) {
166
+ e.preventDefault() // Prevent moving the cursor to the left of the text while selecting item.
167
+ let index = this.highlightedItemIndex
168
+ if (index === -1) index = e.keyCode === 38 ? itemsCount - 1 : 0
169
+ else index = (index + (e.keyCode === 38 ? -1 : 1) + itemsCount) % itemsCount // Never out of range.
170
+
171
+ this.highlightedItem = this.filteredItems[index]?.uid || 0
172
+
173
+ // Scroll the container if highlighted item is not in view.
174
+ const menuEl = this.$refs.menu
175
+ if (menuEl) {
176
+ const { offsetHeight: itemElHeight, offsetTop: itemElTop } = menuEl.childNodes[index] || {}
177
+ if (menuEl.scrollTop + menuEl.offsetHeight - itemElHeight < itemElTop) {
178
+ menuEl.scrollTop = itemElTop - menuEl.offsetHeight + itemElHeight
179
+ }
180
+ else if (menuEl.scrollTop > itemElTop) menuEl.scrollTop = itemElTop
181
+ }
182
+ }
183
+
184
+ // `e.key.length === 1`: allow all control keys but no character creation.
185
+ else if (!this.multiple && this.selection.length && (e.key.length === 1)) e.preventDefault()
186
+ },
187
+
188
+ // On drag & drop of a text in the input field, don't paste if single selection and already selected.
189
+ onDrop (e) {
190
+ if (!this.multiple && this.selection.length) e.preventDefault()
191
+ },
192
+
193
+ // When starting a sequence of keys that produces a character.
194
+ onCompositionStart (e) {
195
+ // e.preventDefault() does not work. https://stackoverflow.com/a/77556830/2012407
196
+ if (!this.multiple && this.selection.length) e.target.setAttribute('readonly', true)
197
+ },
198
+ onCompositionUpdate (e) {
199
+ if (!this.multiple && this.selection.length) setTimeout(() => e.target.removeAttribute('readonly'), 200)
200
+ },
201
+
202
+ openMenu () {
203
+ if (this.menuOpen) return
204
+ this.menuOpen = true
205
+ document.addEventListener('click', this.onDocumentClick)
206
+ },
207
+
208
+ closeMenu () {
209
+ this.menuOpen = false
210
+ document.removeEventListener('click', this.onDocumentClick)
211
+ },
212
+
213
+ onDocumentClick (e) {
214
+ if (!this.$el.contains(e.target) && !this.$el.isSameNode(e.target)) this.closeMenu()
215
+ }
216
+ },
217
+
218
+ created () {
219
+ if (this.modelValue) {
220
+ const arrayOfValues = Array.isArray(this.modelValue) ? this.modelValue : [this.modelValue]
221
+ arrayOfValues.forEach(value => {
222
+ this.selection.push(this.optimizedItemsForSearch.find(item => item[this.itemValueKey] === +value))
223
+ })
224
+ }
225
+ },
226
+
227
+ beforeUnmount () {
228
+ document.removeEventListener('click', this.onDocumentClick)
229
+ }
230
+ }
231
+ </script>
232
+
233
+ <style lang="scss">
234
+ .w-autocomplete {
235
+ display: flex;
236
+ flex-wrap: wrap;
237
+ gap: 4px;
238
+ position: relative;
239
+ border-radius: 4px;
240
+ border: 1px solid rgba(#000, 0.2);
241
+ padding: 2px 4px;
242
+ user-select: none;
243
+
244
+ &--open {
245
+ border-bottom-left-radius: 0;
246
+ border-bottom-right-radius: 0;
247
+ }
248
+
249
+ &__selection {
250
+ display: flex;
251
+ align-items: center;
252
+ background: rgba(#000, 0.035);
253
+ border: 1px solid rgba(#000, 0.05);
254
+ border-radius: 4px;
255
+ padding: 0 2px 0 4px;
256
+ flex-shrink: 0;
257
+
258
+ span {margin-top: -1px;line-height: 1;}
259
+ .w-button .w-icon:before {font-size: 0.8em;line-height: 0;}
260
+ }
261
+
262
+ &__input {
263
+ min-width: 0;
264
+ flex: 1 1 0;
265
+ color: inherit;
266
+ border: none;
267
+ background-color: transparent;
268
+ line-height: 18px;
269
+ }
270
+
271
+ &__placeholder {
272
+ position: absolute;
273
+ color: #ccc;
274
+ pointer-events: none;
275
+ line-height: 18px;
276
+ }
277
+
278
+ &__menu {
279
+ position: absolute;
280
+ inset: 100% -1px auto;
281
+ max-height: clamp(20px, 400px, 80vh);
282
+ margin-top: -1px;
283
+ margin-left: 0;
284
+ background-color: #fff;
285
+ border: 1px solid rgba(#000, 0.2);
286
+ border-top: none;
287
+ border-bottom-left-radius: inherit;
288
+ border-bottom-right-radius: inherit;
289
+ overflow: auto;
290
+ z-index: 10;
291
+
292
+ li {
293
+ list-style-type: none;
294
+ margin: 0;
295
+ padding: 4px 8px;
296
+ border-left: 2px solid transparent;
297
+
298
+ &:hover {background-color: rgba($primary, 0.1);}
299
+
300
+ &.highlighted {
301
+ background-color: rgba($primary, 0.15);
302
+ border-left-color: rgba($primary, 0.75);
303
+ }
304
+ }
305
+ }
306
+ }
307
+
308
+ li.w-autocomplete__no-match--default:hover {background-color: transparent;}
309
+ </style>
@@ -103,6 +103,8 @@ export default {
103
103
  padding: (2 * $base-increment) (3 * $base-increment);
104
104
  font-size: 1.3em;
105
105
  border-bottom: $border;
106
+ border-top-left-radius: inherit;
107
+ border-top-right-radius: inherit;
106
108
 
107
109
  &--has-toolbar {padding: 0;border-bottom: none;}
108
110
  }
@@ -20,11 +20,12 @@ component(
20
20
 
21
21
  //- Input wrapper.
22
22
  .w-input__input-wrap(:class="inputWrapClasses")
23
- w-icon.w-input__icon.w-input__icon--inner-left(
24
- v-if="innerIconLeft"
25
- tag="label"
26
- :for="`w-input--${_.uid}`"
27
- @click="$emit('click:inner-icon-left', $event)") {{ innerIconLeft }}
23
+ slot(name="icon-left" :input-id="`w-input--${_.uid}`")
24
+ w-icon.w-input__icon.w-input__icon--inner-left(
25
+ v-if="innerIconLeft"
26
+ tag="label"
27
+ :for="`w-input--${_.uid}`"
28
+ @click="$emit('click:inner-icon-left', $event)") {{ innerIconLeft }}
28
29
  //- All types of input except file.
29
30
  input.w-input__input(
30
31
  v-if="type !== 'file'"
@@ -78,14 +79,15 @@ component(
78
79
  template(v-if="labelPosition === 'inside' && showLabelInside")
79
80
  label.w-input__label.w-input__label--inside.w-form-el-shakable(
80
81
  v-if="$slots.default || label"
81
- :for="`w-input--${_.uid}`"
82
82
  :class="labelClasses")
83
83
  slot {{ label }}
84
- w-icon.w-input__icon.w-input__icon--inner-right(
85
- v-if="innerIconRight"
86
- tag="label"
87
- :for="`w-input--${_.uid}`"
88
- @click="$emit('click:inner-icon-right', $event)") {{ innerIconRight }}
84
+
85
+ slot(name="icon-right" :input-id="`w-input--${_.uid}`")
86
+ w-icon.w-input__icon.w-input__icon--inner-right(
87
+ v-if="innerIconRight"
88
+ tag="label"
89
+ :for="`w-input--${_.uid}`"
90
+ @click="$emit('click:inner-icon-right', $event)") {{ innerIconRight }}
89
91
 
90
92
  w-progress.fill-width(
91
93
  v-if="hasLoading || (showProgress && (uploadInProgress || uploadComplete))"
@@ -24,7 +24,7 @@ component(
24
24
  custom
25
25
  min-width="activator"
26
26
  v-bind="menuProps || {}")
27
- template(#activator="{ on }")
27
+ template(#activator)
28
28
  //- Input wrapper.
29
29
  .w-select__selection-wrap(
30
30
  @click="!isDisabled && !isReadonly && onInputFieldClick()"
@@ -43,16 +43,11 @@ component(
43
43
  slot(name="selection" :item="multiple ? inputValue : inputValue[0]")
44
44
  .w-select__selection(
45
45
  ref="selection-input"
46
- :contenteditable="isDisabled || isReadonly ? 'false' : 'true'"
47
46
  @focus="!isDisabled && !isReadonly && onFocus($event)"
48
47
  @blur="onBlur"
49
48
  @keydown="!isDisabled && !isReadonly && onKeydown($event)"
50
- :class="{ 'w-select__selection--placeholder': !$slots.selection && !selectionString && placeholder }"
51
- :disabled="isDisabled || null"
52
- readonly
53
- aria-readonly="true"
54
- :tabindex="tabindex || null"
55
- v-html="$slots.selection ? '' : selectionString || placeholder")
49
+ v-bind="selectionAttributes"
50
+ v-html="selectionHtml")
56
51
  //- For standard HTML form submission.
57
52
  input(
58
53
  v-for="(val, i) in (inputValue.length ? inputValue : [{}])"
@@ -76,7 +71,7 @@ component(
76
71
  @item-click="$emit('item-click', $event)"
77
72
  @item-select="onListItemSelect"
78
73
  @keydown:enter="noUnselect && !multiple && closeMenu()"
79
- @keydown:escape="showMenu && (this.showMenu = false) /* Will call closeMenu() from w-menu(@close). */"
74
+ @keydown:escape="showMenu && (showMenu = false) /* Will call closeMenu() from w-menu(@close). */"
80
75
  :items="selectItems"
81
76
  :multiple="multiple"
82
77
  arrows-navigation
@@ -87,7 +82,7 @@ component(
87
82
  :item-color-key="itemColorKey"
88
83
  role="listbox"
89
84
  tabindex="-1")
90
- template(v-for="i in items.length" v-slot:[`item.${i}`]="{ item, selected, index }")
85
+ template(v-for="i in items.length" #[`item.${i}`]="{ item, selected, index }")
91
86
  slot(
92
87
  v-if="$slots[`item.${i}`] && $slots[`item.${i}`](item, selected, index)"
93
88
  :name="`item.${i}`"
@@ -178,20 +173,32 @@ export default {
178
173
  return obj
179
174
  })
180
175
  },
181
- hasValue () {
182
- return Array.isArray(this.inputValue) ? this.inputValue.length : (this.inputValue !== null)
183
- },
184
176
  hasLabel () {
185
177
  return this.label || this.$slots.default
186
178
  },
187
179
  showLabelInside () {
188
- return !this.staticLabel || (!this.hasValue && !this.placeholder)
180
+ return !this.staticLabel || (!this.inputValue.length && !this.placeholder)
181
+ },
182
+ selectionAttributes () {
183
+ return {
184
+ class: { 'w-select__selection--placeholder': !this.$slots.selection && !this.selectionString && this.placeholder },
185
+ disabled: this.isDisabled || null,
186
+ readonly: true,
187
+ ariareadonly: 'true',
188
+ tabindex: this.tabindex ?? null,
189
+ contenteditable: this.isDisabled || this.isReadonly ? 'false' : 'true'
190
+ }
189
191
  },
190
192
  selectionString () {
191
- return this.inputValue && this.inputValue.map(
193
+ return this.inputValue.map(
192
194
  item => item[this.itemValueKey] !== undefined ? item[this.itemLabelKey] : (item[this.itemLabelKey] ?? item)
193
195
  ).join(', ')
194
196
  },
197
+ selectionHtml () {
198
+ if (!this.inputValue.length) return this.placeholder || ''
199
+ if (this.$slots.selection) return ''
200
+ return this.selectionString
201
+ },
195
202
  classes () {
196
203
  return {
197
204
  'w-select': true,
@@ -200,7 +207,7 @@ export default {
200
207
  'w-select--disabled': this.isDisabled,
201
208
  'w-select--fit-to-content': this.fitToContent,
202
209
  'w-select--readonly': this.isReadonly,
203
- [`w-select--${this.hasValue ? 'filled' : 'empty'}`]: true,
210
+ [`w-select--${this.inputValue.length ? 'filled' : 'empty'}`]: true,
204
211
  'w-select--focused': (this.isFocused || this.showMenu) && !this.isReadonly,
205
212
  'w-select--floating-label': this.hasLabel && this.labelPosition === 'inside' && !this.staticLabel,
206
213
  'w-select--no-padding': !this.outline && !this.bgColor && !this.shadow && !this.round,
@@ -298,7 +305,7 @@ export default {
298
305
  onInput (items) {
299
306
  this.inputValue = items === null ? [] : (this.multiple ? items : [items])
300
307
  // Return the original items when returnObject is true (no `value` if there wasn't),
301
- // or the the item value otherwise.
308
+ // or the item value otherwise.
302
309
  items = this.inputValue.map(item => this.returnObject ? this.items[item.index] : item.value)
303
310
 
304
311
  // Emit the selection to the v-model.
@@ -307,6 +314,7 @@ export default {
307
314
  this.$emit('update:modelValue', selection)
308
315
  this.$emit('input', selection)
309
316
  },
317
+
310
318
  onInputFieldClick () {
311
319
  if (this.showMenu) this.showMenu = false // Will call `closeMenu()` from w-menu(@close).
312
320
  else this.openMenu()
@@ -339,7 +347,7 @@ export default {
339
347
  return items.map(item => {
340
348
  let value = item
341
349
  if (item && typeof item === 'object') { // `null` is also an object!
342
- value = item[this.itemValueKey] !== undefined ? item[this.itemValueKey] : (item[this.itemLabelKey] !== undefined ? item[this.itemLabelKey] : item)
350
+ value = item[this.itemValueKey] ?? item[this.itemLabelKey] ?? item
343
351
  }
344
352
 
345
353
  return this.selectItems[allValues.indexOf(value)]
@@ -474,6 +482,9 @@ export default {
474
482
  align-items: center;
475
483
  cursor: pointer;
476
484
  caret-color: transparent;
485
+ border-radius: inherit;
486
+
487
+ &--placeholder {color: #888;}
477
488
 
478
489
  .w-select__selection-slot + & {
479
490
  position: absolute;
@@ -500,8 +511,6 @@ export default {
500
511
  }
501
512
 
502
513
  .w-select--readonly & {cursor: auto;}
503
-
504
- &--placeholder {color: #888;}
505
514
  }
506
515
 
507
516
  &__selection-slot {
@@ -563,14 +572,12 @@ export default {
563
572
 
564
573
  &__label--inside {
565
574
  position: absolute;
566
- top: 50%;
567
- left: 0;
568
- right: 0;
575
+ inset: 0 0 auto;
576
+ min-height: inherit;
569
577
  white-space: nowrap;
570
578
  // Use margin instead of padding as the scale transformation below decreases the real padding
571
579
  // size and misaligns the label.
572
580
  margin-left: 2 * $base-increment;
573
- transform: translateY(-50%);
574
581
  pointer-events: none;
575
582
 
576
583
  .w-select--inner-icon-right & {padding-right: 26px;}
@@ -594,21 +601,15 @@ export default {
594
601
  .w-select--open.w-select--floating-label &,
595
602
  .w-select--filled.w-select--floating-label &,
596
603
  .w-select--has-placeholder.w-select--floating-label & {
597
- transform: translateY(-160%) scale(0.85);
604
+ transform: translateY(-80%) scale(0.85);
598
605
  }
599
- // Chrome & Safari - Must remain in a separated rule as Firefox discard the whole rule seeing -webkit-.
606
+ // Chrome & Safari - Must stay a separated rule or Firefox discards the whole rule seeing -webkit-.
600
607
  .w-select--floating-label .w-select__select:-webkit-autofill & {
601
- transform: translateY(-160%) scale(0.85);
602
- }
603
- // Move label with outline style or with shadow.
604
- .w-select--open.w-select--floating-label .w-select__selection-wrap--box &,
605
- .w-select--filled.w-select--floating-label .w-select__selection-wrap--box &,
606
- .w-select--has-placeholder.w-select--floating-label .w-select__selection-wrap--box & {
607
- transform: translateY(-180%) scale(0.85);
608
+ transform: translateY(-80%) scale(0.85);
608
609
  }
609
610
  .w-select--open.w-select--floating-label.w-select--inner-icon-left &,
610
611
  .w-select--filled.w-select--floating-label.w-select--inner-icon-left & {left: 0;}
611
- // Chrome & Safari - Must remain in a separated rule as Firefox discard the whole rule seeing -webkit-.
612
+ // Chrome & Safari - Must stay a separated rule or Firefox discards the whole rule seeing -webkit-.
612
613
  .w-select--floating-label.w-select--inner-icon-left .w-select__select:-webkit-autofill & {left: 0;}
613
614
  }
614
615
 
@@ -42,7 +42,6 @@ component(
42
42
  template(v-if="labelPosition === 'inside' && showLabelInside")
43
43
  label.w-textarea__label.w-textarea__label--inside.w-form-el-shakable(
44
44
  v-if="$slots.default || label"
45
- :for="`w-textarea--${_.uid}`"
46
45
  :class="labelClasses")
47
46
  slot {{ label }}
48
47
  w-icon.w-textarea__icon.w-textarea__icon--inner-right(