wave-ui 2.34.0 → 2.36.1

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.
@@ -22,17 +22,25 @@ component(
22
22
  :aria-checked="isOn || 'false'"
23
23
  role="switch")
24
24
  template(v-if="hasLabel && labelOnLeft")
25
- label.w-switch__label.w-form-el-shakable(v-if="$slots.default" :for="`w-switch--${_.uid}`")
26
- slot
27
- label.w-switch__label.w-form-el-shakable(v-else-if="label" :for="`w-switch--${_.uid}`" v-html="label")
25
+ label.w-switch__label.w-switch__label--left.w-form-el-shakable(
26
+ v-if="$slots.default || label"
27
+ :for="`w-switch--${_.uid}`"
28
+ :class="labelClasses")
29
+ slot {{ label }}
28
30
  .w-switch__input(
29
31
  @click="$refs.input.focus();$refs.input.click()"
30
32
  v-on="$attrs"
31
33
  :class="inputClasses")
34
+ .w-switch__track(v-if="$slots.track")
35
+ slot(name="track")
36
+ .w-switch__thumb(v-if="$slots.thumb")
37
+ slot(name="thumb")
32
38
  template(v-if="hasLabel && !labelOnLeft")
33
- label.w-switch__label.w-form-el-shakable(v-if="$slots.default" :for="`w-switch--${_.uid}`")
34
- slot
35
- label.w-switch__label.w-form-el-shakable(v-else-if="label" :for="`w-switch--${_.uid}`" v-html="label")
39
+ label.w-switch__label.w-switch__label--right.w-form-el-shakable(
40
+ v-if="$slots.default || label"
41
+ :for="`w-switch--${_.uid}`"
42
+ :class="labelClasses")
43
+ slot {{ label }}
36
44
  </template>
37
45
 
38
46
  <script>
@@ -47,6 +55,7 @@ export default {
47
55
  label: { type: String, default: '' },
48
56
  labelOnLeft: { type: Boolean },
49
57
  color: { type: String, default: 'primary' },
58
+ labelColor: { type: String, default: 'primary' },
50
59
  thin: { type: Boolean },
51
60
  noRipple: { type: Boolean }
52
61
  // Props from mixin: name, disabled, readonly, required, tabindex, validators.
@@ -77,6 +86,8 @@ export default {
77
86
  'w-switch--disabled': this.isDisabled,
78
87
  'w-switch--readonly': this.isReadonly,
79
88
  'w-switch--ripple': this.ripple.start,
89
+ 'w-switch--custom-thumb': this.$slots.thumb,
90
+ 'w-switch--custom-track': this.$slots.track,
80
91
  'w-switch--rippled': this.ripple.end
81
92
  }
82
93
  },
@@ -186,8 +197,18 @@ $disabled-color: #ddd;
186
197
  }
187
198
  }
188
199
 
189
- // Thumb.
190
- &__input:after {
200
+ // Track slot, if any.
201
+ &__track {
202
+ position: absolute;
203
+ left: 100%;
204
+ padding: 0 4px;
205
+ transform: translateX(-100%);
206
+ @include default-transition;
207
+ }
208
+ .w-switch--on &__track {left: 0;transform: translateX(0);}
209
+
210
+ // Thumb: show either the thumb slot if any, or :after otherwise.
211
+ &__thumb, &__input:after {
191
212
  content: '';
192
213
  position: absolute;
193
214
  left: 0;
@@ -196,22 +217,28 @@ $disabled-color: #ddd;
196
217
  height: $small-form-el-size;
197
218
  background-color: #fff;
198
219
  border-radius: 100%;
220
+ text-align: center;
199
221
  @include default-transition;
200
222
 
201
223
  .w-switch[class^="bdrs"] &, .w-switch[class*=" bdrs"] & {border-radius: inherit;}
202
224
 
203
- :checked ~ & {transform: translateX(100%);}
225
+ .w-switch--on & {left: 100%;transform: translateX(-100%);}
204
226
 
205
227
  .w-switch--thin & {
206
228
  top: - round(0.15 * $small-form-el-size);
207
229
  transform: scale(1.1);
208
230
  box-shadow: $box-shadow;
209
231
  }
210
- .w-switch--thin :checked ~ & {
211
- transform: translateX(100%) scale(1.1);
232
+ .w-switch--thin.w-switch--on & {
233
+ transform: translateX(-100%) scale(1.1);
212
234
  background-color: currentColor;
213
235
  }
214
236
  }
237
+ &--custom-thumb &__input:after {display: none;}
238
+ &__thumb > * {
239
+ width: inherit;
240
+ height: inherit;
241
+ }
215
242
 
216
243
  // The focus outline & ripple on switch activation.
217
244
  &__input:before {
@@ -223,11 +250,12 @@ $disabled-color: #ddd;
223
250
  height: $small-form-el-size;
224
251
  background-color: currentColor;
225
252
  border-radius: 100%;
226
- transform: translateX(100%) scale(0);
227
253
  opacity: 0;
228
254
  pointer-events: none;
229
255
  transition: 0.25s ease-in-out;
230
256
 
257
+ :checked ~ & {transform: translateX(-100%) scale(0);left: 100%;}
258
+
231
259
  .w-switch[class^="bdrs"] &, .w-switch[class*=" bdrs"] & {border-radius: inherit;}
232
260
  .w-switch--thin & {top: - round(0.15 * $small-form-el-size);}
233
261
  }
@@ -242,14 +270,14 @@ $disabled-color: #ddd;
242
270
  opacity: 0.2;
243
271
  }
244
272
  :focus:checked ~ &__input:before {
245
- transform: translateX(100%) scale(1.8);
273
+ transform: translateX(-100%) scale(1.8);
246
274
  }
247
275
 
248
276
  // After ripple reset to default state, then remove the class via js and the
249
277
  // `:focus ~ &__input:before` will re-transition to normal focused outline.
250
278
  &--rippled &__input:before {
251
279
  transition: none;
252
- transform: translateX(100%) scale(0);
280
+ transform: translateX(-100%) scale(0);
253
281
  opacity: 0;
254
282
  }
255
283
 
@@ -264,7 +292,7 @@ $disabled-color: #ddd;
264
292
  }
265
293
 
266
294
  @keyframes w-switch-ripple {
267
- 0% {opacity: 0.8;transform: translateX(100%) scale(1);background-color: currentColor;} // Start with visible ripple.
268
- 100% {opacity: 0;transform: translateX(100%) scale(2.8);} // Propagate ripple to max radius and fade out.
295
+ 0% {opacity: 0.8;transform: translateX(-100%) scale(1);background-color: currentColor;} // Start with visible ripple.
296
+ 100% {opacity: 0;transform: translateX(-100%) scale(2.8);} // Propagate ripple to max radius and fade out.
269
297
  }
270
298
  </style>
@@ -9,9 +9,11 @@ component(
9
9
  :class="classes")
10
10
  //- Left label.
11
11
  template(v-if="labelPosition === 'left'")
12
- label.w-textarea__label.w-textarea__label--left.w-form-el-shakable(v-if="$slots.default" :for="`w-textarea--${_.uid}`")
13
- slot
14
- label.w-textarea__label.w-textarea__label--left.w-form-el-shakable(v-else-if="label" :for="`w-textarea--${_.uid}`" v-html="label")
12
+ label.w-textarea__label.w-textarea__label--left.w-form-el-shakable(
13
+ v-if="$slots.default || label"
14
+ :for="`w-textarea--${_.uid}`"
15
+ :class="labelClasses")
16
+ slot {{ label }}
15
17
 
16
18
  //- Input wrapper.
17
19
  .w-textarea__textarea-wrap(:class="inputWrapClasses")
@@ -39,15 +41,10 @@ component(
39
41
  :tabindex="tabindex || null")
40
42
  template(v-if="labelPosition === 'inside' && showLabelInside")
41
43
  label.w-textarea__label.w-textarea__label--inside.w-form-el-shakable(
42
- v-if="$slots.default"
43
- :for="`w-textarea--${_.uid}`"
44
- :class="isFocused && { [valid === false ? 'error' : this.color]: this.color || valid === false }")
45
- slot
46
- label.w-textarea__label.w-textarea__label--inside.w-form-el-shakable(
47
- v-else-if="label"
44
+ v-if="$slots.default || label"
48
45
  :for="`w-textarea--${_.uid}`"
49
- v-html="label"
50
- :class="isFocused && { [valid === false ? 'error' : color]: color || valid === false }")
46
+ :class="labelClasses")
47
+ slot {{ label }}
51
48
  w-icon.w-textarea__icon.w-textarea__icon--inner-right(
52
49
  v-if="innerIconRight"
53
50
  tag="label"
@@ -56,9 +53,11 @@ component(
56
53
 
57
54
  //- Right label.
58
55
  template(v-if="labelPosition === 'right'")
59
- label.w-textarea__label.w-textarea__label--right.w-form-el-shakable(v-if="$slots.default" :for="`w-textarea--${_.uid}`")
60
- slot
61
- label.w-textarea__label.w-textarea__label--right.w-form-el-shakable(v-else-if="label" :for="`w-textarea--${_.uid}`" v-html="label")
56
+ label.w-textarea__label.w-textarea__label--right.w-form-el-shakable(
57
+ v-if="$slots.default || label"
58
+ :for="`w-textarea--${_.uid}`"
59
+ :class="labelClasses")
60
+ slot {{ label }}
62
61
  </template>
63
62
 
64
63
  <script>
@@ -83,6 +82,7 @@ export default {
83
82
  placeholder: { type: String },
84
83
  color: { type: String, default: 'primary' },
85
84
  bgColor: { type: String },
85
+ labelColor: { type: String, default: 'primary' },
86
86
  dark: { type: Boolean },
87
87
  outline: { type: Boolean },
88
88
  shadow: { type: Boolean },
@@ -142,7 +142,7 @@ export default {
142
142
  },
143
143
  inputWrapClasses () {
144
144
  return {
145
- [this.valid === false ? 'error' : this.color]: this.color || this.valid === false,
145
+ [this.valid === false ? this.validationColor : this.color]: this.color || this.valid === false,
146
146
  [`${this.bgColor}--bg`]: this.bgColor,
147
147
  'w-textarea__textarea-wrap--tile': this.tile,
148
148
  // Box adds a padding on input. If there is a bgColor or shadow, a padding is needed.
@@ -387,8 +387,6 @@ $inactive-color: #777;
387
387
  .w-textarea--filled.w-textarea--floating-label.w-textarea--inner-icon-left & {left: 0;}
388
388
  // Chrome & Safari - Must remain in a separated rule as Firefox discard the whole rule seeing -webkit-.
389
389
  .w-textarea--floating-label.w-textarea--inner-icon-left .w-textarea__textarea:-webkit-autofill & {left: 0;}
390
-
391
- .w-textarea--focused & {color: currentColor;}
392
390
  }
393
391
  }
394
392
  </style>
@@ -1,26 +1,16 @@
1
1
  <template lang="pug">
2
- .w-tooltip-wrap
3
- slot(name="activator" :on="activatorEventHandlers")
4
- transition(:name="transitionName" appear)
5
- .w-tooltip(
6
- v-if="detachableVisible"
7
- ref="detachable"
8
- :key="_.uid"
9
- :class="classes"
10
- :style="styles")
11
- slot
2
+ slot(name="activator" :on="activatorEventHandlers")
3
+ transition(:name="transitionName" appear)
4
+ .w-tooltip(
5
+ v-if="detachableVisible"
6
+ ref="detachable"
7
+ :key="_.uid"
8
+ :class="classes"
9
+ :style="styles")
10
+ slot
12
11
  </template>
13
12
 
14
13
  <script>
15
- /**
16
- * Complexity of this component: Vue 2.x can only mount 1 single root element, but we don't
17
- * want to wrap the activator as it may break the layout.
18
- * Another simpler way would be to append the tooltip inside the activator, but some HTML tags
19
- * can't have children like <input>.
20
- * So a solution is to mount both the activator element and the tooltip in a wrapper then unwrap
21
- * and move the tooltip elsewhere in the DOM.
22
- */
23
-
24
14
  import { objectifyClasses } from '../utils/index'
25
15
  import DetachableMixin from '../mixins/detachable'
26
16
 
@@ -193,8 +183,6 @@ export default {
193
183
  </script>
194
184
 
195
185
  <style lang="scss">
196
- .w-tooltip-wrap {display: none;}
197
-
198
186
  .w-tooltip {
199
187
  // Fix Safari where `width: max-content` does not take padding and border into consideration.
200
188
  display: table;
@@ -9,7 +9,6 @@ import { consoleWarn } from '../utils/console'
9
9
  export default {
10
10
  props: {
11
11
  // Position.
12
- detachTo: { type: [String, Boolean, Object], deprecated: true },
13
12
  appendTo: { type: [String, Boolean, Object] },
14
13
  fixed: { type: Boolean },
15
14
  top: { type: Boolean },
@@ -26,6 +25,10 @@ export default {
26
25
  activator: { type: [String, Object, HTMLElement] } // The activator can be a DOM string selector, a ref or a DOM node.
27
26
  },
28
27
 
28
+ inject: {
29
+ detachableDefaultRoot: { default: null }
30
+ },
31
+
29
32
  data: () => ({
30
33
  // The event listeners handlers have to be removed the exact same way they have been attached.
31
34
  // Since the handler functions have variables that change after hot-reload, keep them exactly
@@ -39,14 +42,15 @@ export default {
39
42
  // DOM element to attach tooltip/menu to.
40
43
  // ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
41
44
  appendToTarget () {
42
- const defaultTarget = '.w-app'
45
+ let defaultTarget = '.w-app'
43
46
 
44
- // Convert deprecated prop to renamed one.
45
- if (this.detachTo && !this.appendTo) {
46
- consoleWarn(`The ${this.$options.name} prop \`detach-to\` is deprecated. You can replace it with \`append-to\`.`, this)
47
+ // If used inside a w-dialog, w-drawer, or w-menu without an appendTo, default to that open
48
+ // element instead of the w-app.
49
+ if (typeof this.detachableDefaultRoot === 'function') {
50
+ defaultTarget = this.detachableDefaultRoot() || defaultTarget
47
51
  }
48
52
 
49
- let target = this.appendTo || this.detachTo || defaultTarget
53
+ let target = this.appendTo || defaultTarget
50
54
  if (target === true) target = defaultTarget
51
55
  else if (this.appendTo === 'activator') target = this.$el.previousElementSibling
52
56
  else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
@@ -84,7 +88,7 @@ export default {
84
88
  if (activator instanceof HTMLElement) return activator
85
89
  return document.querySelector(this.activator)
86
90
  }
87
- return this.$el.firstElementChild
91
+ return this.$el.nextElementSibling
88
92
  },
89
93
  set () {}
90
94
  },
@@ -145,7 +149,7 @@ export default {
145
149
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
146
150
  getActivatorCoordinates () {
147
151
  // Get the activator coordinates relative to window.
148
- const { top, left, width, height } = (this.activatorEl).getBoundingClientRect()
152
+ const { top, left, width, height } = this.activatorEl.getBoundingClientRect()
149
153
  let coords = { top, left, width, height }
150
154
 
151
155
  // If absolute position, adjust top & left.
@@ -167,6 +171,11 @@ export default {
167
171
  // Get the activator coordinates.
168
172
  let { top, left, width, height } = this.getActivatorCoordinates()
169
173
 
174
+ // Prevent error in case the detachable component unmounted hook is fired but the activator
175
+ // is still in the DOM until the end of a transition and the user toggles it.
176
+ // Unmounted is called straight away from beforeLeave: https://github.com/vuejs/core/issues/994
177
+ if (!this.detachableEl) return
178
+
170
179
  // 1. First display the menu but hide it (So we can get its dimension).
171
180
  // --------------------------------------------------
172
181
  this.detachableEl.style.visibility = 'hidden'
@@ -273,7 +282,6 @@ export default {
273
282
  this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
274
283
 
275
284
  // Move the tooltip/menu elsewhere in the DOM.
276
- // wrapper.parentNode.insertBefore(this.detachableEl, wrapper)
277
285
  if (this.detachableEl) this.appendToTarget.appendChild(this.detachableEl)
278
286
  resolve()
279
287
  })
@@ -315,33 +323,25 @@ export default {
315
323
  },
316
324
 
317
325
  mounted () {
318
- const wrapper = this.$el
319
-
320
- // Unwrap the activator element if the activator is in the activator slot.
321
- if (this.$slots.activator) wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
322
-
323
326
  // If the activator is external.
324
- else if (this.activator) this.bindActivatorEvents()
327
+ if (this.activator) this.bindActivatorEvents()
325
328
 
326
329
  // If the activator seems to be undefined, it is probably a DOM node or Vue ref,
327
330
  // so check it on nextTick.
328
331
  else {
329
332
  this.$nextTick(() => {
330
- this.activator && this.bindActivatorEvents()
333
+ if (this.activator) this.bindActivatorEvents()
331
334
  if (this.modelValue) this.toggle({ type: 'click', target: this.activatorEl })
332
335
  })
333
336
  }
334
337
 
335
338
  // Unwrap the overlay if any.
336
- if (this.overlay) {
337
- this.overlayEl = this.$refs.overlay?.$el
338
- wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
339
- }
339
+ if (this.overlay) this.overlayEl = this.$refs.overlay?.$el
340
340
 
341
341
  if (this.modelValue && this.activator) this.toggle({ type: 'click', target: this.activatorEl })
342
342
  },
343
343
 
344
- beforeUnmount () {
344
+ unmounted () {
345
345
  this.close()
346
346
 
347
347
  this.removeFromDOM()
@@ -353,19 +353,12 @@ export default {
353
353
  document.removeEventListener(eventName, handler)
354
354
  })
355
355
  }
356
-
357
- if (this.overlay && this.overlayEl.parentNode) this.overlayEl.remove()
358
- if (this.activatorEl?.parentNode && this.$slots.activator) this.activatorEl.remove()
359
356
  },
360
357
 
361
358
  watch: {
362
359
  modelValue (bool) {
363
360
  if (!!bool !== this.detachableVisible) this.toggle({ type: 'click', target: this.activatorEl })
364
361
  },
365
- detachTo () {
366
- this.removeFromDOM()
367
- this.insertInDOM()
368
- },
369
362
  appendTo () {
370
363
  this.removeFromDOM()
371
364
  this.insertInDOM()
@@ -29,6 +29,15 @@ export default {
29
29
  },
30
30
  isReadonly () {
31
31
  return this.readonly || this.formProps.readonly
32
+ },
33
+ validationColor () {
34
+ return this.formProps.validationColor
35
+ },
36
+ labelClasses () {
37
+ return {
38
+ [this.labelColor]: this.labelColor && this.valid !== false,
39
+ [this.validationColor]: this.valid === false
40
+ }
32
41
  }
33
42
  },
34
43