quasar 2.21.4 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/Mutation.json +1 -1
- package/dist/api/QLinearProgress.json +1 -1
- package/dist/api/QUploader.json +1 -1
- package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
- package/dist/icon-set/eva-icons.umd.prod.js +1 -1
- package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
- package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
- package/dist/icon-set/fontawesome-v6-pro.umd.prod.js +1 -1
- package/dist/icon-set/fontawesome-v6.umd.prod.js +1 -1
- package/dist/icon-set/fontawesome-v7-pro.umd.prod.js +1 -1
- package/dist/icon-set/fontawesome-v7.umd.prod.js +1 -1
- package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
- package/dist/icon-set/line-awesome.umd.prod.js +1 -1
- package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
- package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
- package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
- package/dist/icon-set/material-icons.umd.prod.js +1 -1
- package/dist/icon-set/material-symbols-outlined.umd.prod.js +1 -1
- package/dist/icon-set/material-symbols-rounded.umd.prod.js +1 -1
- package/dist/icon-set/material-symbols-sharp.umd.prod.js +1 -1
- package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
- package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
- package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
- package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
- package/dist/icon-set/mdi-v7.umd.prod.js +1 -1
- package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
- package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
- package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
- package/dist/icon-set/svg-fontawesome-v6.umd.prod.js +1 -1
- package/dist/icon-set/svg-fontawesome-v7.umd.prod.js +1 -1
- package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
- package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
- package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
- package/dist/icon-set/svg-ionicons-v7.umd.prod.js +1 -1
- package/dist/icon-set/svg-ionicons-v8.umd.prod.js +1 -1
- package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-symbols-outlined.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-symbols-rounded.umd.prod.js +1 -1
- package/dist/icon-set/svg-material-symbols-sharp.umd.prod.js +1 -1
- package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
- package/dist/icon-set/svg-mdi-v7.umd.prod.js +1 -1
- package/dist/icon-set/svg-themify.umd.prod.js +1 -1
- package/dist/icon-set/themify.umd.prod.js +1 -1
- package/dist/lang/ar-TN.umd.prod.js +1 -1
- package/dist/lang/ar.umd.prod.js +1 -1
- package/dist/lang/az-Latn.umd.prod.js +1 -1
- package/dist/lang/bg.umd.prod.js +1 -1
- package/dist/lang/bn.umd.prod.js +1 -1
- package/dist/lang/bs-BA.umd.prod.js +1 -1
- package/dist/lang/ca.umd.prod.js +1 -1
- package/dist/lang/cs.umd.prod.js +1 -1
- package/dist/lang/da.umd.prod.js +1 -1
- package/dist/lang/de-CH.umd.prod.js +1 -1
- package/dist/lang/de-DE.umd.prod.js +1 -1
- package/dist/lang/de.umd.prod.js +1 -1
- package/dist/lang/el.umd.prod.js +1 -1
- package/dist/lang/en-GB.umd.prod.js +1 -1
- package/dist/lang/en-US.umd.prod.js +1 -1
- package/dist/lang/eo.umd.prod.js +1 -1
- package/dist/lang/es.umd.prod.js +1 -1
- package/dist/lang/et.umd.prod.js +1 -1
- package/dist/lang/eu.umd.prod.js +1 -1
- package/dist/lang/fa-IR.umd.prod.js +1 -1
- package/dist/lang/fa.umd.prod.js +1 -1
- package/dist/lang/fi.umd.prod.js +1 -1
- package/dist/lang/fr.umd.prod.js +1 -1
- package/dist/lang/gn.umd.prod.js +1 -1
- package/dist/lang/he.umd.prod.js +1 -1
- package/dist/lang/hi.umd.prod.js +1 -1
- package/dist/lang/hr.umd.prod.js +1 -1
- package/dist/lang/hu.umd.prod.js +1 -1
- package/dist/lang/id.umd.prod.js +1 -1
- package/dist/lang/is.umd.prod.js +1 -1
- package/dist/lang/it.umd.prod.js +1 -1
- package/dist/lang/ja.umd.prod.js +1 -1
- package/dist/lang/kk.umd.prod.js +1 -1
- package/dist/lang/km.umd.prod.js +1 -1
- package/dist/lang/ko-KR.umd.prod.js +1 -1
- package/dist/lang/kur-CKB.umd.prod.js +1 -1
- package/dist/lang/lb.umd.prod.js +1 -1
- package/dist/lang/lt.umd.prod.js +1 -1
- package/dist/lang/lu.umd.prod.js +1 -1
- package/dist/lang/lv.umd.prod.js +1 -1
- package/dist/lang/mk.umd.prod.js +1 -1
- package/dist/lang/ml.umd.prod.js +1 -1
- package/dist/lang/mm.umd.prod.js +1 -1
- package/dist/lang/ms-MY.umd.prod.js +1 -1
- package/dist/lang/ms.umd.prod.js +1 -1
- package/dist/lang/my.umd.prod.js +1 -1
- package/dist/lang/nb-NO.umd.prod.js +1 -1
- package/dist/lang/nl.umd.prod.js +1 -1
- package/dist/lang/pl.umd.prod.js +1 -1
- package/dist/lang/pt-BR.umd.prod.js +1 -1
- package/dist/lang/pt.umd.prod.js +1 -1
- package/dist/lang/ro.umd.prod.js +1 -1
- package/dist/lang/ru.umd.prod.js +1 -1
- package/dist/lang/sk.umd.prod.js +1 -1
- package/dist/lang/sl.umd.prod.js +1 -1
- package/dist/lang/sm.umd.prod.js +1 -1
- package/dist/lang/sq.umd.prod.js +1 -1
- package/dist/lang/sr-CYR.umd.prod.js +1 -1
- package/dist/lang/sr.umd.prod.js +1 -1
- package/dist/lang/sv.umd.prod.js +1 -1
- package/dist/lang/ta.umd.prod.js +1 -1
- package/dist/lang/th.umd.prod.js +1 -1
- package/dist/lang/tl.umd.prod.js +1 -1
- package/dist/lang/tr.umd.prod.js +1 -1
- package/dist/lang/ug.umd.prod.js +1 -1
- package/dist/lang/uk.umd.prod.js +1 -1
- package/dist/lang/ur-PK.umd.prod.js +1 -1
- package/dist/lang/uz-Cyrl.umd.prod.js +1 -1
- package/dist/lang/uz-Latn.umd.prod.js +1 -1
- package/dist/lang/vi.umd.prod.js +1 -1
- package/dist/lang/zh-CN.umd.prod.js +1 -1
- package/dist/lang/zh-TW.umd.prod.js +1 -1
- package/dist/quasar.client.js +654 -207
- package/dist/quasar.css +20 -11
- package/dist/quasar.prod.css +1 -1
- package/dist/quasar.rtl.css +25 -20
- package/dist/quasar.rtl.prod.css +1 -1
- package/dist/quasar.sass +21 -12
- package/dist/quasar.server.prod.cjs +35 -35
- package/dist/quasar.server.prod.js +25 -25
- package/dist/quasar.umd.js +654 -206
- package/dist/quasar.umd.prod.js +19 -19
- package/dist/transforms/import-map.json +1 -1
- package/dist/types/composables.d.ts +15 -5
- package/dist/types/index.d.ts +5 -5
- package/dist/web-types/web-types.json +1 -1
- package/package.json +6 -6
- package/src/components/btn/QBtn.js +1 -1
- package/src/components/carousel/QCarousel.js +2 -0
- package/src/components/chip/QChip.js +20 -4
- package/src/components/date/QDate.js +9 -2
- package/src/components/dialog/QDialog.js +9 -2
- package/src/components/editor/QEditor.js +18 -3
- package/src/components/expansion-item/QExpansionItem.js +6 -1
- package/src/components/fab/QFab.sass +11 -9
- package/src/components/input/QInput.js +42 -1
- package/src/components/input/QInput.json +7 -0
- package/src/components/input/use-mask.js +12 -4
- package/src/components/item/QItem.js +10 -1
- package/src/components/linear-progress/QLinearProgress.js +4 -11
- package/src/components/linear-progress/QLinearProgress.json +1 -1
- package/src/components/linear-progress/QLinearProgress.sass +7 -2
- package/src/components/menu/QMenu.js +9 -2
- package/src/components/range/QRange.js +2 -1
- package/src/components/rating/QRating.js +27 -8
- package/src/components/rating/QRating.sass +4 -0
- package/src/components/resize-observer/QResizeObserver.js +14 -3
- package/src/components/scroll-area/QScrollArea.js +69 -17
- package/src/components/slide-item/QSlideItem.js +3 -2
- package/src/components/slider/use-slider.js +7 -3
- package/src/components/stepper/StepHeader.js +11 -2
- package/src/components/table/QTable.js +11 -9
- package/src/components/table/QTh.js +20 -1
- package/src/components/table/table-column-selection.js +9 -1
- package/src/components/table/table-pagination.js +5 -2
- package/src/components/table/table-row-selection.js +10 -10
- package/src/components/tabs/QTabs.js +5 -6
- package/src/components/time/QTime.js +35 -10
- package/src/components/tooltip/QTooltip.js +112 -6
- package/src/components/tree/QTree.js +124 -4
- package/src/components/uploader/uploader-core.js +19 -4
- package/src/components/uploader/xhr-uploader-plugin.js +93 -43
- package/src/components/uploader/xhr-uploader-plugin.json +4 -4
- package/src/composables/private.use-field/use-field.js +2 -0
- package/src/composables/private.use-file/use-file.js +21 -12
- package/src/composables/private.use-fullscreen/use-fullscreen.js +71 -11
- package/src/composables/private.use-fullscreen/use-fullscreen.test.js +158 -0
- package/src/composables/private.use-model-toggle/use-model-toggle.js +2 -0
- package/src/composables/private.use-panel/use-panel.js +6 -2
- package/src/composables/use-animation-frame/use-animation-frame.js +47 -0
- package/src/composables/use-animation-frame/use-animation-frame.test.js +122 -0
- package/src/composables/use-interval/use-interval.js +8 -0
- package/src/composables/use-render-cache/use-render-cache.js +15 -10
- package/src/composables/use-timeout/use-timeout.js +8 -0
- package/src/composables.js +1 -1
- package/src/directives/intersection/Intersection.js +2 -2
- package/src/directives/mutation/Mutation.json +1 -1
- package/src/directives/touch-repeat/TouchRepeat.js +3 -1
- package/src/plugins/cookies/Cookies.js +29 -13
- package/src/plugins/meta/Meta.js +57 -9
- package/src/utils/extend/extend.js +4 -0
- package/src/utils/extend/extend.test.js +29 -0
- package/src/utils/private.click-outside/click-outside.js +27 -12
- package/src/utils/private.click-outside/click-outside.test.js +132 -0
- package/src/utils/scroll/prevent-scroll.js +4 -4
|
@@ -31,6 +31,7 @@ function injectPlugin({ props, emit, helpers }) {
|
|
|
31
31
|
const xhrs = ref([])
|
|
32
32
|
const promises = ref([])
|
|
33
33
|
const workingThreads = ref(0)
|
|
34
|
+
const abortedPromises = new WeakSet()
|
|
34
35
|
|
|
35
36
|
const xhrProps = computed(() => ({
|
|
36
37
|
url: getFn(props.url),
|
|
@@ -46,14 +47,17 @@ function injectPlugin({ props, emit, helpers }) {
|
|
|
46
47
|
const isUploading = computed(() => workingThreads.value > 0)
|
|
47
48
|
const isBusy = computed(() => promises.value.length !== 0)
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
const getLiveFiles = files =>
|
|
51
|
+
files.filter(file => helpers.files.value.includes(file))
|
|
50
52
|
|
|
51
53
|
function abort() {
|
|
52
54
|
xhrs.value.forEach(x => {
|
|
53
55
|
x.abort()
|
|
54
56
|
})
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
promises.value.forEach(promise => {
|
|
59
|
+
abortedPromises.add(promise)
|
|
60
|
+
})
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
function upload() {
|
|
@@ -72,54 +76,82 @@ function injectPlugin({ props, emit, helpers }) {
|
|
|
72
76
|
function runFactory(files) {
|
|
73
77
|
workingThreads.value++
|
|
74
78
|
|
|
79
|
+
const failed = (err, promise) => {
|
|
80
|
+
if (helpers.isAlive()) {
|
|
81
|
+
if (promise !== void 0) {
|
|
82
|
+
promises.value = promises.value.filter(p => p !== promise)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const liveFiles = getLiveFiles(files)
|
|
86
|
+
|
|
87
|
+
if (liveFiles.length !== 0) {
|
|
88
|
+
helpers.queuedFiles.value.push(...liveFiles)
|
|
89
|
+
liveFiles.forEach(f => {
|
|
90
|
+
helpers.updateFileStatus(f, 'failed')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
emit('factoryFailed', err, liveFiles)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
workingThreads.value--
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
75
100
|
if (typeof props.factory !== 'function') {
|
|
76
|
-
|
|
101
|
+
startUpload({})
|
|
77
102
|
return
|
|
78
103
|
}
|
|
79
104
|
|
|
80
|
-
|
|
105
|
+
let res
|
|
106
|
+
try {
|
|
107
|
+
res = props.factory(files)
|
|
108
|
+
} catch (err) {
|
|
109
|
+
failed(err)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
81
112
|
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
'factoryFailed',
|
|
85
|
-
new Error('QUploader: factory() does not return properly'),
|
|
86
|
-
files
|
|
87
|
-
)
|
|
88
|
-
workingThreads.value--
|
|
113
|
+
if (Object(res) !== res) {
|
|
114
|
+
failed(new Error('QUploader: factory() does not return properly'))
|
|
89
115
|
} else if (
|
|
90
116
|
typeof res.catch === 'function' &&
|
|
91
117
|
typeof res.then === 'function'
|
|
92
118
|
) {
|
|
93
119
|
promises.value.push(res)
|
|
94
120
|
|
|
95
|
-
const failed = err => {
|
|
96
|
-
if (helpers.isAlive()) {
|
|
97
|
-
promises.value = promises.value.filter(p => p !== res)
|
|
98
|
-
|
|
99
|
-
if (promises.value.length === 0) abortPromises = false
|
|
100
|
-
|
|
101
|
-
helpers.queuedFiles.value.push(...files)
|
|
102
|
-
files.forEach(f => {
|
|
103
|
-
helpers.updateFileStatus(f, 'failed')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
emit('factoryFailed', err, files)
|
|
107
|
-
workingThreads.value--
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
121
|
res
|
|
112
122
|
.then(factory => {
|
|
113
|
-
if (
|
|
114
|
-
failed(new Error('Aborted'))
|
|
123
|
+
if (abortedPromises.has(res)) {
|
|
124
|
+
failed(new Error('Aborted'), res)
|
|
125
|
+
} else if (Object(factory) !== factory) {
|
|
126
|
+
failed(
|
|
127
|
+
new Error('QUploader: factory() does not return properly'),
|
|
128
|
+
res
|
|
129
|
+
)
|
|
115
130
|
} else if (helpers.isAlive()) {
|
|
116
131
|
promises.value = promises.value.filter(p => p !== res)
|
|
117
|
-
|
|
132
|
+
startUpload(factory)
|
|
118
133
|
}
|
|
119
134
|
})
|
|
120
|
-
.catch(
|
|
135
|
+
.catch(err => {
|
|
136
|
+
failed(err, res)
|
|
137
|
+
})
|
|
121
138
|
} else {
|
|
122
|
-
|
|
139
|
+
startUpload(res)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function startUpload(factory) {
|
|
143
|
+
const liveFiles = getLiveFiles(files)
|
|
144
|
+
|
|
145
|
+
if (liveFiles.length === 0) {
|
|
146
|
+
workingThreads.value--
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
performUpload(liveFiles, factory)
|
|
152
|
+
} catch (err) {
|
|
153
|
+
failed(err)
|
|
154
|
+
}
|
|
123
155
|
}
|
|
124
156
|
}
|
|
125
157
|
|
|
@@ -136,6 +168,10 @@ function injectPlugin({ props, emit, helpers }) {
|
|
|
136
168
|
|
|
137
169
|
if (!url) {
|
|
138
170
|
console.error('q-uploader: invalid or no URL specified')
|
|
171
|
+
helpers.queuedFiles.value.push(...files)
|
|
172
|
+
files.forEach(f => {
|
|
173
|
+
helpers.updateFileStatus(f, 'failed')
|
|
174
|
+
})
|
|
139
175
|
workingThreads.value--
|
|
140
176
|
return
|
|
141
177
|
}
|
|
@@ -166,7 +202,7 @@ function injectPlugin({ props, emit, helpers }) {
|
|
|
166
202
|
let size = localUploadedSize - uploadIndexSize
|
|
167
203
|
for (let i = uploadIndex; size > 0 && i < files.length; i++) {
|
|
168
204
|
const file = files[i],
|
|
169
|
-
uploaded = size
|
|
205
|
+
uploaded = size >= file.size
|
|
170
206
|
|
|
171
207
|
if (uploaded) {
|
|
172
208
|
size -= file.size
|
|
@@ -186,19 +222,33 @@ function injectPlugin({ props, emit, helpers }) {
|
|
|
186
222
|
if (xhr.readyState < 4) return
|
|
187
223
|
|
|
188
224
|
if (xhr.status && xhr.status < 400) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
225
|
+
const liveFiles = getLiveFiles(files)
|
|
226
|
+
const liveUploadSize = liveFiles.reduce(
|
|
227
|
+
(total, file) => total + file.size,
|
|
228
|
+
0
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
helpers.uploadedSize.value += liveUploadSize - localUploadedSize
|
|
232
|
+
|
|
233
|
+
if (liveFiles.length !== 0) {
|
|
234
|
+
helpers.uploadedFiles.value.push(...liveFiles)
|
|
235
|
+
liveFiles.forEach(f => {
|
|
236
|
+
helpers.updateFileStatus(f, 'uploaded')
|
|
237
|
+
})
|
|
238
|
+
emit('uploaded', { files: liveFiles, xhr })
|
|
239
|
+
}
|
|
194
240
|
} else {
|
|
195
241
|
aborted = true
|
|
196
242
|
helpers.uploadedSize.value -= localUploadedSize
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
243
|
+
|
|
244
|
+
const liveFiles = getLiveFiles(files)
|
|
245
|
+
if (liveFiles.length !== 0) {
|
|
246
|
+
helpers.queuedFiles.value.push(...liveFiles)
|
|
247
|
+
liveFiles.forEach(f => {
|
|
248
|
+
helpers.updateFileStatus(f, 'failed')
|
|
249
|
+
})
|
|
250
|
+
emit('failed', { files: liveFiles, xhr })
|
|
251
|
+
}
|
|
202
252
|
}
|
|
203
253
|
|
|
204
254
|
workingThreads.value--
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
}
|
|
104
104
|
},
|
|
105
105
|
"returns": {
|
|
106
|
-
"type": "
|
|
106
|
+
"type": "Array",
|
|
107
107
|
"desc": "An array consisting of objects with header definitions"
|
|
108
108
|
},
|
|
109
109
|
"category": "upload"
|
|
@@ -138,8 +138,8 @@
|
|
|
138
138
|
}
|
|
139
139
|
},
|
|
140
140
|
"returns": {
|
|
141
|
-
"type": "
|
|
142
|
-
"desc": "An array
|
|
141
|
+
"type": "Array",
|
|
142
|
+
"desc": "An array consisting of objects with additional field definitions (used by FormData to be uploaded)"
|
|
143
143
|
},
|
|
144
144
|
"category": "upload"
|
|
145
145
|
},
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
},
|
|
265
265
|
|
|
266
266
|
"factory-failed": {
|
|
267
|
-
"desc": "Emitted when factory function
|
|
267
|
+
"desc": "Emitted when the factory function throws, returns an invalid value, or supplies a Promise which is rejected or aborted",
|
|
268
268
|
"params": {
|
|
269
269
|
"err": {
|
|
270
270
|
"type": "Error",
|
|
@@ -105,7 +105,9 @@ export default function useFile({
|
|
|
105
105
|
files = filterFiles(files, rejectedFiles, 'accept', file =>
|
|
106
106
|
extensions.value.some(
|
|
107
107
|
ext =>
|
|
108
|
-
|
|
108
|
+
(ext.endsWith('/')
|
|
109
|
+
? file.type.toUpperCase().startsWith(ext)
|
|
110
|
+
: file.type.toUpperCase() === ext) ||
|
|
109
111
|
file.name.toUpperCase().endsWith(ext)
|
|
110
112
|
)
|
|
111
113
|
)
|
|
@@ -137,19 +139,23 @@ export default function useFile({
|
|
|
137
139
|
|
|
138
140
|
// Compute key to use for each file
|
|
139
141
|
files.forEach(file => {
|
|
140
|
-
file.__key =
|
|
141
|
-
file.webkitRelativePath
|
|
142
|
+
file.__key = JSON.stringify([
|
|
143
|
+
file.webkitRelativePath,
|
|
144
|
+
file.lastModified,
|
|
145
|
+
file.name,
|
|
146
|
+
file.size
|
|
147
|
+
])
|
|
142
148
|
})
|
|
143
149
|
|
|
144
150
|
if (append) {
|
|
145
151
|
// Avoid duplicate files
|
|
146
|
-
const
|
|
147
|
-
files = filterFiles(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
152
|
+
const filenameSet = new Set(currentFileList.map(entry => entry.__key))
|
|
153
|
+
files = filterFiles(files, rejectedFiles, 'duplicate', file => {
|
|
154
|
+
if (filenameSet.has(file.__key)) return false
|
|
155
|
+
|
|
156
|
+
filenameSet.add(file.__key)
|
|
157
|
+
return true
|
|
158
|
+
})
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
if (files.length === 0) {
|
|
@@ -162,8 +168,11 @@ export default function useFile({
|
|
|
162
168
|
: 0
|
|
163
169
|
|
|
164
170
|
files = filterFiles(files, rejectedFiles, 'max-total-size', file => {
|
|
165
|
-
size
|
|
166
|
-
|
|
171
|
+
const newSize = size + file.size
|
|
172
|
+
if (newSize > maxTotalSizeNumber.value) return false
|
|
173
|
+
|
|
174
|
+
size = newSize
|
|
175
|
+
return true
|
|
167
176
|
})
|
|
168
177
|
|
|
169
178
|
if (files.length === 0) {
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getCurrentInstance,
|
|
3
|
+
onActivated,
|
|
3
4
|
onBeforeMount,
|
|
4
5
|
onBeforeUnmount,
|
|
6
|
+
onDeactivated,
|
|
5
7
|
onMounted,
|
|
6
8
|
ref,
|
|
7
9
|
watch
|
|
8
10
|
} from 'vue'
|
|
9
11
|
|
|
10
12
|
import History from '../../plugins/private.history/History.js'
|
|
11
|
-
import { vmHasRouter } from '../../utils/private.vm/vm.js'
|
|
13
|
+
import { vmHasRouter, vmIsDestroyed } from '../../utils/private.vm/vm.js'
|
|
14
|
+
import useTick from '../use-tick/use-tick.js'
|
|
15
|
+
import useAnimationFrame from '../use-animation-frame/use-animation-frame.js'
|
|
16
|
+
import {
|
|
17
|
+
getHorizontalScrollPosition,
|
|
18
|
+
getVerticalScrollPosition
|
|
19
|
+
} from '../../utils/scroll/scroll.js'
|
|
12
20
|
|
|
13
21
|
let counter = 0
|
|
22
|
+
let restoreState = null
|
|
14
23
|
|
|
15
24
|
export const useFullscreenProps = {
|
|
16
25
|
fullscreen: Boolean,
|
|
@@ -24,7 +33,10 @@ export default function useFullscreen() {
|
|
|
24
33
|
const { props, emit, proxy } = vm
|
|
25
34
|
|
|
26
35
|
let historyEntry, fullscreenFillerNode
|
|
36
|
+
|
|
27
37
|
const inFullscreen = ref(false)
|
|
38
|
+
const { registerTick, removeTick } = useTick()
|
|
39
|
+
const { registerAnimationFrame, removeAnimationFrame } = useAnimationFrame()
|
|
28
40
|
|
|
29
41
|
if (vmHasRouter(vm)) {
|
|
30
42
|
watch(
|
|
@@ -56,7 +68,19 @@ export default function useFullscreen() {
|
|
|
56
68
|
}
|
|
57
69
|
|
|
58
70
|
function setFullscreen() {
|
|
59
|
-
if (inFullscreen.value) return
|
|
71
|
+
if (vmIsDestroyed(vm) || inFullscreen.value) return
|
|
72
|
+
|
|
73
|
+
removeAnimationFrame()
|
|
74
|
+
removeTick()
|
|
75
|
+
|
|
76
|
+
if (counter === 0) {
|
|
77
|
+
restoreState = {
|
|
78
|
+
left: getHorizontalScrollPosition(window),
|
|
79
|
+
top: getVerticalScrollPosition(window),
|
|
80
|
+
width: window.innerWidth,
|
|
81
|
+
height: window.innerHeight
|
|
82
|
+
}
|
|
83
|
+
}
|
|
60
84
|
|
|
61
85
|
inFullscreen.value = true
|
|
62
86
|
proxy.$el.replaceWith(fullscreenFillerNode)
|
|
@@ -73,7 +97,7 @@ export default function useFullscreen() {
|
|
|
73
97
|
History.add(historyEntry)
|
|
74
98
|
}
|
|
75
99
|
|
|
76
|
-
function
|
|
100
|
+
function exitFullscreenImpl(shouldRestoreElement) {
|
|
77
101
|
if (!inFullscreen.value) return
|
|
78
102
|
|
|
79
103
|
if (historyEntry !== void 0) {
|
|
@@ -81,20 +105,48 @@ export default function useFullscreen() {
|
|
|
81
105
|
historyEntry = void 0
|
|
82
106
|
}
|
|
83
107
|
|
|
84
|
-
|
|
108
|
+
if (shouldRestoreElement === true) {
|
|
109
|
+
fullscreenFillerNode.replaceWith(proxy.$el)
|
|
110
|
+
} else {
|
|
111
|
+
fullscreenFillerNode.remove()
|
|
112
|
+
}
|
|
113
|
+
|
|
85
114
|
inFullscreen.value = false
|
|
86
115
|
|
|
87
116
|
counter = Math.max(0, counter - 1)
|
|
117
|
+
if (counter !== 0) return
|
|
88
118
|
|
|
89
|
-
|
|
90
|
-
document.body.classList.remove('q-body--fullscreen-mixin')
|
|
119
|
+
document.body.classList.remove('q-body--fullscreen-mixin')
|
|
91
120
|
|
|
92
|
-
|
|
93
|
-
|
|
121
|
+
if (restoreState === null) return
|
|
122
|
+
|
|
123
|
+
const { left, top, width, height } = restoreState
|
|
124
|
+
restoreState = null
|
|
125
|
+
|
|
126
|
+
if (vmIsDestroyed(vm)) return
|
|
127
|
+
|
|
128
|
+
registerTick(() => {
|
|
129
|
+
if (counter !== 0) return
|
|
130
|
+
registerAnimationFrame(() => {
|
|
131
|
+
if (counter !== 0) return
|
|
132
|
+
|
|
133
|
+
if (window.innerWidth !== width || window.innerHeight !== height) {
|
|
134
|
+
/**
|
|
135
|
+
* If user has resized the window while in fullscreen mode,
|
|
136
|
+
* we cannot restore the scroll position because it will be wrong.
|
|
137
|
+
* So we just scroll the element into view instead,
|
|
138
|
+
* which is the best we can do.
|
|
139
|
+
*/
|
|
94
140
|
proxy.$el.scrollIntoView()
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
141
|
+
} else {
|
|
142
|
+
window.scrollTo(left, top)
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function exitFullscreen() {
|
|
149
|
+
exitFullscreenImpl(true)
|
|
98
150
|
}
|
|
99
151
|
|
|
100
152
|
onBeforeMount(() => {
|
|
@@ -105,6 +157,14 @@ export default function useFullscreen() {
|
|
|
105
157
|
if (props.fullscreen) setFullscreen()
|
|
106
158
|
})
|
|
107
159
|
|
|
160
|
+
onDeactivated(() => {
|
|
161
|
+
// A direct child of KeepAlive has already been moved to its storage
|
|
162
|
+
// container at this point. Its filler must not move it back into the DOM.
|
|
163
|
+
exitFullscreenImpl(proxy.$el.isConnected)
|
|
164
|
+
})
|
|
165
|
+
onActivated(() => {
|
|
166
|
+
if (props.fullscreen) setFullscreen()
|
|
167
|
+
})
|
|
108
168
|
onBeforeUnmount(exitFullscreen)
|
|
109
169
|
|
|
110
170
|
// expose public methods
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from 'vitest'
|
|
2
|
+
import { flushPromises, mount } from '@vue/test-utils'
|
|
3
|
+
import { KeepAlive, defineComponent, h, ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
import useFullscreen, {
|
|
6
|
+
useFullscreenEmits,
|
|
7
|
+
useFullscreenProps
|
|
8
|
+
} from './use-fullscreen.js'
|
|
9
|
+
|
|
10
|
+
let wrapper, mountTarget
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
wrapper?.unmount()
|
|
14
|
+
mountTarget?.remove()
|
|
15
|
+
wrapper = mountTarget = void 0
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const FullscreenComponent = defineComponent({
|
|
19
|
+
name: 'FullscreenComponent',
|
|
20
|
+
props: useFullscreenProps,
|
|
21
|
+
emits: useFullscreenEmits,
|
|
22
|
+
|
|
23
|
+
setup() {
|
|
24
|
+
const { inFullscreen } = useFullscreen()
|
|
25
|
+
|
|
26
|
+
return () =>
|
|
27
|
+
h(
|
|
28
|
+
'section',
|
|
29
|
+
{
|
|
30
|
+
'data-test': 'fullscreen-component',
|
|
31
|
+
'data-fullscreen': inFullscreen.value
|
|
32
|
+
},
|
|
33
|
+
'Fullscreen component'
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const OtherComponent = defineComponent({
|
|
39
|
+
name: 'OtherComponent',
|
|
40
|
+
setup: () => () => h('section', { 'data-test': 'other-component' })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('[useFullscreen API]', () => {
|
|
44
|
+
describe('[Variables]', () => {
|
|
45
|
+
describe('[(variable)useFullscreenProps]', () => {
|
|
46
|
+
test('is defined correctly', () => {
|
|
47
|
+
expect(useFullscreenProps).toBeTypeOf('object')
|
|
48
|
+
expect(Object.keys(useFullscreenProps)).not.toHaveLength(0)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('[(variable)useFullscreenEmits]', () => {
|
|
53
|
+
test('is defined correctly', () => {
|
|
54
|
+
expect(Array.isArray(useFullscreenEmits)).toBe(true)
|
|
55
|
+
expect(useFullscreenEmits).not.toHaveLength(0)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('[Functions]', () => {
|
|
61
|
+
describe('[(function)default]', () => {
|
|
62
|
+
test('does not move a deactivated root back into the live DOM', async () => {
|
|
63
|
+
const showFullscreen = ref(true)
|
|
64
|
+
|
|
65
|
+
mountTarget = document.createElement('div')
|
|
66
|
+
document.body.append(mountTarget)
|
|
67
|
+
|
|
68
|
+
wrapper = mount(
|
|
69
|
+
defineComponent({
|
|
70
|
+
setup: () => () =>
|
|
71
|
+
h(KeepAlive, null, () =>
|
|
72
|
+
showFullscreen.value === true
|
|
73
|
+
? h(FullscreenComponent, { fullscreen: true })
|
|
74
|
+
: h(OtherComponent)
|
|
75
|
+
)
|
|
76
|
+
}),
|
|
77
|
+
{ attachTo: mountTarget }
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
await flushPromises()
|
|
81
|
+
|
|
82
|
+
const getFullscreenElement = () =>
|
|
83
|
+
document.body.querySelector('[data-test="fullscreen-component"]')
|
|
84
|
+
|
|
85
|
+
expect(getFullscreenElement()).not.toBeNull()
|
|
86
|
+
expect(getFullscreenElement().dataset.fullscreen).toBe('true')
|
|
87
|
+
expect(
|
|
88
|
+
document.body.classList.contains('q-body--fullscreen-mixin')
|
|
89
|
+
).toBe(true)
|
|
90
|
+
|
|
91
|
+
showFullscreen.value = false
|
|
92
|
+
await flushPromises()
|
|
93
|
+
|
|
94
|
+
expect(getFullscreenElement()).toBeNull()
|
|
95
|
+
expect(wrapper.find('[data-test="other-component"]').exists()).toBe(
|
|
96
|
+
true
|
|
97
|
+
)
|
|
98
|
+
expect(
|
|
99
|
+
document.body.classList.contains('q-body--fullscreen-mixin')
|
|
100
|
+
).toBe(false)
|
|
101
|
+
|
|
102
|
+
showFullscreen.value = true
|
|
103
|
+
await flushPromises()
|
|
104
|
+
|
|
105
|
+
expect(getFullscreenElement()).not.toBeNull()
|
|
106
|
+
expect(getFullscreenElement().dataset.fullscreen).toBe('true')
|
|
107
|
+
expect(
|
|
108
|
+
document.body.classList.contains('q-body--fullscreen-mixin')
|
|
109
|
+
).toBe(true)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('restores a nested fullscreen component to cached content', async () => {
|
|
113
|
+
const showFullscreen = ref(true)
|
|
114
|
+
const CachedComponent = defineComponent({
|
|
115
|
+
name: 'CachedComponent',
|
|
116
|
+
setup: () => () =>
|
|
117
|
+
h('main', null, [h(FullscreenComponent, { fullscreen: true })])
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
mountTarget = document.createElement('div')
|
|
121
|
+
document.body.append(mountTarget)
|
|
122
|
+
|
|
123
|
+
wrapper = mount(
|
|
124
|
+
defineComponent({
|
|
125
|
+
setup: () => () =>
|
|
126
|
+
h(KeepAlive, null, () =>
|
|
127
|
+
showFullscreen.value === true
|
|
128
|
+
? h(CachedComponent)
|
|
129
|
+
: h(OtherComponent)
|
|
130
|
+
)
|
|
131
|
+
}),
|
|
132
|
+
{ attachTo: mountTarget }
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
await flushPromises()
|
|
136
|
+
|
|
137
|
+
const getFullscreenElement = () =>
|
|
138
|
+
document.body.querySelector('[data-test="fullscreen-component"]')
|
|
139
|
+
|
|
140
|
+
expect(getFullscreenElement()).not.toBeNull()
|
|
141
|
+
|
|
142
|
+
showFullscreen.value = false
|
|
143
|
+
await flushPromises()
|
|
144
|
+
|
|
145
|
+
expect(getFullscreenElement()).toBeNull()
|
|
146
|
+
expect(wrapper.find('[data-test="other-component"]').exists()).toBe(
|
|
147
|
+
true
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
showFullscreen.value = true
|
|
151
|
+
await flushPromises()
|
|
152
|
+
|
|
153
|
+
expect(getFullscreenElement()).not.toBeNull()
|
|
154
|
+
expect(getFullscreenElement().dataset.fullscreen).toBe('true')
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
})
|
|
@@ -21,6 +21,7 @@ export default function useModelToggle({
|
|
|
21
21
|
hideOnRouteChange, // optional
|
|
22
22
|
handleShow, // optional
|
|
23
23
|
handleHide, // optional
|
|
24
|
+
handleRouteChange, // optional
|
|
24
25
|
processOnMount // optional
|
|
25
26
|
}) {
|
|
26
27
|
const vm = getCurrentInstance()
|
|
@@ -127,6 +128,7 @@ export default function useModelToggle({
|
|
|
127
128
|
() => proxy.$route.fullPath,
|
|
128
129
|
() => {
|
|
129
130
|
if (hideOnRouteChange.value && showing.value) {
|
|
131
|
+
handleRouteChange?.()
|
|
130
132
|
hide()
|
|
131
133
|
}
|
|
132
134
|
}
|
|
@@ -108,11 +108,15 @@ export default function usePanel() {
|
|
|
108
108
|
])
|
|
109
109
|
|
|
110
110
|
const transitionPrev = computed(
|
|
111
|
-
() =>
|
|
111
|
+
() =>
|
|
112
|
+
props.transitionPrev ||
|
|
113
|
+
`slide-${props.vertical ? 'down' : proxy.$q.lang.rtl ? 'left' : 'right'}`
|
|
112
114
|
)
|
|
113
115
|
|
|
114
116
|
const transitionNext = computed(
|
|
115
|
-
() =>
|
|
117
|
+
() =>
|
|
118
|
+
props.transitionNext ||
|
|
119
|
+
`slide-${props.vertical ? 'up' : proxy.$q.lang.rtl ? 'right' : 'left'}`
|
|
116
120
|
)
|
|
117
121
|
|
|
118
122
|
const transitionStyle = computed(
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getCurrentInstance, onBeforeUnmount, onDeactivated } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { vmIsDestroyed } from '../../utils/private.vm/vm.js'
|
|
4
|
+
import { noop } from '../../utils/event/event.js'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Usage:
|
|
8
|
+
* registerAnimationFrame(fn)
|
|
9
|
+
* removeAnimationFrame()
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export default function useAnimationFrame() {
|
|
13
|
+
if (__QUASAR_SSR_SERVER__) {
|
|
14
|
+
return {
|
|
15
|
+
removeAnimationFrame: noop,
|
|
16
|
+
registerAnimationFrame: noop
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let frameId = null
|
|
21
|
+
const vm = getCurrentInstance()
|
|
22
|
+
|
|
23
|
+
function removeAnimationFrame() {
|
|
24
|
+
if (frameId !== null) {
|
|
25
|
+
cancelAnimationFrame(frameId)
|
|
26
|
+
frameId = null
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onDeactivated(removeAnimationFrame)
|
|
31
|
+
onBeforeUnmount(removeAnimationFrame)
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
removeAnimationFrame,
|
|
35
|
+
|
|
36
|
+
registerAnimationFrame(fn) {
|
|
37
|
+
removeAnimationFrame()
|
|
38
|
+
|
|
39
|
+
if (!vmIsDestroyed(vm)) {
|
|
40
|
+
frameId = requestAnimationFrame(() => {
|
|
41
|
+
frameId = null
|
|
42
|
+
fn()
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|