@energie360/ui-library 0.1.23 → 0.1.24

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 (36) hide show
  1. package/components/badge/u-badge.vue +2 -2
  2. package/components/card-amount/card-amount.scss +7 -0
  3. package/components/card-amount/u-card-amount.vue +21 -0
  4. package/components/card-amount-illustrated/card-amount-illustrated.scss +35 -0
  5. package/components/card-amount-illustrated/u-card-amount-illustrated.vue +80 -0
  6. package/components/card-contact/card-contact.scss +5 -3
  7. package/components/card-contact/u-card-contact.vue +15 -10
  8. package/components/card-footer/u-card-footer.vue +6 -2
  9. package/components/card-group/card-group.scss +25 -11
  10. package/components/card-group/u-card-group.vue +59 -4
  11. package/components/chip/chip.scss +31 -4
  12. package/components/chip/u-chip.vue +5 -2
  13. package/components/index.js +4 -0
  14. package/components/progress-bar/progress-bar.scss +5 -9
  15. package/components/progress-bar/u-progress-bar.vue +3 -6
  16. package/components/richtext/richtext.scss +2 -0
  17. package/components/skeleton-loader/u-skeleton-loader.vue +1 -1
  18. package/components/slider/slider.scss +1 -0
  19. package/components/slider/u-slider.vue +26 -15
  20. package/components/slider-progress-animation/slider-progress-animation.scss +25 -0
  21. package/components/slider-progress-animation/u-slider-progress-animation.vue +65 -0
  22. package/components/sprite-animation/sprite-animation.scss +7 -0
  23. package/components/sprite-animation/u-sprite-animation.vue +169 -0
  24. package/components/table/cell-progress-bar.scss +4 -0
  25. package/components/table/u-cell-icon-group.vue +12 -7
  26. package/components/table/u-cell-progress-bar.vue +5 -5
  27. package/components/text-block/text-block.scss +18 -14
  28. package/components/text-block/u-text-block.vue +17 -11
  29. package/elements/index.js +0 -1
  30. package/elements/select-chip/select-chip.scss +1 -1
  31. package/elements/select-chip/u-select-chip.vue +7 -6
  32. package/package.json +3 -1
  33. package/utils/functions/animate.js +258 -0
  34. package/wizard/wizard-layout/u-wizard-layout-block.vue +2 -2
  35. package/wizard/wizard-top-bar/u-wizard-top-bar.vue +0 -8
  36. package/elements/range-slider/u-range-slider.vue +0 -138
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- interface Props {
2
+ export interface Badge {
3
3
  color?: string
4
4
  dot?: boolean
5
5
  type?: 'default' | 'success' | 'error' | 'warning' | 'info'
@@ -19,7 +19,7 @@ const {
19
19
  color = 'var(--e-c-mono-900)',
20
20
  type = 'default',
21
21
  show = true,
22
- } = defineProps<Props>()
22
+ } = defineProps<Badge>()
23
23
  </script>
24
24
 
25
25
  <template>
@@ -0,0 +1,7 @@
1
+ @use '../../base/abstracts' as a;
2
+
3
+ .card-amount__text {
4
+ @include a.type(700, strong);
5
+
6
+ margin-bottom: var(--e-space-2);
7
+ }
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { UProgressBar } from '../'
3
+
4
+ interface Props {
5
+ progress: number
6
+ text: string
7
+ }
8
+
9
+ defineProps<Props>()
10
+ </script>
11
+
12
+ <template>
13
+ <div class="card-amount">
14
+ <div class="card-amount__text">
15
+ {{ text }}
16
+ </div>
17
+ <UProgressBar :value="progress"></UProgressBar>
18
+ </div>
19
+ </template>
20
+
21
+ <style scoped lang="scss" src="./card-amount.scss"></style>
@@ -0,0 +1,35 @@
1
+ @use '../../base/abstracts' as a;
2
+
3
+ .card-amount-illustrated {
4
+ display: flex;
5
+ flex-direction: column;
6
+ row-gap: var(--e-space-3);
7
+ }
8
+
9
+ .card-amount-illustrated__title {
10
+ @include a.type(300, strong);
11
+ }
12
+
13
+ .card-amount-illustrated__amount {
14
+ padding: a.rem(22) 0;
15
+ }
16
+
17
+ .card-amount-illustrated__illustration {
18
+ position: relative;
19
+ width: a.rem(200);
20
+ height: a.rem(200);
21
+ align-self: center;
22
+ }
23
+
24
+ .card-amount-illustrated__max-amount-image {
25
+ position: absolute;
26
+ top: 0;
27
+ right: 0;
28
+ aspect-ratio: 5/2;
29
+ width: 85px;
30
+ }
31
+
32
+ .card-amount-illustrated__slider {
33
+ margin-top: -2px;
34
+ margin-bottom: -2px;
35
+ }
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ import { SpriteAnimation } from '../sprite-animation/u-sprite-animation.vue'
3
+ import { UProgressBar, USpriteAnimation } from '../'
4
+ import { useTemplateRef } from 'vue'
5
+ import { Image } from '../../elements/types'
6
+
7
+ interface Props {
8
+ title?: string
9
+ amount?: number
10
+ maxAmountImage: Image
11
+ spriteAnimation?: Pick<
12
+ SpriteAnimation,
13
+ 'spritesheetPath' | 'framePositions' | 'frameHeight' | 'frameWidth'
14
+ >
15
+ }
16
+
17
+ defineProps<Props>()
18
+ defineEmits(['amount'])
19
+
20
+ const spriteAnimRef = useTemplateRef('spriteAnim')
21
+ let pausedFrame: number
22
+
23
+ const onPause = (frame: number) => {
24
+ pausedFrame = frame
25
+ }
26
+
27
+ const onMouseenter = () => {
28
+ spriteAnimRef.value.play()
29
+ }
30
+
31
+ const onMouseleave = () => {
32
+ spriteAnimRef.value.pause()
33
+ spriteAnimRef.value.playReverse(pausedFrame)
34
+ }
35
+ </script>
36
+
37
+ <template>
38
+ <div class="card-amount-illustrated">
39
+ <h3 class="card-amount-illustrated__title">
40
+ <slot name="title">{{ title }}</slot>
41
+ </h3>
42
+
43
+ <div
44
+ v-if="$slots.illustration || spriteAnimation"
45
+ class="card-amount-illustrated__illustration"
46
+ >
47
+ <slot name="illustration">
48
+ <USpriteAnimation
49
+ ref="spriteAnim"
50
+ v-bind="spriteAnimation"
51
+ :duration="200"
52
+ :width="200"
53
+ :height="200"
54
+ @mouseenter="onMouseenter"
55
+ @mouseleave="onMouseleave"
56
+ @pause="onPause"
57
+ ></USpriteAnimation>
58
+ </slot>
59
+
60
+ <div
61
+ v-if="($slots.maxAmountImage || maxAmountImage) && amount === 100"
62
+ class="card-amount-illustrated__max-amount-image"
63
+ >
64
+ <slot name="maxAmountImage">
65
+ <img :src="maxAmountImage.src" :alt="maxAmountImage.alt" />
66
+ </slot>
67
+ </div>
68
+ </div>
69
+
70
+ <div v-if="amount && !$slots.slider" class="card-amount-illustrated__amount">
71
+ <UProgressBar :value="amount"></UProgressBar>
72
+ </div>
73
+
74
+ <div v-if="$slots.slider" class="card-amount-illustrated__slider">
75
+ <slot name="slider"></slot>
76
+ </div>
77
+ </div>
78
+ </template>
79
+
80
+ <style scoped lang="scss" src="./card-amount-illustrated.scss"></style>
@@ -21,11 +21,13 @@
21
21
  @include a.type(200);
22
22
 
23
23
  margin-top: var(--e-space-1);
24
- }
25
24
 
26
- .card-contact__tel {
27
- @include t.text-link;
25
+ &:has(+ .card-contact__contacts) {
26
+ margin-bottom: var(--e-space-4);
27
+ }
28
+ }
28
29
 
30
+ .card-contact__contacts {
29
31
  margin-top: auto;
30
32
  }
31
33
 
@@ -1,16 +1,21 @@
1
1
  <script setup lang="ts">
2
- import { Image, Cta } from '../../elements/types'
3
-
4
- type Tel = Omit<Cta, 'target'>
2
+ import { Image } from '../../elements/types'
3
+ import { URichtext } from '../'
5
4
 
6
5
  interface Props {
7
6
  name?: string
8
7
  role?: string
9
- tel?: Tel
10
8
  image?: Image
9
+ contacts?: string
11
10
  }
12
11
 
13
12
  defineProps<Props>()
13
+ defineSlots<{
14
+ name()
15
+ role()
16
+ contacts()
17
+ image()
18
+ }>()
14
19
  </script>
15
20
 
16
21
  <template>
@@ -24,12 +29,12 @@ defineProps<Props>()
24
29
  <slot name="role">{{ role }}</slot>
25
30
  </p>
26
31
 
27
- <div v-if="$slots.tel || tel" class="card-contact__tel">
28
- <slot name="tel">
29
- <a :href="tel.href">
30
- {{ tel.label }}
31
- </a>
32
- </slot>
32
+ <div v-if="$slots.contacts || contacts" class="card-contact__contacts">
33
+ <URichtext>
34
+ <slot name="contacts">
35
+ <div v-html="contacts"></div>
36
+ </slot>
37
+ </URichtext>
33
38
  </div>
34
39
  </div>
35
40
 
@@ -13,6 +13,7 @@ interface Radio {
13
13
  label: string
14
14
  name?: string
15
15
  value?: string
16
+ disabled?: boolean
16
17
  }
17
18
 
18
19
  interface Props {
@@ -21,21 +22,24 @@ interface Props {
21
22
 
22
23
  const { cta = undefined } = defineProps<Props>()
23
24
 
25
+ const emits = defineEmits(['change'])
26
+
24
27
  const { toggleActiveCard } = inject('card')
25
28
  const { currentValue } = inject('card-group', {})
26
29
 
27
30
  const onChange = () => {
28
31
  toggleActiveCard(true)
32
+ emits('change', cta.value)
29
33
  }
30
34
 
31
35
  if (currentValue) {
32
36
  watch(currentValue, (newV) => {
33
- toggleActiveCard(newV === cta.value)
37
+ toggleActiveCard(newV === cta.value && !cta.disabled)
34
38
  })
35
39
 
36
40
  onMounted(() => {
37
41
  if (currentValue?.value) {
38
- toggleActiveCard(currentValue.value === cta.value)
42
+ toggleActiveCard(currentValue.value === cta.value && !cta.disabled)
39
43
  }
40
44
  })
41
45
  }
@@ -1,25 +1,39 @@
1
- @use '../../base/abstracts/' as a;
1
+ @use '../../base/abstracts' as a;
2
2
 
3
3
  .card-group {
4
4
  display: grid;
5
5
  gap: var(--e-space-5);
6
6
 
7
- // >= 3 card items
8
- grid-template-columns: repeat(3, 1fr);
9
- grid-template-rows: 1fr;
10
-
11
- // 1 card item
12
- &:has(> :last-child:nth-child(1)) {
13
- grid-template-columns: repeat(1, 1fr);
7
+ // Column behaviours
8
+ &.columns-auto {
9
+ // >= 3 card items
10
+ grid-template-columns: repeat(3, 1fr);
14
11
  grid-template-rows: 1fr;
12
+
13
+ // 1 card item
14
+ &:has(> :last-child:nth-child(1)) {
15
+ grid-template-columns: repeat(1, 1fr);
16
+ grid-template-rows: 1fr;
17
+ }
18
+
19
+ // 2 card items
20
+ &:has(> :last-child:nth-child(2)) {
21
+ grid-template-columns: repeat(2, 1fr);
22
+ grid-template-rows: 1fr;
23
+ }
15
24
  }
16
25
 
17
- // 2 card items
18
- &:has(> :last-child:nth-child(2)) {
19
- grid-template-columns: repeat(2, 1fr);
26
+ &.columns-4 {
27
+ grid-template-columns: repeat(4, 1fr);
20
28
  grid-template-rows: 1fr;
21
29
  }
22
30
 
31
+ &.use-carousel {
32
+ display: block;
33
+
34
+ // touch-action: pan-y;
35
+ }
36
+
23
37
  @include a.bp(lg) {
24
38
  &:has(> :last-child:nth-child(n)) {
25
39
  grid-template-columns: repeat(1, 1fr);
@@ -1,13 +1,48 @@
1
1
  <script setup lang="ts">
2
- import { onMounted, onUnmounted, useTemplateRef } from 'vue'
2
+ import { register } from 'swiper/element/bundle'
3
+ import { onMounted, onUnmounted, useTemplateRef, watch } from 'vue'
3
4
  import { debounceRaf } from '../../utils/functions/debounce'
4
5
  import { isLarge } from '../../utils/functions/breakpoint'
5
6
  import { useRadioGroup } from '../../elements/radio-group/radio-group-composables'
6
7
 
7
- const model = defineModel<string>()
8
+ // Register swiper custom elements.
9
+ register()
10
+
11
+ interface Props {
12
+ columns?: 1 | 2 | 3 | 4 | 'auto'
13
+ useCarousel?: boolean
14
+ }
8
15
 
16
+ const { columns = 'auto', useCarousel = false } = defineProps<Props>()
17
+ const model = defineModel<string>()
9
18
  const group = useTemplateRef('group')
10
19
 
20
+ const initSwiper = () => {
21
+ if (!useCarousel) {
22
+ return
23
+ }
24
+
25
+ const swiperEl = group.value.querySelector('swiper-container')
26
+ const swiperParams = {
27
+ spaceBetween: 20,
28
+ breakpoints: {
29
+ 1240: {
30
+ slidesPerView: 4,
31
+ },
32
+ 1020: {
33
+ slidesPerView: 3,
34
+ },
35
+ 740: {
36
+ slidesPerView: 2,
37
+ },
38
+ },
39
+ }
40
+
41
+ Object.assign(swiperEl, swiperParams)
42
+
43
+ swiperEl.initialize()
44
+ }
45
+
11
46
  const onResize = debounceRaf(() => {
12
47
  const headers = Array.from(group.value.querySelectorAll('.card-header'))
13
48
 
@@ -45,6 +80,8 @@ onMounted(() => {
45
80
  // Initial state
46
81
  onResize()
47
82
  })
83
+
84
+ initSwiper()
48
85
  })
49
86
 
50
87
  onUnmounted(() => {
@@ -55,11 +92,29 @@ useRadioGroup({
55
92
  model,
56
93
  provideKey: 'card-group',
57
94
  })
95
+
96
+ watch(
97
+ () => useCarousel,
98
+ () => {
99
+ initSwiper()
100
+ },
101
+ )
58
102
  </script>
59
103
 
60
104
  <template>
61
- <div ref="group" class="card-group">
62
- <slot></slot>
105
+ <div ref="group" :class="['card-group', `columns-${columns}`, { 'use-carousel': useCarousel }]">
106
+ <template v-if="useCarousel">
107
+ <swiper-container init="false">
108
+ <template v-for="(vnode, idx) in $slots.default()[0].children" :key="idx">
109
+ <swiper-slide>
110
+ <component :is="vnode" />
111
+ </swiper-slide>
112
+ </template>
113
+ </swiper-container>
114
+ </template>
115
+ <template v-else>
116
+ <slot />
117
+ </template>
63
118
  </div>
64
119
  </template>
65
120
 
@@ -1,25 +1,52 @@
1
1
  @use '../../base/abstracts' as a;
2
2
 
3
3
  .chip {
4
- background-color: var(--e-c-mono-50);
5
- border: 1px solid var(--e-c-mono-200);
4
+ --chip-background-color: var(--e-c-mono-50);
5
+ --chip-font-color: var(--e-c-mono-900);
6
+ --chip-caption-color: var(--e-c-mono-700);
7
+ --chip-border-color: var(--e-c-mono-200);
8
+ --chip-icon-color: var(--e-c-mono-700);
9
+
10
+ background-color: var(--chip-background-color);
11
+ border: 1px solid var(--chip-border-color);
6
12
  border-radius: var(--e-brd-radius-2);
7
13
  padding: var(--e-space-1_5) var(--e-space-3);
8
14
  display: inline-flex;
9
15
  align-items: center;
16
+ justify-content: space-between;
10
17
  column-gap: var(--e-space-1);
18
+
19
+ &.chip--icon-right {
20
+ .chip__icon {
21
+ order: 2;
22
+ }
23
+
24
+ .chip__label {
25
+ order: 1;
26
+ }
27
+ }
28
+
29
+ &.full-width {
30
+ width: 100%;
31
+ }
32
+
33
+ &.big {
34
+ padding: a.rem(16);
35
+ }
11
36
  }
12
37
 
13
38
  .chip__icon {
14
- color: var(--e-c-mono-700);
39
+ color: var(--chip-icon-color);
15
40
  }
16
41
 
17
42
  .chip__label {
18
43
  @include a.type(200, strong);
44
+
45
+ color: var(--chip-font-color);
19
46
  }
20
47
 
21
48
  .chip__caption {
22
49
  @include a.type(50, strong);
23
50
 
24
- color: var(--e-c-mono-700);
51
+ color: var(--chip-caption-color);
25
52
  }
@@ -5,13 +5,16 @@ export interface Chip {
5
5
  caption?: string
6
6
  label?: string
7
7
  icon?: string
8
+ iconPosition?: 'left' | 'right'
9
+ fullWidth?: boolean
10
+ big?: boolean
8
11
  }
9
12
 
10
- defineProps<Chip>()
13
+ const { caption = '', label = '', icon = undefined, iconPosition = 'left' } = defineProps<Chip>()
11
14
  </script>
12
15
 
13
16
  <template>
14
- <div class="chip">
17
+ <div :class="['chip', `chip--icon-${iconPosition}`, { 'full-width': fullWidth, big }]">
15
18
  <span v-if="icon" class="chip__icon">
16
19
  <UIcon :name="icon"></UIcon>
17
20
  </span>
@@ -14,6 +14,8 @@ export { default as UCardCtaHeader } from './card-cta-header/u-card-cta-header.v
14
14
  export { default as UCardCtaBar } from './card-cta-bar/u-card-cta-bar.vue'
15
15
  export { default as UCardContact } from './card-contact/u-card-contact.vue'
16
16
  export { default as UCardPriceList } from './card-price-list/u-card-price-list.vue'
17
+ export { default as UCardAmountIllustrated } from './card-amount-illustrated/u-card-amount-illustrated.vue'
18
+ export { default as UCardAmount } from './card-amount/u-card-amount.vue'
17
19
 
18
20
  // Collapsible
19
21
  export { default as UCollapsible } from './collapsible/u-collapsible.vue'
@@ -63,3 +65,5 @@ export { default as UCardHighlight } from './card-highlight/u-card-highlight.vue
63
65
  export { default as URating } from './rating/u-rating.vue'
64
66
  export { default as UChip } from './chip/u-chip.vue'
65
67
  export { default as USlider } from './slider/u-slider.vue'
68
+ export { default as USpriteAnimation } from './sprite-animation/u-sprite-animation.vue'
69
+ export { default as USliderProgressAnimation } from './slider-progress-animation/u-slider-progress-animation.vue'
@@ -5,22 +5,18 @@
5
5
  align-items: center;
6
6
  column-gap: a.rem(4);
7
7
  height: a.rem(10);
8
- width: #{a.rem(64 + 4 + 10)}; // progress-track + space + icon
8
+ width: 100%;
9
9
 
10
- &.full-width {
11
- width: 100%;
12
-
13
- .progress-track {
14
- width: calc(100% - #{a.rem(4 + 10)});
15
- }
10
+ .progress-track {
11
+ width: calc(100% - #{a.rem(4 + 10)});
16
12
  }
17
13
  }
18
14
 
19
15
  .progress-track {
20
- width: a.rem(64);
16
+ width: calc(100% - #{a.rem(4 + 10)});
21
17
  min-width: a.rem(64);
22
18
  height: a.rem(4);
23
- background-color: var(--e-c-mono-100);
19
+ background-color: var(--e-c-primary-01-100);
24
20
  border-radius: a.rem(4);
25
21
  overflow: hidden;
26
22
  }
@@ -1,14 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  interface Props {
3
3
  value: number
4
- fullWidth?: boolean
5
4
  }
6
5
 
7
- const { fullWidth = false } = defineProps<Props>()
6
+ defineProps<Props>()
8
7
  </script>
9
8
 
10
9
  <template>
11
- <div :class="['progress-bar', { 'full-width': fullWidth }]">
10
+ <div class="progress-bar">
12
11
  <div class="progress-track">
13
12
  <div class="progress-position" :style="{ width: `${value}%` }"></div>
14
13
  </div>
@@ -16,6 +15,4 @@ const { fullWidth = false } = defineProps<Props>()
16
15
  </div>
17
16
  </template>
18
17
 
19
- <style scoped lang="scss">
20
- @use './progress-bar.scss';
21
- </style>
18
+ <style scoped lang="scss" src="./progress-bar.scss"></style>
@@ -88,6 +88,8 @@
88
88
  transition:
89
89
  text-decoration-color a.$trs-default,
90
90
  color a.$trs-default;
91
+ -webkit-font-smoothing: antialiased;
92
+ -moz-osx-font-smoothing: grayscale;
91
93
 
92
94
  &:hover {
93
95
  text-decoration-color: var(--e-c-primary-01-50);
@@ -21,7 +21,7 @@ const { type = SkeletonType.fit } = defineProps<Props>()
21
21
  </template>
22
22
 
23
23
  <template v-if="type === SkeletonType.fit">
24
- <div class="skeleton-loader fit"></div>
24
+ <div class="skeleton-loader fit" v-bind="$attrs"></div>
25
25
  </template>
26
26
  </template>
27
27
 
@@ -188,6 +188,7 @@
188
188
  width: a.rem(16);
189
189
  height: a.rem(16);
190
190
  transform: translateX(-50%);
191
+ box-sizing: border-box;
191
192
  }
192
193
 
193
194
  .slider__tooltip-placeholder {
@@ -9,8 +9,7 @@ interface SliderDot {
9
9
  isHighlighted?: boolean
10
10
  }
11
11
 
12
- // TODO: I get an error with importing interface RangeSlider and extending it here. No idea why this happens...
13
- interface Props {
12
+ export interface Slider {
14
13
  name: string
15
14
  dots?: SliderDot[]
16
15
  label: string
@@ -29,7 +28,7 @@ const {
29
28
  step = 1,
30
29
  valuePrefix = '',
31
30
  valueSuffix = '',
32
- } = defineProps<Props>()
31
+ } = defineProps<Slider>()
33
32
 
34
33
  const model = defineModel<number>()
35
34
  const emits = defineEmits(['change'])
@@ -42,6 +41,9 @@ const controlEl = useTemplateRef('control')
42
41
 
43
42
  const getValueString = (v) => `${valuePrefix}${v}${valueSuffix}`
44
43
 
44
+ const snapNumberToGrid = (n: number, grid: number, offset: number = 0) =>
45
+ Math.round(n / grid) * grid + offset
46
+
45
47
  const getLeftPosition = (v) => scaleValue(Number(v), Number(min), Number(max), 0, 100, 2)
46
48
  const setThumbPosition = () => {
47
49
  const position = getLeftPosition(model.value)
@@ -50,10 +52,6 @@ const setThumbPosition = () => {
50
52
  lowerFillEl.value.style.width = `${position}%`
51
53
  }
52
54
 
53
- const setModelValueFromLeftPosition = (leftPos) => {
54
- model.value = scaleValue(leftPos, 0, 100, min, max, 0)
55
- }
56
-
57
55
  onMounted(() => {
58
56
  setThumbPosition()
59
57
  })
@@ -68,32 +66,45 @@ const onPointerdown = (e: PointerEvent) => {
68
66
  return
69
67
  }
70
68
 
69
+ e.preventDefault()
70
+ e.stopPropagation()
71
+
71
72
  isSliding.value = true
72
73
 
73
- // Get correct left position
74
- const leftPos = Math.round(
74
+ const leftPos = clamp(
75
75
  ((e.clientX - controlEl.value.getBoundingClientRect().left) / controlEl.value.offsetWidth) *
76
76
  100,
77
+ 0,
78
+ 100,
79
+ )
80
+
81
+ model.value = clamp(
82
+ snapNumberToGrid(scaleValue(leftPos, 0, 100, min, max, 2), step, min % step),
83
+ min,
84
+ max,
77
85
  )
78
86
 
79
87
  isTouch.value = e.pointerType !== 'mouse'
80
88
 
81
- setModelValueFromLeftPosition(leftPos)
82
89
  controlEl.value.addEventListener('pointermove', onPointermove)
83
90
  document.addEventListener('pointerup', onPointerup)
84
91
  }
85
92
  const onPointermove = (e: PointerEvent) => {
86
93
  e.preventDefault()
94
+ e.stopPropagation()
87
95
 
88
- // Get correct left position
89
- let leftPos = Math.round(
96
+ const leftPos = clamp(
90
97
  ((e.clientX - controlEl.value.getBoundingClientRect().left) / controlEl.value.offsetWidth) *
91
98
  100,
99
+ 0,
100
+ 100,
92
101
  )
93
102
 
94
- leftPos = clamp(leftPos, 0, 100)
95
-
96
- setModelValueFromLeftPosition(leftPos)
103
+ model.value = clamp(
104
+ snapNumberToGrid(scaleValue(leftPos, 0, 100, min, max, 2), step, min % step),
105
+ min,
106
+ max,
107
+ )
97
108
  }
98
109
  const onPointerup = () => {
99
110
  isSliding.value = false