quasar 2.3.4 → 2.4.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.
Files changed (204) hide show
  1. package/dist/api/Loading.json +2 -6
  2. package/dist/api/QBreadcrumbsEl.json +31 -10
  3. package/dist/api/QBtn.json +30 -14
  4. package/dist/api/QBtnDropdown.json +30 -14
  5. package/dist/api/QBtnToggle.json +3 -0
  6. package/dist/api/QChatMessage.json +4 -12
  7. package/dist/api/QExpansionItem.json +31 -10
  8. package/dist/api/QFab.json +30 -0
  9. package/dist/api/QFabAction.json +8 -0
  10. package/dist/api/QField.json +1 -0
  11. package/dist/api/QFile.json +1 -0
  12. package/dist/api/QInput.json +1 -0
  13. package/dist/api/QItem.json +31 -10
  14. package/dist/api/QOptionGroup.json +74 -4
  15. package/dist/api/QRange.json +592 -107
  16. package/dist/api/QRouteTab.json +31 -11
  17. package/dist/api/QSelect.json +3 -6
  18. package/dist/api/QSlider.json +504 -39
  19. package/dist/api/QUploader.json +16 -2
  20. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  21. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  22. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  23. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  24. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  25. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  26. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  27. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  28. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  29. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  30. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  31. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  32. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  33. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  34. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  35. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  36. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  37. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  38. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  39. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  40. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  41. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
  42. package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
  43. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
  44. package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
  45. package/dist/icon-set/svg-mdi-v4.umd.prod.js +1 -1
  46. package/dist/icon-set/svg-mdi-v5.umd.prod.js +1 -1
  47. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  48. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  49. package/dist/icon-set/themify.umd.prod.js +1 -1
  50. package/dist/lang/ar.umd.prod.js +1 -1
  51. package/dist/lang/az-Latn.umd.prod.js +1 -1
  52. package/dist/lang/bg.umd.prod.js +1 -1
  53. package/dist/lang/bn.umd.prod.js +1 -1
  54. package/dist/lang/ca.umd.prod.js +1 -1
  55. package/dist/lang/cs.umd.prod.js +1 -1
  56. package/dist/lang/da.umd.prod.js +1 -1
  57. package/dist/lang/de.umd.prod.js +1 -1
  58. package/dist/lang/el.umd.prod.js +1 -1
  59. package/dist/lang/en-GB.umd.prod.js +1 -1
  60. package/dist/lang/en-US.umd.prod.js +1 -1
  61. package/dist/lang/eo.umd.prod.js +1 -1
  62. package/dist/lang/es.umd.prod.js +1 -1
  63. package/dist/lang/et.umd.prod.js +1 -1
  64. package/dist/lang/fa-IR.umd.prod.js +1 -1
  65. package/dist/lang/fa.umd.prod.js +1 -1
  66. package/dist/lang/fi.umd.prod.js +1 -1
  67. package/dist/lang/fr.umd.prod.js +1 -1
  68. package/dist/lang/gn.umd.prod.js +1 -1
  69. package/dist/lang/he.umd.prod.js +1 -1
  70. package/dist/lang/hr.umd.prod.js +1 -1
  71. package/dist/lang/hu.umd.prod.js +1 -1
  72. package/dist/lang/id.umd.prod.js +1 -1
  73. package/dist/lang/is.umd.prod.js +1 -1
  74. package/dist/lang/it.umd.prod.js +1 -1
  75. package/dist/lang/ja.umd.prod.js +1 -1
  76. package/dist/lang/km.umd.prod.js +1 -1
  77. package/dist/lang/ko-KR.umd.prod.js +1 -1
  78. package/dist/lang/kur-CKB.umd.prod.js +1 -1
  79. package/dist/lang/lt.umd.prod.js +1 -1
  80. package/dist/lang/lu.umd.prod.js +1 -1
  81. package/dist/lang/lv.umd.prod.js +1 -1
  82. package/dist/lang/ml.umd.prod.js +1 -1
  83. package/dist/lang/ms.umd.prod.js +1 -1
  84. package/dist/lang/nb-NO.umd.prod.js +1 -1
  85. package/dist/lang/nl.umd.prod.js +1 -1
  86. package/dist/lang/pl.umd.prod.js +1 -1
  87. package/dist/lang/pt-BR.umd.prod.js +1 -1
  88. package/dist/lang/pt.umd.prod.js +1 -1
  89. package/dist/lang/ro.umd.prod.js +1 -1
  90. package/dist/lang/ru.umd.prod.js +1 -1
  91. package/dist/lang/sk.umd.prod.js +1 -1
  92. package/dist/lang/sl.umd.prod.js +1 -1
  93. package/dist/lang/sr-CYR.umd.prod.js +1 -1
  94. package/dist/lang/sr.umd.prod.js +1 -1
  95. package/dist/lang/sv.umd.prod.js +1 -1
  96. package/dist/lang/ta.umd.prod.js +1 -1
  97. package/dist/lang/th.umd.prod.js +1 -1
  98. package/dist/lang/tr.umd.prod.js +1 -1
  99. package/dist/lang/ug.umd.prod.js +1 -1
  100. package/dist/lang/uk.umd.prod.js +1 -1
  101. package/dist/lang/vi.umd.prod.js +1 -1
  102. package/dist/lang/zh-CN.umd.prod.js +1 -1
  103. package/dist/lang/zh-TW.umd.prod.js +1 -1
  104. package/dist/quasar.cjs.prod.js +2 -2
  105. package/dist/quasar.css +264 -183
  106. package/dist/quasar.esm.prod.js +2 -2
  107. package/dist/quasar.prod.css +1 -1
  108. package/dist/quasar.rtl.css +331 -262
  109. package/dist/quasar.rtl.prod.css +1 -1
  110. package/dist/quasar.sass +235 -178
  111. package/dist/quasar.umd.js +16537 -16226
  112. package/dist/quasar.umd.prod.js +2 -2
  113. package/dist/ssr-directives/Morph.js +1 -1
  114. package/dist/transforms/loader-asset-urls.json +20 -0
  115. package/dist/types/api/slider.d.ts +46 -0
  116. package/dist/types/api/validation.d.ts +4 -0
  117. package/dist/types/api.d.ts +2 -0
  118. package/dist/types/composables.d.ts +3 -3
  119. package/dist/types/index.d.ts +594 -120
  120. package/dist/vetur/quasar-attributes.json +250 -82
  121. package/dist/vetur/quasar-tags.json +59 -17
  122. package/dist/web-types/web-types.json +578 -149
  123. package/package.json +1 -1
  124. package/src/api.extends.json +0 -1
  125. package/src/components/breadcrumbs/QBreadcrumbs.js +7 -2
  126. package/src/components/breadcrumbs/QBreadcrumbs.sass +0 -3
  127. package/src/components/breadcrumbs/QBreadcrumbsEl.js +14 -8
  128. package/src/components/btn/QBtn.js +5 -5
  129. package/src/components/btn/use-btn.js +21 -21
  130. package/src/components/btn/use-btn.json +22 -13
  131. package/src/components/btn-toggle/QBtnToggle.json +3 -0
  132. package/src/components/checkbox/use-checkbox.js +1 -1
  133. package/src/components/color/QColor.js +32 -26
  134. package/src/components/color/QColor.sass +10 -23
  135. package/src/components/date/QDate.sass +2 -0
  136. package/src/components/drawer/QDrawer.js +18 -15
  137. package/src/components/editor/QEditor.js +1 -1
  138. package/src/components/editor/QEditor.sass +10 -1
  139. package/src/components/expansion-item/QExpansionItem.js +4 -1
  140. package/src/components/fab/QFab.js +18 -12
  141. package/src/components/fab/QFab.json +33 -0
  142. package/src/components/fab/QFab.sass +1 -1
  143. package/src/components/fab/QFabAction.js +14 -7
  144. package/src/components/fab/QFabAction.json +10 -0
  145. package/src/components/file/QFile.js +12 -5
  146. package/src/components/file/QFile.sass +4 -2
  147. package/src/components/footer/QFooter.js +1 -1
  148. package/src/components/header/QHeader.js +1 -1
  149. package/src/components/icon/QIcon.js +1 -1
  150. package/src/components/infinite-scroll/QInfiniteScroll.js +4 -5
  151. package/src/components/item/QItem.js +2 -3
  152. package/src/components/option-group/QOptionGroup.js +3 -0
  153. package/src/components/option-group/QOptionGroup.json +48 -2
  154. package/src/components/parallax/QParallax.js +4 -2
  155. package/src/components/popup-edit/QPopupEdit.js +2 -5
  156. package/src/components/radio/QRadio.js +2 -7
  157. package/src/components/range/QRange.js +103 -222
  158. package/src/components/range/QRange.json +11 -121
  159. package/src/components/scroll-area/QScrollArea.js +3 -1
  160. package/src/components/slider/QSlider.js +46 -132
  161. package/src/components/slider/QSlider.json +1 -121
  162. package/src/components/slider/QSlider.sass +198 -132
  163. package/src/components/slider/use-slider.js +453 -109
  164. package/src/components/slider/use-slider.json +546 -0
  165. package/src/components/stepper/QStepper.js +3 -3
  166. package/src/components/stepper/QStepper.sass +24 -26
  167. package/src/components/table/QTable.js +26 -46
  168. package/src/components/tabs/QRouteTab.js +1 -2
  169. package/src/components/tabs/QRouteTab.json +0 -7
  170. package/src/components/tabs/QTabs.js +71 -20
  171. package/src/components/tabs/use-tab.js +26 -13
  172. package/src/components/tree/QTree.js +14 -12
  173. package/src/components/uploader/QUploader.json +14 -2
  174. package/src/components/uploader/uploader-core.js +16 -9
  175. package/src/components/virtual-scroll/QVirtualScroll.sass +1 -0
  176. package/src/components/virtual-scroll/use-virtual-scroll.js +30 -17
  177. package/src/composables/private/use-field.js +5 -5
  178. package/src/composables/private/use-file.js +20 -5
  179. package/src/composables/private/use-form.js +2 -3
  180. package/src/composables/private/use-fullscreen.js +15 -4
  181. package/src/composables/private/use-router-link.js +44 -23
  182. package/src/composables/private/use-router-link.json +26 -10
  183. package/src/composables/private/use-split-attrs.js +4 -4
  184. package/src/composables/private/use-validate.js +21 -15
  185. package/src/composables/private/use-validate.json +1 -0
  186. package/src/css/core/helpers.sass +3 -0
  187. package/src/css/core/positioning.sass +5 -0
  188. package/src/directives/ScrollFire.js +1 -0
  189. package/src/icon-set.js +2 -4
  190. package/src/plugins/AppFullscreen.js +70 -53
  191. package/src/plugins/AppVisibility.js +2 -3
  192. package/src/plugins/BottomSheet.js +3 -5
  193. package/src/plugins/Dialog.js +3 -5
  194. package/src/plugins/LoadingBar.js +17 -18
  195. package/src/plugins/Notify.js +296 -295
  196. package/src/plugins/Platform.js +14 -14
  197. package/src/utils/date.js +4 -4
  198. package/src/utils/dom.js +2 -2
  199. package/src/utils/open-url.js +2 -2
  200. package/src/utils/patterns.js +1 -0
  201. package/src/utils/private/define-reactive-plugin.js +10 -8
  202. package/src/utils/private/global-dialog.js +6 -8
  203. package/src/utils/private/inject-obj-prop.js +13 -0
  204. package/src/utils/private/is.js +2 -2
@@ -3,41 +3,27 @@ import { h, ref, computed, onBeforeUnmount, getCurrentInstance } from 'vue'
3
3
  import TouchPan from '../../directives/TouchPan.js'
4
4
 
5
5
  import useDark, { useDarkProps } from '../../composables/private/use-dark.js'
6
+ import { useFormProps, useFormInject } from '../../composables/private/use-form.js'
6
7
 
7
8
  import { between } from '../../utils/format.js'
8
9
  import { position } from '../../utils/event.js'
9
10
  import { isNumber } from '../../utils/private/is.js'
11
+ import { hDir } from '../../utils/private/render.js'
12
+
13
+ const markerPrefixClass = 'q-slider__marker-labels'
14
+ const defaultMarkerConvertFn = v => ({ value: v })
15
+ const defaultMarkerLabelRenderFn = ({ marker }) => h('div', {
16
+ key: marker.value,
17
+ style: marker.style,
18
+ class: marker.classes
19
+ }, marker.label)
10
20
 
11
21
  // PGDOWN, LEFT, DOWN, PGUP, RIGHT, UP
12
22
  export const keyCodes = [ 34, 37, 40, 33, 39, 38 ]
13
23
 
14
- export function getRatio (evt, dragging, reverse, vertical) {
15
- const
16
- pos = position(evt),
17
- val = vertical === true
18
- ? between((pos.top - dragging.top) / dragging.height, 0, 1)
19
- : between((pos.left - dragging.left) / dragging.width, 0, 1)
20
-
21
- return reverse === true ? 1.0 - val : val
22
- }
23
-
24
- export function getModel (ratio, min, max, step, decimals) {
25
- let model = min + ratio * (max - min)
26
-
27
- if (step > 0) {
28
- const modulo = (model - min) % step
29
- model += (Math.abs(modulo) >= step / 2 ? (modulo < 0 ? -1 : 1) * step : 0) - modulo
30
- }
31
-
32
- if (decimals > 0) {
33
- model = parseFloat(model.toFixed(decimals))
34
- }
35
-
36
- return between(model, min, max)
37
- }
38
-
39
24
  export const useSliderProps = {
40
25
  ...useDarkProps,
26
+ ...useFormProps,
41
27
 
42
28
  min: {
43
29
  type: Number,
@@ -47,30 +33,58 @@ export const useSliderProps = {
47
33
  type: Number,
48
34
  default: 100
49
35
  },
36
+ innerMin: Number,
37
+ innerMax: Number,
38
+
50
39
  step: {
51
40
  type: Number,
52
41
  default: 1,
53
42
  validator: v => v >= 0
54
43
  },
55
44
 
45
+ snap: Boolean,
46
+
47
+ vertical: Boolean,
48
+ reverse: Boolean,
49
+
50
+ hideSelection: Boolean,
51
+
56
52
  color: String,
53
+ markerLabelsClass: String,
57
54
 
55
+ label: Boolean,
58
56
  labelColor: String,
59
57
  labelTextColor: String,
60
- dense: Boolean,
61
-
62
- label: Boolean,
63
58
  labelAlways: Boolean,
59
+ switchLabelSide: Boolean,
60
+
64
61
  markers: [ Boolean, Number ],
65
- snap: Boolean,
62
+ markerLabels: [ Boolean, Array, Object, Function ],
63
+ switchMarkerLabelsSide: Boolean,
66
64
 
67
- vertical: Boolean,
68
- reverse: Boolean,
65
+ trackImg: String,
66
+ trackColor: String,
67
+ innerTrackImg: String,
68
+ innerTrackColor: String,
69
+ selectionColor: String,
70
+ selectionImg: String,
71
+
72
+ thumbSize: {
73
+ type: String,
74
+ default: '20px'
75
+ },
76
+ trackSize: {
77
+ type: String,
78
+ default: '4px'
79
+ },
69
80
 
70
81
  disable: Boolean,
71
82
  readonly: Boolean,
83
+ dense: Boolean,
84
+
72
85
  tabindex: [ String, Number ],
73
86
 
87
+ thumbColor: String,
74
88
  thumbPath: {
75
89
  type: String,
76
90
  default: 'M 4, 10 a 6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0'
@@ -79,16 +93,19 @@ export const useSliderProps = {
79
93
 
80
94
  export const useSliderEmits = [ 'pan', 'update:modelValue', 'change' ]
81
95
 
82
- export default function ({ updateValue, updatePosition, getDragging }) {
83
- const { props, emit, proxy: { $q } } = getCurrentInstance()
96
+ export default function ({ updateValue, updatePosition, getDragging, formAttrs }) {
97
+ const { props, emit, slots, proxy: { $q } } = getCurrentInstance()
84
98
  const isDark = useDark(props, $q)
85
99
 
100
+ const injectFormInput = useFormInject(formAttrs)
101
+
86
102
  const active = ref(false)
87
103
  const preventFocus = ref(false)
88
104
  const focus = ref(false)
89
105
  const dragging = ref(false)
90
106
 
91
107
  const axis = computed(() => (props.vertical === true ? '--v' : '--h'))
108
+ const labelSide = computed(() => '-' + (props.switchLabelSide === true ? 'switched' : 'standard'))
92
109
 
93
110
  const isReversed = computed(() => (
94
111
  props.vertical === true
@@ -96,12 +113,64 @@ export default function ({ updateValue, updatePosition, getDragging }) {
96
113
  : props.reverse !== ($q.lang.rtl === true)
97
114
  ))
98
115
 
99
- const editable = computed(() => props.disable !== true && props.readonly !== true && props.min < props.max)
116
+ const innerMin = computed(() => (
117
+ isNaN(props.innerMin) === true || props.innerMin < props.min
118
+ ? props.min
119
+ : props.innerMin
120
+ ))
121
+ const innerMax = computed(() => (
122
+ isNaN(props.innerMax) === true || props.innerMax > props.max
123
+ ? props.max
124
+ : props.innerMax
125
+ ))
126
+
127
+ const editable = computed(() => (
128
+ props.disable !== true && props.readonly !== true
129
+ && innerMin.value < innerMax.value
130
+ ))
131
+
132
+ const decimals = computed(() => (String(props.step).trim('0').split('.')[ 1 ] || '').length)
133
+ const step = computed(() => (props.step === 0 ? 1 : props.step))
134
+ const tabindex = computed(() => (editable.value === true ? props.tabindex || 0 : -1))
135
+
136
+ const trackLen = computed(() => props.max - props.min)
137
+ const innerBarLen = computed(() => innerMax.value - innerMin.value)
138
+
139
+ const innerMinRatio = computed(() => convertModelToRatio(innerMin.value))
140
+ const innerMaxRatio = computed(() => convertModelToRatio(innerMax.value))
141
+
142
+ const positionProp = computed(() => (
143
+ props.vertical === true
144
+ ? (isReversed.value === true ? 'bottom' : 'top')
145
+ : isReversed.value === true ? 'right' : 'left'
146
+ ))
147
+
148
+ const sizeProp = computed(() => (props.vertical === true ? 'height' : 'width'))
149
+ const thicknessProp = computed(() => (props.vertical === true ? 'width' : 'height'))
150
+ const orientation = computed(() => (props.vertical === true ? 'vertical' : 'horizontal'))
151
+
152
+ const attributes = computed(() => {
153
+ const acc = {
154
+ role: 'slider',
155
+ 'aria-valuemin': props.min,
156
+ 'aria-valuemax': props.max,
157
+ 'aria-orientation': orientation.value,
158
+ 'data-step': props.step
159
+ }
160
+
161
+ if (props.disable === true) {
162
+ acc[ 'aria-disabled' ] = 'true'
163
+ }
164
+ else if (props.readonly === true) {
165
+ acc[ 'aria-readonly' ] = 'true'
166
+ }
167
+
168
+ return acc
169
+ })
100
170
 
101
171
  const classes = computed(() =>
102
- `q-slider q-slider${ axis.value } q-slider--${ active.value === true ? '' : 'in' }active`
103
- + (isReversed.value === true ? ' q-slider--reversed' : '')
104
- + (props.color !== void 0 ? ` text-${ props.color }` : '')
172
+ `q-slider q-slider${ axis.value } q-slider--${ active.value === true ? '' : 'in' }active inline no-wrap `
173
+ + (props.vertical === true ? 'row' : 'column')
105
174
  + (props.disable === true ? ' disabled' : ' q-slider--enabled' + (editable.value === true ? ' q-slider--editable' : ''))
106
175
  + (focus.value === 'both' ? ' q-slider--focus' : '')
107
176
  + (props.label || props.labelAlways === true ? ' q-slider--label' : '')
@@ -110,19 +179,148 @@ export default function ({ updateValue, updatePosition, getDragging }) {
110
179
  + (props.dense === true ? ' q-slider--dense q-slider--dense' + axis.value : '')
111
180
  )
112
181
 
113
- const decimals = computed(() => (String(props.step).trim('0').split('.')[ 1 ] || '').length)
114
- const step = computed(() => (props.step === 0 ? 1 : props.step))
115
- const minMaxDiff = computed(() => props.max - props.min)
182
+ function getPositionClass (name) {
183
+ const cls = 'q-slider__' + name
184
+ return `${ cls } ${ cls }${ axis.value } ${ cls }${ axis.value }${ labelSide.value }`
185
+ }
186
+ function getAxisClass (name) {
187
+ const cls = 'q-slider__' + name
188
+ return `${ cls } ${ cls }${ axis.value }`
189
+ }
190
+
191
+ const selectionBarClass = computed(() => {
192
+ const color = props.selectionColor || props.color
193
+ return 'q-slider__selection absolute'
194
+ + (color !== void 0 ? ` text-${ color }` : '')
195
+ })
196
+ const markerClass = computed(() => getAxisClass('markers') + ' absolute overflow-hidden')
197
+ const trackContainerClass = computed(() => getAxisClass('track-container'))
198
+ const pinClass = computed(() => getPositionClass('pin'))
199
+ const labelClass = computed(() => getPositionClass('label'))
200
+ const textContainerClass = computed(() => getPositionClass('text-container'))
201
+ const markerLabelsContainerClass = computed(() =>
202
+ getPositionClass('marker-labels-container')
203
+ + (props.markerLabelsClass !== void 0 ? ` ${ props.markerLabelsClass }` : '')
204
+ )
205
+
206
+ const trackClass = computed(() =>
207
+ 'q-slider__track relative-position no-outline'
208
+ + (props.trackColor !== void 0 ? ` bg-${ props.trackColor }` : '')
209
+ )
210
+ const trackStyle = computed(() => {
211
+ const acc = { [ thicknessProp.value ]: props.trackSize }
212
+ if (props.trackImg !== void 0) {
213
+ acc.backgroundImage = `url(${ props.trackImg }) !important`
214
+ }
215
+ return acc
216
+ })
217
+
218
+ const innerBarClass = computed(() =>
219
+ 'q-slider__inner absolute'
220
+ + (props.innerTrackColor !== void 0 ? ` bg-${ props.innerTrackColor }` : '')
221
+ )
222
+ const innerBarStyle = computed(() => {
223
+ const acc = {
224
+ [ positionProp.value ]: `${ 100 * innerMinRatio.value }%`,
225
+ [ sizeProp.value ]: `${ 100 * (innerMaxRatio.value - innerMinRatio.value) }%`
226
+ }
227
+ if (props.innerTrackImg !== void 0) {
228
+ acc.backgroundImage = `url(${ props.innerTrackImg }) !important`
229
+ }
230
+ return acc
231
+ })
232
+
233
+ function convertRatioToModel (ratio) {
234
+ const { min, max, step } = props
235
+ let model = min + ratio * (max - min)
236
+
237
+ if (step > 0) {
238
+ const modulo = (model - min) % step
239
+ model += (Math.abs(modulo) >= step / 2 ? (modulo < 0 ? -1 : 1) * step : 0) - modulo
240
+ }
241
+
242
+ if (decimals.value > 0) {
243
+ model = parseFloat(model.toFixed(decimals.value))
244
+ }
245
+
246
+ return between(model, innerMin.value, innerMax.value)
247
+ }
248
+
249
+ function convertModelToRatio (model) {
250
+ return trackLen.value === 0
251
+ ? 0
252
+ : (model - props.min) / trackLen.value
253
+ }
254
+
255
+ function getDraggingRatio (evt, dragging) {
256
+ const
257
+ pos = position(evt),
258
+ val = props.vertical === true
259
+ ? between((pos.top - dragging.top) / dragging.height, 0, 1)
260
+ : between((pos.left - dragging.left) / dragging.width, 0, 1)
261
+
262
+ return between(
263
+ isReversed.value === true ? 1.0 - val : val,
264
+ innerMinRatio.value,
265
+ innerMaxRatio.value
266
+ )
267
+ }
116
268
 
117
269
  const markerStep = computed(() => (
118
270
  isNumber(props.markers) === true ? props.markers : step.value)
119
271
  )
120
272
 
273
+ const markerTicks = computed(() => {
274
+ const acc = []
275
+ const step = markerStep.value
276
+ const max = props.max
277
+
278
+ let value = props.min
279
+ do {
280
+ acc.push(value)
281
+ value += step
282
+ } while (value < max)
283
+
284
+ acc.push(max)
285
+ return acc
286
+ })
287
+
288
+ const markerLabelClass = computed(() => {
289
+ const prefix = ` ${ markerPrefixClass }${ axis.value }-`
290
+ return markerPrefixClass
291
+ + `${ prefix }${ props.switchMarkerLabelsSide === true ? 'switched' : 'standard' }`
292
+ + `${ prefix }${ isReversed.value === true ? 'rtl' : 'ltr' }`
293
+ })
294
+
295
+ const markerLabelsList = computed(() => {
296
+ if (props.markerLabels === false) { return null }
297
+
298
+ return getMarkerList(props.markerLabels).map((entry, index) => ({
299
+ index,
300
+ value: entry.value,
301
+ label: entry.label || entry.value,
302
+ classes: markerLabelClass.value
303
+ + (entry.classes !== void 0 ? ' ' + entry.classes : ''),
304
+ style: {
305
+ ...getMarkerLabelStyle(entry.value),
306
+ ...(entry.style || {})
307
+ }
308
+ }))
309
+ })
310
+
311
+ const markerScope = computed(() => ({
312
+ markerList: markerLabelsList.value,
313
+ markerMap: markerLabelsMap.value,
314
+ classes: markerLabelClass.value, // TODO ts definition
315
+ getStyle: getMarkerLabelStyle
316
+ }))
317
+
121
318
  const markerStyle = computed(() => {
122
- if (minMaxDiff.value !== 0) {
123
- const size = 100 * markerStep.value / minMaxDiff.value
319
+ if (innerBarLen.value !== 0) {
320
+ const size = 100 * markerStep.value / innerBarLen.value
124
321
 
125
322
  return {
323
+ ...innerBarStyle.value,
126
324
  backgroundSize: props.vertical === true
127
325
  ? `2px ${ size }%`
128
326
  : `${ size }% 2px`
@@ -132,37 +330,57 @@ export default function ({ updateValue, updatePosition, getDragging }) {
132
330
  return null
133
331
  })
134
332
 
135
- const tabindex = computed(() => (editable.value === true ? props.tabindex || 0 : -1))
136
-
137
- const positionProp = computed(() => (
138
- props.vertical === true
139
- ? (isReversed.value === true ? 'bottom' : 'top')
140
- : isReversed.value === true ? 'right' : 'left'
141
- ))
142
-
143
- const sizeProp = computed(() => (props.vertical === true ? 'height' : 'width'))
144
-
145
- const orientation = computed(() => (props.vertical === true ? 'vertical' : 'horizontal'))
333
+ function getMarkerList (def) {
334
+ if (def === false) { return null }
146
335
 
147
- const attributes = computed(() => {
148
- const acc = {
149
- role: 'slider',
150
- 'aria-valuemin': props.min,
151
- 'aria-valuemax': props.max,
152
- 'aria-orientation': orientation.value,
153
- 'data-step': props.step
336
+ if (def === true) {
337
+ return markerTicks.value.map(defaultMarkerConvertFn)
154
338
  }
155
339
 
156
- if (props.disable === true) {
157
- acc[ 'aria-disabled' ] = 'true'
340
+ if (typeof def === 'function') {
341
+ return markerTicks.value.map(value => {
342
+ const item = def(value)
343
+ return Object(item) === item ? { ...item, value } : { value, label: item }
344
+ })
158
345
  }
159
- else if (props.readonly === true) {
160
- acc[ 'aria-readonly' ] = 'true'
346
+
347
+ if (Array.isArray(def) === true) {
348
+ return def.map(item => (Object(item) === item ? item : { value: item }))
161
349
  }
162
350
 
351
+ return Object.keys(def).map(key => {
352
+ const item = def[ key ]
353
+ const value = Number(key)
354
+ return Object(item) === item ? { ...item, value } : { value, label: item }
355
+ })
356
+ }
357
+
358
+ function getMarkerLabelStyle (val) {
359
+ return { [ positionProp.value ]: `${ 100 * (val - props.min) / trackLen.value }%` }
360
+ }
361
+
362
+ const markerLabelsMap = computed(() => {
363
+ if (props.markerLabels === false) { return null }
364
+
365
+ const acc = {}
366
+ markerLabelsList.value.forEach(entry => {
367
+ acc[ entry.value ] = entry
368
+ })
163
369
  return acc
164
370
  })
165
371
 
372
+ function getMarkerLabelsContent () {
373
+ if (slots[ 'marker-label-group' ] !== void 0) {
374
+ return slots[ 'marker-label-group' ](markerScope.value)
375
+ }
376
+
377
+ const fn = slots[ 'marker-label' ] || defaultMarkerLabelRenderFn
378
+ return markerLabelsList.value.map(marker => fn({
379
+ marker,
380
+ ...markerScope.value
381
+ }))
382
+ }
383
+
166
384
  const panDirective = computed(() => {
167
385
  // if editable.value === true
168
386
  return [ [
@@ -179,36 +397,6 @@ export default function ({ updateValue, updatePosition, getDragging }) {
179
397
  ] ]
180
398
  })
181
399
 
182
- function getThumbSvg () {
183
- return h('svg', {
184
- class: 'q-slider__thumb absolute',
185
- viewBox: '0 0 20 20',
186
- width: '20',
187
- height: '20',
188
- 'aria-hidden': 'true'
189
- }, [
190
- h('path', { d: props.thumbPath })
191
- ])
192
- }
193
-
194
- function getPinStyle (percent, ratio) {
195
- if (props.vertical === true) {
196
- return {}
197
- }
198
-
199
- const offset = `${ Math.ceil(20 * Math.abs(0.5 - ratio)) }px`
200
- return {
201
- pin: {
202
- transformOrigin: `${ $q.lang.rtl === true ? offset : `calc(100% - ${ offset })` } 50%`
203
- },
204
-
205
- pinTextContainer: {
206
- [ $q.lang.rtl === true ? 'left' : 'right' ]: `${ percent * 100 }%`,
207
- transform: `translateX(${ Math.ceil(($q.lang.rtl === true ? -1 : 1) * 20 * percent) }px)`
208
- }
209
- }
210
- }
211
-
212
400
  function onPan (event) {
213
401
  if (event.isFinal === true) {
214
402
  if (dragging.value !== void 0) {
@@ -249,10 +437,7 @@ export default function ({ updateValue, updatePosition, getDragging }) {
249
437
 
250
438
  function onDeactivate () {
251
439
  preventFocus.value = false
252
-
253
- if (dragging.value === false) {
254
- active.value = false
255
- }
440
+ active.value = false
256
441
 
257
442
  updateValue(true)
258
443
  onBlur()
@@ -271,6 +456,161 @@ export default function ({ updateValue, updatePosition, getDragging }) {
271
456
  }
272
457
  }
273
458
 
459
+ function getTextContainerStyle (ratio) {
460
+ if (props.vertical === true) { return null }
461
+
462
+ const p = $q.lang.rtl !== props.reverse ? 1 - ratio : ratio
463
+ return {
464
+ transform: `translateX(calc(${ 2 * p - 1 } * ${ props.thumbSize } / 2 + ${ 50 - 100 * p }%))`
465
+ }
466
+ }
467
+
468
+ function getThumbRenderFn (thumb) {
469
+ const focusClass = computed(() => (
470
+ preventFocus.value === false && focus.value === thumb.focusValue
471
+ ? ' q-slider--focus'
472
+ : ''
473
+ ))
474
+
475
+ const classes = computed(() =>
476
+ `q-slider__thumb q-slider__thumb${ axis.value } q-slider__thumb${ axis.value }-${ isReversed.value === true ? 'rtl' : 'ltr' } absolute non-selectable`
477
+ + focusClass.value
478
+ + (thumb.thumbColor.value !== void 0 ? ` text-${ thumb.thumbColor.value }` : '')
479
+ )
480
+
481
+ const style = computed(() => ({
482
+ width: props.thumbSize,
483
+ height: props.thumbSize,
484
+ [ positionProp.value ]: `${ 100 * thumb.ratio.value }%`,
485
+ 'z-index': thumb.nextFocus !== void 0
486
+ ? thumb.nextFocus.value === 'min' ? 2 : void 0
487
+ : void 0
488
+ }))
489
+
490
+ const pinColor = computed(() => (
491
+ thumb.labelColor.value !== void 0
492
+ ? ` text-${ thumb.labelColor.value }`
493
+ : ''
494
+ ))
495
+
496
+ const textContainerStyle = computed(() => getTextContainerStyle(thumb.ratio.value))
497
+
498
+ const textClass = computed(() => (
499
+ 'q-slider__text'
500
+ + (thumb.labelTextColor.value !== void 0 ? ` text-${ thumb.labelTextColor.value }` : '')
501
+ ))
502
+
503
+ return () => {
504
+ const thumbContent = [
505
+ h('svg', {
506
+ class: 'q-slider__thumb-shape absolute-full',
507
+ viewBox: '0 0 20 20',
508
+ 'aria-hidden': 'true'
509
+ }, [
510
+ h('path', { d: props.thumbPath })
511
+ ]),
512
+
513
+ h('div', { class: 'q-slider__focus-ring fit' })
514
+ ]
515
+
516
+ if (props.label === true || props.labelAlways === true) {
517
+ thumbContent.push(
518
+ h('div', {
519
+ class: pinClass.value + ' absolute fit no-pointer-events' + pinColor.value
520
+ }, [
521
+ h('div', {
522
+ class: labelClass.value,
523
+ style: { minWidth: props.thumbSize }
524
+ }, [
525
+ h('div', {
526
+ class: textContainerClass.value,
527
+ style: textContainerStyle.value
528
+ }, [
529
+ h('span', { class: textClass.value }, thumb.label.value)
530
+ ])
531
+ ])
532
+ ])
533
+ )
534
+
535
+ if (props.name !== void 0 && props.disable !== true) {
536
+ injectFormInput(thumbContent, 'push')
537
+ }
538
+ }
539
+
540
+ return h('div', {
541
+ class: classes.value,
542
+ style: style.value,
543
+ ...(thumb.getNodeData !== void 0 ? thumb.getNodeData() : {})
544
+ }, thumbContent)
545
+ }
546
+ }
547
+
548
+ function getContent (selectionBarStyle, events, injectThumb) {
549
+ const trackContent = []
550
+
551
+ props.innerTrackColor !== 'transparent' && trackContent.push(
552
+ h('div', {
553
+ key: 'inner',
554
+ class: innerBarClass.value,
555
+ style: innerBarStyle.value
556
+ })
557
+ )
558
+
559
+ props.selectionColor !== 'transparent' && trackContent.push(
560
+ h('div', {
561
+ key: 'selection',
562
+ class: selectionBarClass.value,
563
+ style: selectionBarStyle.value
564
+ })
565
+ )
566
+
567
+ props.markers !== false && trackContent.push(
568
+ h('div', {
569
+ key: 'marker',
570
+ class: markerClass.value,
571
+ style: markerStyle.value
572
+ })
573
+ )
574
+
575
+ injectThumb(trackContent)
576
+
577
+ const content = [
578
+ hDir(
579
+ 'div',
580
+ {
581
+ key: 'trackC',
582
+ class: trackContainerClass.value,
583
+ tabindex: tabindex.value,
584
+ ...events.value
585
+ },
586
+ [
587
+ h('div', {
588
+ class: trackClass.value,
589
+ style: trackStyle.value
590
+ },
591
+ trackContent)
592
+ ],
593
+ 'slide',
594
+ editable.value, () => panDirective.value
595
+ )
596
+ ]
597
+
598
+ if (props.markerLabels !== false) {
599
+ const action = props.switchMarkerLabelsSide === true
600
+ ? 'unshift'
601
+ : 'push'
602
+
603
+ content[ action ](
604
+ h('div', {
605
+ key: 'markerL',
606
+ class: markerLabelsContainerClass.value
607
+ }, getMarkerLabelsContent())
608
+ )
609
+ }
610
+
611
+ return content
612
+ }
613
+
274
614
  onBeforeUnmount(() => {
275
615
  document.removeEventListener('mouseup', onDeactivate, true)
276
616
  })
@@ -282,19 +622,20 @@ export default function ({ updateValue, updatePosition, getDragging }) {
282
622
  preventFocus,
283
623
  dragging,
284
624
 
285
- axis,
286
- isReversed,
287
625
  editable,
288
626
  classes,
289
- decimals,
290
- step,
291
- minMaxDiff,
292
- markerStyle,
293
627
  tabindex,
294
- positionProp,
295
- sizeProp,
296
628
  attributes,
297
- panDirective
629
+
630
+ step,
631
+ decimals,
632
+ trackLen,
633
+ innerMin,
634
+ innerMinRatio,
635
+ innerMax,
636
+ innerMaxRatio,
637
+ positionProp,
638
+ sizeProp
298
639
  },
299
640
 
300
641
  methods: {
@@ -302,8 +643,11 @@ export default function ({ updateValue, updatePosition, getDragging }) {
302
643
  onMobileClick,
303
644
  onBlur,
304
645
  onKeyup,
305
- getThumbSvg,
306
- getPinStyle
646
+ getContent,
647
+ getThumbRenderFn,
648
+ convertRatioToModel,
649
+ convertModelToRatio,
650
+ getDraggingRatio
307
651
  }
308
652
  }
309
653
  }