quasar 2.4.6 → 2.4.10

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 (129) hide show
  1. package/dist/api/QFile.json +1 -0
  2. package/dist/api/QTree.json +7 -0
  3. package/dist/api/QUploader.json +3 -1
  4. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  5. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  6. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  7. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  8. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  9. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  10. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  11. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  12. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  13. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  14. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  15. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  16. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  17. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  18. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  19. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  20. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  21. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  22. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  23. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  24. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  25. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
  26. package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
  27. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
  28. package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
  29. package/dist/icon-set/svg-mdi-v4.umd.prod.js +1 -1
  30. package/dist/icon-set/svg-mdi-v5.umd.prod.js +1 -1
  31. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  32. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  33. package/dist/icon-set/themify.umd.prod.js +1 -1
  34. package/dist/lang/ar.umd.prod.js +1 -1
  35. package/dist/lang/az-Latn.umd.prod.js +1 -1
  36. package/dist/lang/bg.umd.prod.js +1 -1
  37. package/dist/lang/bn.umd.prod.js +1 -1
  38. package/dist/lang/ca.umd.prod.js +1 -1
  39. package/dist/lang/cs.umd.prod.js +1 -1
  40. package/dist/lang/da.umd.prod.js +1 -1
  41. package/dist/lang/de.umd.prod.js +1 -1
  42. package/dist/lang/el.umd.prod.js +1 -1
  43. package/dist/lang/en-GB.umd.prod.js +1 -1
  44. package/dist/lang/en-US.umd.prod.js +1 -1
  45. package/dist/lang/eo.umd.prod.js +1 -1
  46. package/dist/lang/es.umd.prod.js +1 -1
  47. package/dist/lang/et.umd.prod.js +1 -1
  48. package/dist/lang/fa-IR.umd.prod.js +1 -1
  49. package/dist/lang/fa.umd.prod.js +1 -1
  50. package/dist/lang/fi.umd.prod.js +1 -1
  51. package/dist/lang/fr.umd.prod.js +1 -1
  52. package/dist/lang/gn.umd.prod.js +1 -1
  53. package/dist/lang/he.umd.prod.js +1 -1
  54. package/dist/lang/hr.umd.prod.js +1 -1
  55. package/dist/lang/hu.umd.prod.js +1 -1
  56. package/dist/lang/id.umd.prod.js +1 -1
  57. package/dist/lang/is.umd.prod.js +1 -1
  58. package/dist/lang/it.umd.prod.js +1 -1
  59. package/dist/lang/ja.umd.prod.js +1 -1
  60. package/dist/lang/km.umd.prod.js +1 -1
  61. package/dist/lang/ko-KR.umd.prod.js +1 -1
  62. package/dist/lang/kur-CKB.umd.prod.js +1 -1
  63. package/dist/lang/lt.umd.prod.js +1 -1
  64. package/dist/lang/lu.umd.prod.js +1 -1
  65. package/dist/lang/lv.umd.prod.js +1 -1
  66. package/dist/lang/ml.umd.prod.js +1 -1
  67. package/dist/lang/ms.umd.prod.js +1 -1
  68. package/dist/lang/my.umd.prod.js +6 -0
  69. package/dist/lang/nb-NO.umd.prod.js +1 -1
  70. package/dist/lang/nl.umd.prod.js +1 -1
  71. package/dist/lang/pl.umd.prod.js +1 -1
  72. package/dist/lang/pt-BR.umd.prod.js +1 -1
  73. package/dist/lang/pt.umd.prod.js +1 -1
  74. package/dist/lang/ro.umd.prod.js +1 -1
  75. package/dist/lang/ru.umd.prod.js +1 -1
  76. package/dist/lang/sk.umd.prod.js +1 -1
  77. package/dist/lang/sl.umd.prod.js +1 -1
  78. package/dist/lang/sr-CYR.umd.prod.js +1 -1
  79. package/dist/lang/sr.umd.prod.js +1 -1
  80. package/dist/lang/sv.umd.prod.js +1 -1
  81. package/dist/lang/ta.umd.prod.js +1 -1
  82. package/dist/lang/th.umd.prod.js +1 -1
  83. package/dist/lang/tr.umd.prod.js +1 -1
  84. package/dist/lang/ug.umd.prod.js +1 -1
  85. package/dist/lang/uk.umd.prod.js +1 -1
  86. package/dist/lang/vi.umd.prod.js +1 -1
  87. package/dist/lang/zh-CN.umd.prod.js +1 -1
  88. package/dist/lang/zh-TW.umd.prod.js +1 -1
  89. package/dist/quasar.cjs.prod.js +2 -2
  90. package/dist/quasar.css +7 -4
  91. package/dist/quasar.esm.prod.js +2 -2
  92. package/dist/quasar.prod.css +1 -1
  93. package/dist/quasar.rtl.css +7 -4
  94. package/dist/quasar.rtl.prod.css +1 -1
  95. package/dist/quasar.sass +8 -4
  96. package/dist/quasar.umd.js +78 -60
  97. package/dist/quasar.umd.prod.js +2 -2
  98. package/dist/ssr-directives/Morph.js +1 -1
  99. package/dist/types/api/qfile.d.ts +8 -0
  100. package/dist/types/api/qselect.d.ts +4 -0
  101. package/dist/types/api/qtable.d.ts +12 -0
  102. package/dist/types/api/qtree.d.ts +31 -0
  103. package/dist/types/api/quploader.d.ts +28 -0
  104. package/dist/types/api/touchswipe.d.ts +11 -0
  105. package/dist/types/api.d.ts +6 -0
  106. package/dist/types/index.d.ts +287 -311
  107. package/dist/types/ts-helpers.d.ts +6 -2
  108. package/dist/types/utils.d.ts +4 -1
  109. package/dist/vetur/quasar-attributes.json +4 -0
  110. package/dist/vetur/quasar-tags.json +1 -0
  111. package/dist/web-types/web-types.json +11 -1
  112. package/lang/index.json +4 -0
  113. package/lang/my.js +90 -0
  114. package/package.json +1 -1
  115. package/src/components/date/QDate.js +6 -1
  116. package/src/components/dialog-plugin/DialogPlugin.js +1 -1
  117. package/src/components/icon/QIcon.js +4 -9
  118. package/src/components/icon/QIcon.sass +4 -3
  119. package/src/components/layout/QLayout.js +6 -5
  120. package/src/components/scroll-area/QScrollArea.js +15 -10
  121. package/src/components/scroll-observer/QScrollObserver.js +12 -10
  122. package/src/components/select/QSelect.js +20 -19
  123. package/src/components/select/QSelect.sass +3 -0
  124. package/src/components/tree/QTree.js +6 -1
  125. package/src/components/tree/QTree.json +8 -0
  126. package/src/components/uploader/uploader-core.js +2 -0
  127. package/src/components/uploader/xhr-uploader-plugin.json +2 -1
  128. package/src/composables/private/use-field.js +2 -0
  129. package/src/composables/private/use-file.json +1 -0
@@ -1,11 +1,15 @@
1
1
  import { ComponentOptions, ComponentPublicInstance, ComputedOptions, MethodOptions, VNodeProps, AllowedComponentProps, ComponentCustomProps } from 'vue';
2
2
 
3
- export type LooseDictionary = { [index in string]: any };
4
-
5
3
  export type StringDictionary<T extends string> = Required<
6
4
  { [index in T]: string }
7
5
  >;
8
6
 
7
+ // Needed to prevent TS to collapse `'value1' | 'value2' | string` to `string`, which breaks first parameter autocomplete
8
+ // See: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611
9
+ export type LiteralUnion<T extends U, U = string> =
10
+ | T
11
+ | (U & Record<never, never>);
12
+
9
13
  // See: https://stackoverflow.com/a/49936686/7931540
10
14
  export type DeepPartial<T> = {
11
15
  [P in keyof T]?: T[P] extends Array<infer U>
@@ -1,3 +1,6 @@
1
+ // Error on "quasar" import shown in IDE is normal, as we only have Components/Directives/Plugins types after the build step
2
+ // The import will work correctly at runtime
3
+ import { QUploader } from "quasar";
1
4
  import {
2
5
  ComponentOptionsMixin,
3
6
  ComponentPropsOptions,
@@ -135,4 +138,4 @@ export function createUploaderComponent<
135
138
  Emits extends EmitsOptions = []
136
139
  >(
137
140
  options: CreateUploaderComponentOptions<Props, Emits>
138
- ): DefineComponent<Props, {}, {}, {}, {}, {}, {}, EmitsOptions>;
141
+ ): QUploader & DefineComponent<Props, {}, {}, {}, {}, {}, {}, Emits>;
@@ -5559,6 +5559,10 @@
5559
5559
  "type": "any",
5560
5560
  "description": "Key of node currently selected"
5561
5561
  },
5562
+ "q-tree/no-selection-unset": {
5563
+ "type": "boolean",
5564
+ "description": "Do not allow un-selection when clicking currently selected node"
5565
+ },
5562
5566
  "q-tree/default-expand-all": {
5563
5567
  "type": "boolean",
5564
5568
  "description": "Allow the tree to have all its branches expanded, when first rendered"
@@ -1977,6 +1977,7 @@
1977
1977
  "ticked",
1978
1978
  "expanded",
1979
1979
  "selected",
1980
+ "no-selection-unset",
1980
1981
  "default-expand-all",
1981
1982
  "accordion",
1982
1983
  "filter",
@@ -2,7 +2,7 @@
2
2
  "$schema": "",
3
3
  "framework": "vue",
4
4
  "name": "quasar",
5
- "version": "2.4.6",
5
+ "version": "2.4.10",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "types-syntax": "typescript",
@@ -17964,6 +17964,16 @@
17964
17964
  "description": "Key of node currently selected\n\nExamples:\nv-model:selected=\"selectedKey\"",
17965
17965
  "doc-url": "https://v2.quasar.dev/vue-components/tree"
17966
17966
  },
17967
+ {
17968
+ "name": "no-selection-unset",
17969
+ "value": {
17970
+ "kind": "expression",
17971
+ "type": "boolean"
17972
+ },
17973
+ "description": "Do not allow un-selection when clicking currently selected node",
17974
+ "doc-url": "https://v2.quasar.dev/vue-components/tree",
17975
+ "type": "boolean"
17976
+ },
17967
17977
  {
17968
17978
  "name": "default-expand-all",
17969
17979
  "value": {
package/lang/index.json CHANGED
@@ -135,6 +135,10 @@
135
135
  "isoName": "ms",
136
136
  "nativeName": "Bahasa Melayu"
137
137
  },
138
+ {
139
+ "isoName": "my",
140
+ "nativeName": "Malaysia"
141
+ },
138
142
  {
139
143
  "isoName": "nb-NO",
140
144
  "nativeName": "Norsk"
package/lang/my.js ADDED
@@ -0,0 +1,90 @@
1
+ export default {
2
+ isoName: 'my',
3
+ nativeName: 'Malaysia',
4
+ label: {
5
+ clear: 'kosong',
6
+ ok: 'pasti',
7
+ cancel: 'Batal',
8
+ close: 'penutupan',
9
+ set: 'sediakan',
10
+ select: 'pilih',
11
+ reset: 'set semula',
12
+ remove: 'keluarkan',
13
+ update: 'memperbaharui',
14
+ create: 'cipta',
15
+ search: 'cari',
16
+ filter: 'penapis',
17
+ refresh: 'segarkan semula'
18
+ },
19
+ date: {
20
+ days: 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'),
21
+ daysShort: 'Aha_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'),
22
+ months: 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'),
23
+ monthsShort: 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogo_Sep_Okt_Nov_Dis'.split('_'),
24
+ headerTitle: date => new Intl.DateTimeFormat('my', {
25
+ weekday: 'short', month: 'short', day: 'numeric'
26
+ }).format(date),
27
+ firstDayOfWeek: 0, // 0-6, 0 - Sunday, 1 Monday, ...
28
+ format24h: false,
29
+ pluralDay: 'langit'
30
+ },
31
+ table: {
32
+ noData: 'tiada data tersedia',
33
+ noResults: 'Tiada data yang sepadan ditemui',
34
+ loading: 'memuatkan...',
35
+ selectedRecords: rows => 'dipilih' + rows + 'baris',
36
+ recordsPerPage: 'baris setiap muka surat:',
37
+ allRows: 'semua',
38
+ pagination: (start, end, total) => start + '-' + end + ' / ' + total,
39
+ columns: 'Senaraikan'
40
+ },
41
+ editor: {
42
+ url: 'URL',
43
+ bold: 'berani',
44
+ italic: 'condong',
45
+ strikethrough: 'tembus',
46
+ underline: 'gariskan',
47
+ unorderedList: 'senarai tidak teratur',
48
+ orderedList: 'senarai pesanan',
49
+ subscript: 'subskrip',
50
+ superscript: 'superskrip',
51
+ hyperlink: 'Hiperpautan',
52
+ toggleFullscreen: 'togol skrin penuh',
53
+ quote: 'tanda petikan',
54
+ left: 'Jajar ke kiri',
55
+ center: 'penjajaran tengah',
56
+ right: 'Sejajar ke kanan',
57
+ justify: 'wajar',
58
+ print: 'Cetak',
59
+ outdent: 'mengurangkan lekukan',
60
+ indent: 'meningkatkan inden',
61
+ removeFormat: 'gaya yang jelas',
62
+ formatting: 'format',
63
+ fontSize: 'Saiz huruf',
64
+ align: 'selaraskan',
65
+ hr: 'Masukkan garisan mendatar',
66
+ undo: 'membatalkan',
67
+ redo: 'buat semula',
68
+ heading1: 'Tajuk satu',
69
+ heading2: 'Tajuk dua',
70
+ heading3: 'Tajuk tiga',
71
+ heading4: 'Tajuk Empat',
72
+ heading5: 'Tajuk Lima',
73
+ heading6: 'Tajuk Enam',
74
+ paragraph: 'perenggan',
75
+ code: 'kod',
76
+ size1: 'sangat kecil',
77
+ size2: 'lebih kecil',
78
+ size3: 'biasa',
79
+ size4: 'sederhana hingga besar',
80
+ size5: 'besar',
81
+ size6: 'sangat besar',
82
+ size7: 'super besar',
83
+ defaultFont: 'fon lalai',
84
+ viewSource: 'menyemak data'
85
+ },
86
+ tree: {
87
+ noNodes: 'tiada nod tersedia',
88
+ noResults: 'Tiada nod yang sepadan ditemui'
89
+ }
90
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar",
3
- "version": "2.4.6",
3
+ "version": "2.4.10",
4
4
  "description": "Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time",
5
5
  "main": "dist/quasar.cjs.prod.js",
6
6
  "module": "dist/quasar.esm.prod.js",
@@ -153,7 +153,12 @@ export default createComponent({
153
153
  normalizedModel.value
154
154
  .filter(date => typeof date === 'string')
155
155
  .map(date => decodeString(date, innerMask.value, innerLocale.value))
156
- .filter(date => date.dateHash !== null)
156
+ .filter(date =>
157
+ date.dateHash !== null
158
+ && date.day !== null
159
+ && date.month !== null
160
+ && date.year !== null
161
+ )
157
162
  )
158
163
 
159
164
  const rangeModel = computed(() => {
@@ -144,8 +144,8 @@ export default createComponent({
144
144
  color: vmColor.value,
145
145
  label: okLabel.value,
146
146
  ripple: false,
147
- ...(Object(props.ok) === props.ok ? props.ok : { flat: true }),
148
147
  disable: okDisabled.value,
148
+ ...(Object(props.ok) === props.ok ? props.ok : { flat: true }),
149
149
  'data-autofocus': (props.focus === 'ok' && hasForm.value !== true) || void 0,
150
150
  onClick: onOk
151
151
  }))
@@ -168,27 +168,22 @@ export default createComponent({
168
168
  }
169
169
 
170
170
  if (type.value.img === true) {
171
- return h('div', data, hMergeSlot(slots.default, [
172
- h('img', {
173
- class: type.value.cls,
174
- src: type.value.src
175
- })
171
+ return h('span', data, hMergeSlot(slots.default, [
172
+ h('img', { src: type.value.src })
176
173
  ]))
177
174
  }
178
175
 
179
176
  if (type.value.svg === true) {
180
- return h('div', data, hMergeSlot(slots.default, [
177
+ return h('span', data, hMergeSlot(slots.default, [
181
178
  h('svg', {
182
- class: type.value.cls,
183
179
  viewBox: type.value.viewBox
184
180
  }, type.value.nodes)
185
181
  ]))
186
182
  }
187
183
 
188
184
  if (type.value.svguse === true) {
189
- return h('div', data, hMergeSlot(slots.default, [
185
+ return h('span', data, hMergeSlot(slots.default, [
190
186
  h('svg', {
191
- class: type.value.cls,
192
187
  viewBox: type.value.viewBox
193
188
  }, [
194
189
  h('use', { 'xlink:href': type.value.src })
@@ -19,9 +19,10 @@
19
19
  align-items: center
20
20
  justify-content: center
21
21
 
22
- > svg
23
- width: 100%
24
- height: 100%
22
+ > svg,
23
+ > img
24
+ width: 1em
25
+ height: 1em
25
26
 
26
27
  .q-icon,
27
28
  .material-icons,
@@ -175,7 +175,7 @@ export default createComponent({
175
175
 
176
176
  // prevent scrollbar flicker while resizing window height
177
177
  // if no page scrollbar is already present
178
- if (__QUASAR_SSR__ !== true) {
178
+ if (__QUASAR_SSR_SERVER__ !== true && getScrollbarWidth() > 0) {
179
179
  let timer = null
180
180
  const el = document.body
181
181
 
@@ -188,7 +188,7 @@ export default createComponent({
188
188
  if (timer === null) {
189
189
  // if it has no scrollbar then there's nothing to do
190
190
 
191
- if (el.scrollHeight > window.innerHeight) {
191
+ if (el.scrollHeight > $q.screen.height) {
192
192
  return
193
193
  }
194
194
 
@@ -211,11 +211,12 @@ export default createComponent({
211
211
  }
212
212
 
213
213
  watch(
214
- () => (props.container !== true && scrollbarWidth.value > 0 ? 'add' : 'remove'),
215
- updateScrollEvent,
216
- { immediate: true }
214
+ () => (props.container !== true ? 'add' : 'remove'),
215
+ updateScrollEvent
217
216
  )
218
217
 
218
+ props.container !== true && updateScrollEvent('add')
219
+
219
220
  onUnmounted(() => {
220
221
  updateScrollEvent('remove')
221
222
  })
@@ -108,6 +108,9 @@ export default createComponent({
108
108
  && panning.value === false
109
109
  ) || scroll.vertical.size.value <= container.vertical.value + 1
110
110
  )
111
+ scroll.vertical.thumbStart = computed(() =>
112
+ scroll.vertical.percentage.value * (container.vertical.value - scroll.vertical.thumbSize.value)
113
+ )
111
114
  scroll.vertical.thumbSize = computed(() =>
112
115
  Math.round(
113
116
  between(
@@ -118,13 +121,11 @@ export default createComponent({
118
121
  )
119
122
  )
120
123
  scroll.vertical.style = computed(() => {
121
- const thumbSize = scroll.vertical.thumbSize.value
122
- const pos = scroll.vertical.percentage.value * (container.vertical.value - thumbSize)
123
124
  return {
124
125
  ...props.thumbStyle,
125
126
  ...props.verticalThumbStyle,
126
- top: `${ pos }px`,
127
- height: `${ thumbSize }px`
127
+ top: `${ scroll.vertical.thumbStart.value }px`,
128
+ height: `${ scroll.vertical.thumbSize.value }px`
128
129
  }
129
130
  })
130
131
  scroll.vertical.thumbClass = computed(() =>
@@ -149,6 +150,9 @@ export default createComponent({
149
150
  && panning.value === false
150
151
  ) || scroll.horizontal.size.value <= container.horizontal.value + 1
151
152
  )
153
+ scroll.horizontal.thumbStart = computed(() =>
154
+ scroll.horizontal.percentage.value * (container.horizontal.value - scroll.horizontal.thumbSize.value)
155
+ )
152
156
  scroll.horizontal.thumbSize = computed(() =>
153
157
  Math.round(
154
158
  between(
@@ -159,13 +163,11 @@ export default createComponent({
159
163
  )
160
164
  )
161
165
  scroll.horizontal.style = computed(() => {
162
- const thumbSize = scroll.horizontal.thumbSize.value
163
- const pos = scroll.horizontal.percentage.value * (container.horizontal.value - thumbSize)
164
166
  return {
165
167
  ...props.thumbStyle,
166
168
  ...props.horizontalThumbStyle,
167
- left: `${ pos }px`,
168
- width: `${ thumbSize }px`
169
+ left: `${ scroll.horizontal.thumbStart.value }px`,
170
+ width: `${ scroll.horizontal.thumbSize.value }px`
169
171
  }
170
172
  })
171
173
  scroll.horizontal.thumbClass = computed(() =>
@@ -311,8 +313,11 @@ export default createComponent({
311
313
  const data = scroll[ axis ]
312
314
 
313
315
  if (data.thumbHidden.value !== true) {
314
- const pos = evt[ dirProps[ axis ].offset ] - data.thumbSize.value / 2
315
- setScroll(pos / container[ axis ].value * data.size.value, axis)
316
+ const offset = evt[ dirProps[ axis ].offset ]
317
+ if (offset < data.thumbStart.value || offset > data.thumbStart.value + data.thumbSize.value) {
318
+ const pos = offset - data.thumbSize.value / 2
319
+ setScroll(pos / container[ axis ].value * data.size.value, axis)
320
+ }
316
321
 
317
322
  // activate thumb pan
318
323
  if (data.ref.value !== null) {
@@ -47,7 +47,7 @@ export default createComponent({
47
47
  }
48
48
  }
49
49
 
50
- let timer = null, localScrollTarget, parentEl
50
+ let clearTimer = null, localScrollTarget, parentEl
51
51
 
52
52
  watch(() => props.scrollTarget, () => {
53
53
  unconfigureScrollTarget()
@@ -55,9 +55,7 @@ export default createComponent({
55
55
  })
56
56
 
57
57
  function emitEvent () {
58
- clearTimeout(timer)
59
- cancelAnimationFrame(timer)
60
- timer = null
58
+ clearTimer !== null && clearTimer()
61
59
 
62
60
  const top = Math.max(0, getVerticalScrollPosition(localScrollTarget))
63
61
  const left = getHorizontalScrollPosition(localScrollTarget)
@@ -107,10 +105,15 @@ export default createComponent({
107
105
  if (immediately === true || props.debounce === 0 || props.debounce === '0') {
108
106
  emitEvent()
109
107
  }
110
- else if (timer === null) {
111
- timer = props.debounce
112
- ? setTimeout(emitEvent, props.debounce)
113
- : requestAnimationFrame(emitEvent)
108
+ else if (clearTimer === null) {
109
+ const [ timer, fn ] = props.debounce
110
+ ? [ setTimeout(emitEvent, props.debounce), clearTimeout ]
111
+ : [ requestAnimationFrame(emitEvent), cancelAnimationFrame ]
112
+
113
+ clearTimer = () => {
114
+ fn(timer)
115
+ clearTimer = null
116
+ }
114
117
  }
115
118
  }
116
119
 
@@ -122,8 +125,7 @@ export default createComponent({
122
125
  })
123
126
 
124
127
  onBeforeUnmount(() => {
125
- clearTimeout(timer)
126
- cancelAnimationFrame(timer)
128
+ clearTimer !== null && clearTimer()
127
129
  unconfigureScrollTarget()
128
130
  })
129
131
 
@@ -644,34 +644,34 @@ export default createComponent({
644
644
 
645
645
  if (typeof value === 'string' && value.length > 0) {
646
646
  const needle = value.toLocaleLowerCase()
647
+ const findFn = extractFn => {
648
+ const option = props.options.find(opt => extractFn.value(opt).toLocaleLowerCase() === needle)
647
649
 
648
- let fn = opt => getOptionValue.value(opt).toLocaleLowerCase() === needle
649
- let option = props.options.find(fn)
650
+ if (option === void 0) {
651
+ return false
652
+ }
650
653
 
651
- if (option !== void 0) {
652
654
  if (innerValue.value.indexOf(option) === -1) {
653
655
  toggleOption(option)
654
656
  }
655
657
  else {
656
658
  hidePopup()
657
659
  }
658
- }
659
- else {
660
- fn = opt => getOptionLabel.value(opt).toLocaleLowerCase() === needle
661
- option = props.options.find(fn)
662
660
 
663
- if (option !== void 0) {
664
- if (innerValue.value.indexOf(option) === -1) {
665
- toggleOption(option)
666
- }
667
- else {
668
- hidePopup()
669
- }
661
+ return true
662
+ }
663
+ const fillFn = afterFilter => {
664
+ if (findFn(getOptionValue) === true) {
665
+ return
670
666
  }
671
- else {
672
- filter(value, true)
667
+ if (findFn(getOptionLabel) === true || afterFilter === true) {
668
+ return
673
669
  }
670
+
671
+ filter(value, true, () => fillFn(true))
674
672
  }
673
+
674
+ fillFn()
675
675
  }
676
676
  else {
677
677
  state.clearValue(e)
@@ -1050,7 +1050,7 @@ export default createComponent({
1050
1050
  }
1051
1051
  }
1052
1052
 
1053
- function filter (val, keepClosed) {
1053
+ function filter (val, keepClosed, afterUpdateFn) {
1054
1054
  if (props.onFilter === void 0 || (keepClosed !== true && state.focused.value !== true)) {
1055
1055
  return
1056
1056
  }
@@ -1108,6 +1108,7 @@ export default createComponent({
1108
1108
  }
1109
1109
 
1110
1110
  typeof afterFn === 'function' && nextTick(() => { afterFn(proxy) })
1111
+ typeof afterUpdateFn === 'function' && nextTick(() => { afterUpdateFn(proxy) })
1111
1112
  })
1112
1113
  }
1113
1114
  },
@@ -1462,7 +1463,7 @@ export default createComponent({
1462
1463
  h('div', {
1463
1464
  ref: targetRef,
1464
1465
  key: 'd_t',
1465
- class: 'no-outline',
1466
+ class: 'q-select__focus-target',
1466
1467
  id: state.targetUid.value,
1467
1468
  ...comboboxAttrs.value,
1468
1469
  onKeydown: onTargetKeydown,
@@ -1474,7 +1475,7 @@ export default createComponent({
1474
1475
  if (typeof props.autocomplete === 'string' && props.autocomplete.length > 0) {
1475
1476
  child.push(
1476
1477
  h('input', {
1477
- class: 'q-select__autocomplete-input no-outline',
1478
+ class: 'q-select__autocomplete-input',
1478
1479
  autocomplete: props.autocomplete,
1479
1480
  onKeyup: onTargetAutocomplete
1480
1481
  })
@@ -12,7 +12,10 @@
12
12
  &--padding
13
13
  padding-left: 4px
14
14
 
15
+ &__focus-target,
15
16
  &__autocomplete-input
17
+ position: absolute
18
+ outline: 0 !important
16
19
  width: 0
17
20
  height: 0
18
21
  padding: 0
@@ -58,6 +58,8 @@ export default createComponent({
58
58
  expanded: Array, // v-model:expanded
59
59
  selected: {}, // v-model:selected
60
60
 
61
+ noSelectionUnset: Boolean,
62
+
61
63
  defaultExpandAll: Boolean,
62
64
  accordion: Boolean,
63
65
 
@@ -615,7 +617,10 @@ export default createComponent({
615
617
 
616
618
  if (hasSelection.value) {
617
619
  if (meta.selectable) {
618
- emit('update:selected', meta.key !== props.selected ? meta.key : null)
620
+ const val = meta.key !== props.selected ? meta.key : null
621
+ if (props.noSelectionUnset !== true || val !== null) {
622
+ emit('update:selected', val)
623
+ }
619
624
  }
620
625
  }
621
626
  else {
@@ -6,6 +6,7 @@
6
6
  "props": {
7
7
  "nodes": {
8
8
  "type": "Array",
9
+ "tsType": "QTreeNode",
9
10
  "desc": "The array of nodes that designates the tree structure",
10
11
  "required": true,
11
12
  "examples": [ "[ {...}, {...} ]" ],
@@ -106,6 +107,13 @@
106
107
  "category": "state"
107
108
  },
108
109
 
110
+ "no-selection-unset": {
111
+ "type": "Boolean",
112
+ "desc": "Do not allow un-selection when clicking currently selected node",
113
+ "category": "behavior",
114
+ "addedIn": "v2.4.10"
115
+ },
116
+
109
117
  "default-expand-all": {
110
118
  "type": "Boolean",
111
119
  "desc": "Allow the tree to have all its branches expanded, when first rendered",
@@ -436,6 +436,8 @@ export function getRenderer (getPlugin) {
436
436
  abort: state.abort
437
437
  }
438
438
 
439
+ // TODO: the result of this computed, especially the dynamic part, isn't currently typed
440
+ // This result in an error with Volar when accessing the state (eg. files array)
439
441
  const slotScope = computed(() => {
440
442
  const acc = {
441
443
  canAddFiles: canAddFiles.value,
@@ -2,6 +2,7 @@
2
2
  "props": {
3
3
  "factory": {
4
4
  "type": "Function",
5
+ "tsType": "QUploaderFactoryFn",
5
6
  "desc": "Function which should return an Object or a Promise resolving with an Object; For best performance, reference it from your scope and do not define it inline",
6
7
  "params": {
7
8
  "files": {
@@ -112,7 +113,7 @@
112
113
  },
113
114
  "returns": {
114
115
  "type": "String",
115
- "desc": "An array consists of objects with header definitions",
116
+ "desc": "An array consisting of objects with header definitions",
116
117
  "__exemption": [ "examples" ]
117
118
  },
118
119
  "category": "upload"
@@ -402,6 +402,8 @@ export default function (state) {
402
402
  name: props.clearIcon || $q.iconSet.field.clear,
403
403
  tabindex: 0,
404
404
  type: 'button',
405
+ 'aria-hidden': null,
406
+ role: null,
405
407
  onClick: clearValue
406
408
  })
407
409
  ])
@@ -72,6 +72,7 @@
72
72
  "params": {
73
73
  "rejectedEntries": {
74
74
  "type": "Array",
75
+ "tsType": "QRejectedEntry",
75
76
  "desc": "Array of { failedPropValidation: string, file: File } Objects for files that do not pass the validation",
76
77
  "__exemption": ["examples"]
77
78
  }