refactor: add strict typing to refs

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/877/head
Varun Patil 2023-10-15 17:19:35 -07:00
parent 2144ba0d64
commit 8d13f0be98
24 changed files with 585 additions and 108 deletions

336
package-lock.json generated
View File

@ -45,6 +45,7 @@
"playwright": "^1.39.0",
"prettier": "^3.0.3",
"typescript": "^5.2.2",
"vue-tsc": "^1.8.19",
"workbox-webpack-plugin": "^7.0.0"
},
"engines": {
@ -2826,6 +2827,61 @@
"is-function": "^1.0.1"
}
},
"node_modules/@volar/language-core": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.4.tgz",
"integrity": "sha512-Na69qA6uwVIdA0rHuOc2W3pHtVQQO8hCNim7FOaKNpRJh0oAFnu5r9i7Oopo5C4cnELZkPNjTrbmpcCTiW+CMQ==",
"dev": true,
"dependencies": {
"@volar/source-map": "1.10.4"
}
},
"node_modules/@volar/source-map": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.4.tgz",
"integrity": "sha512-RxZdUEL+pV8p+SMqnhVjzy5zpb1QRZTlcwSk4bdcBO7yOu4rtEWqDGahVCEj4CcXour+0yJUMrMczfSCpP9Uxg==",
"dev": true,
"dependencies": {
"muggle-string": "^0.3.1"
}
},
"node_modules/@volar/typescript": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.4.tgz",
"integrity": "sha512-BCCUEBASBEMCrz7qmNSi2hBEWYsXD0doaktRKpmmhvb6XntM2sAWYu6gbyK/MluLDgluGLFiFRpWgobgzUqolg==",
"dev": true,
"dependencies": {
"@volar/language-core": "1.10.4"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
"integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.21.3",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-core/node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
"node_modules/@vue/compiler-dom": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
"integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
"dev": true,
"dependencies": {
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "2.7.14",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz",
@ -2898,6 +2954,70 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/@vue/language-core": {
"version": "1.8.19",
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.19.tgz",
"integrity": "sha512-nt3dodGs97UM6fnxeQBazO50yYCKBK53waFWB3qMbLmR6eL3aUryZgQtZoBe1pye17Wl8fs9HysV3si6xMgndQ==",
"dev": true,
"dependencies": {
"@volar/language-core": "~1.10.4",
"@volar/source-map": "~1.10.4",
"@vue/compiler-dom": "^3.3.0",
"@vue/reactivity": "^3.3.0",
"@vue/shared": "^3.3.0",
"minimatch": "^9.0.3",
"muggle-string": "^0.3.1",
"vue-template-compiler": "^2.7.14"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@vue/language-core/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@vue/reactivity": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
"integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
"dev": true,
"dependencies": {
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/shared": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
"dev": true
},
"node_modules/@vue/typescript": {
"version": "1.8.19",
"resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.19.tgz",
"integrity": "sha512-k/SHeeQROUgqsxyHQ8Cs3Zz5TnX57p7BcBDVYR2E0c61QL2DJ2G8CsaBremmNGuGE6o1R5D50IHIxFmroMz8iw==",
"dev": true,
"dependencies": {
"@volar/typescript": "~1.10.4",
"@vue/language-core": "1.8.19"
}
},
"node_modules/@vueuse/components": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@vueuse/components/-/components-10.1.2.tgz",
@ -4479,8 +4599,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/debounce": {
"version": "1.2.1",
@ -7798,6 +7917,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/muggle-string": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz",
"integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==",
"dev": true
},
"node_modules/multicast-dns": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
@ -11113,7 +11238,6 @@
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
"dev": true,
"peer": true,
"dependencies": {
"de-indent": "^1.0.2",
"he": "^1.2.0"
@ -11126,6 +11250,56 @@
"dev": true,
"peer": true
},
"node_modules/vue-tsc": {
"version": "1.8.19",
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.19.tgz",
"integrity": "sha512-tacMQLQ0CXAfbhRycCL5sWIy1qujXaIEtP1hIQpzHWOUuICbtTj9gJyFf91PvzG5KCNIkA5Eg7k2Fmgt28l5DQ==",
"dev": true,
"dependencies": {
"@vue/language-core": "1.8.19",
"@vue/typescript": "1.8.19",
"semver": "^7.5.4"
},
"bin": {
"vue-tsc": "bin/vue-tsc.js"
},
"peerDependencies": {
"typescript": "*"
}
},
"node_modules/vue-tsc/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/vue-tsc/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/vue-tsc/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/vue-virtual-scroller": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-1.1.2.tgz",
@ -13983,6 +14157,63 @@
"is-function": "^1.0.1"
}
},
"@volar/language-core": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.4.tgz",
"integrity": "sha512-Na69qA6uwVIdA0rHuOc2W3pHtVQQO8hCNim7FOaKNpRJh0oAFnu5r9i7Oopo5C4cnELZkPNjTrbmpcCTiW+CMQ==",
"dev": true,
"requires": {
"@volar/source-map": "1.10.4"
}
},
"@volar/source-map": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.4.tgz",
"integrity": "sha512-RxZdUEL+pV8p+SMqnhVjzy5zpb1QRZTlcwSk4bdcBO7yOu4rtEWqDGahVCEj4CcXour+0yJUMrMczfSCpP9Uxg==",
"dev": true,
"requires": {
"muggle-string": "^0.3.1"
}
},
"@volar/typescript": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.4.tgz",
"integrity": "sha512-BCCUEBASBEMCrz7qmNSi2hBEWYsXD0doaktRKpmmhvb6XntM2sAWYu6gbyK/MluLDgluGLFiFRpWgobgzUqolg==",
"dev": true,
"requires": {
"@volar/language-core": "1.10.4"
}
},
"@vue/compiler-core": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
"integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
"dev": true,
"requires": {
"@babel/parser": "^7.21.3",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
},
"dependencies": {
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
}
}
},
"@vue/compiler-dom": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
"integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
"dev": true,
"requires": {
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"@vue/compiler-sfc": {
"version": "2.7.14",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz",
@ -14039,6 +14270,58 @@
}
}
},
"@vue/language-core": {
"version": "1.8.19",
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.19.tgz",
"integrity": "sha512-nt3dodGs97UM6fnxeQBazO50yYCKBK53waFWB3qMbLmR6eL3aUryZgQtZoBe1pye17Wl8fs9HysV3si6xMgndQ==",
"dev": true,
"requires": {
"@volar/language-core": "~1.10.4",
"@volar/source-map": "~1.10.4",
"@vue/compiler-dom": "^3.3.0",
"@vue/reactivity": "^3.3.0",
"@vue/shared": "^3.3.0",
"minimatch": "^9.0.3",
"muggle-string": "^0.3.1",
"vue-template-compiler": "^2.7.14"
},
"dependencies": {
"minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
}
}
}
},
"@vue/reactivity": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
"integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
"dev": true,
"requires": {
"@vue/shared": "3.3.4"
}
},
"@vue/shared": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
"dev": true
},
"@vue/typescript": {
"version": "1.8.19",
"resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.19.tgz",
"integrity": "sha512-k/SHeeQROUgqsxyHQ8Cs3Zz5TnX57p7BcBDVYR2E0c61QL2DJ2G8CsaBremmNGuGE6o1R5D50IHIxFmroMz8iw==",
"dev": true,
"requires": {
"@volar/typescript": "~1.10.4",
"@vue/language-core": "1.8.19"
}
},
"@vueuse/components": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@vueuse/components/-/components-10.1.2.tgz",
@ -15255,8 +15538,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
"dev": true,
"peer": true
"dev": true
},
"debounce": {
"version": "1.2.1",
@ -17657,6 +17939,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"muggle-string": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz",
"integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==",
"dev": true
},
"multicast-dns": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
@ -20089,7 +20377,6 @@
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
"dev": true,
"peer": true,
"requires": {
"de-indent": "^1.0.2",
"he": "^1.2.0"
@ -20102,6 +20389,43 @@
"dev": true,
"peer": true
},
"vue-tsc": {
"version": "1.8.19",
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.19.tgz",
"integrity": "sha512-tacMQLQ0CXAfbhRycCL5sWIy1qujXaIEtP1hIQpzHWOUuICbtTj9gJyFf91PvzG5KCNIkA5Eg7k2Fmgt28l5DQ==",
"dev": true,
"requires": {
"@vue/language-core": "1.8.19",
"@vue/typescript": "1.8.19",
"semver": "^7.5.4"
},
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"vue-virtual-scroller": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-1.1.2.tgz",

View File

@ -73,6 +73,7 @@
"playwright": "^1.39.0",
"prettier": "^3.0.3",
"typescript": "^5.2.2",
"vue-tsc": "^1.8.19",
"workbox-webpack-plugin": "^7.0.0"
}
}

View File

@ -129,6 +129,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
recycler: VueRecyclerType;
};
},
click(item: ICluster) {
switch (item.cluster_type) {
case 'plus':
@ -140,7 +146,7 @@ export default defineComponent({
},
resize() {
this.recyclerWidth = (<any>this.$refs.recycler).$el.clientWidth;
this.recyclerWidth = this.refs().recycler?.$el.clientWidth;
},
},
});

View File

@ -82,14 +82,19 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
dtm?: InstanceType<typeof DynamicTopMatter>;
};
},
async routeChange() {
try {
this.items = [];
this.loading++;
await this.$nextTick();
// @ts-ignore
await this.$refs.dtm?.refresh?.();
await this.refs().dtm?.refresh?.();
if (this.routeIsAlbums) {
this.items = await dav.getAlbums(this.config.album_list_sort);

View File

@ -80,7 +80,7 @@ export default defineComponent({
},
/** Actual recycler component */
recycler: {
type: Object,
type: Object as PropType<VueRecyclerType>,
required: false,
},
/** Recycler before slot component */
@ -168,6 +168,14 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
scroller?: HTMLDivElement;
cursorSt?: HTMLSpanElement;
hoverCursor?: HTMLSpanElement;
};
},
/** Reset state */
reset() {
this.ticks = [];
@ -229,7 +237,7 @@ export default defineComponent({
// Move hover cursor to same position unless hovering
// Regardless, we need this call because the internal mapping might have changed
if (!utils.isMobile() && (<HTMLElement>this.$refs.scroller).matches(':hover')) {
if (!utils.isMobile() && this.refs().scroller?.matches(':hover')) {
this.moveHoverCursor(this.hoverCursorY);
} else {
this.moveHoverCursor(rtop);
@ -322,7 +330,7 @@ export default defineComponent({
this.dynTopMatterHeight = this.recyclerBefore?.clientHeight ?? 0;
// Exclude hover cursor height
const hoverCursor = <HTMLSpanElement>this.$refs.hoverCursor;
const hoverCursor = this.refs().hoverCursor;
this.topPadding = hoverCursor?.offsetHeight ?? 0;
// Add extra padding for any top elements (top matter, mobile header)
@ -378,12 +386,12 @@ export default defineComponent({
/** Mark ticks as visible or invisible */
computeVisibleTicks() {
// Kind of unrelated here, but refresh rect
this.scrollerRect = (this.$refs.scroller as HTMLElement).getBoundingClientRect();
this.scrollerRect = this.refs().scroller!.getBoundingClientRect();
// Do another pass to figure out which points are visible
// This is not as bad as it looks, it's actually 12*O(n)
// because there are only 12 months in a year
const fontSizePx = parseFloat(getComputedStyle(this.$refs.cursorSt as any).fontSize);
const fontSizePx = parseFloat(getComputedStyle(this.refs().cursorSt!).fontSize);
const minGap = fontSizePx + (_m.window.innerWidth <= 768 ? 5 : 2);
let prevShow = -9999;
for (const [idx, tick] of this.ticks.entries()) {

View File

@ -227,13 +227,19 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
multiPathModal: InstanceType<typeof MultiPathSelectionModal>;
};
},
onClose() {
this.$emit('update:open', false);
},
// Paths settings
async chooseTimelinePath() {
(<any>this.$refs.multiPathModal).open(this.config.timeline_path.split(';'));
this.refs().multiPathModal.open(this.config.timeline_path.split(';'));
},
async saveTimelinePath(paths: string[]) {

View File

@ -91,6 +91,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
metadata?: InstanceType<typeof Metadata>;
};
},
async open(photo: IPhoto | number, filename?: string, useNative = false) {
if (!this.reducedOpen && this.native && (!photo || useNative)) {
// Open native sidebar
@ -102,8 +108,7 @@ export default defineComponent({
await this.$nextTick();
// Update metadata compoenent
const m = <any>this.$refs.metadata;
const info: IImageInfo = await m?.update(photo);
const info = await this.refs().metadata?.update(photo);
if (!info) return; // failure or state change
this.basename = info.basename;
this.handleOpen();

View File

@ -75,7 +75,7 @@ export default defineComponent({
mounted() {
// Set up hammerjs hooks
this.hammer = new Hammer(this.$refs.timelineHeader as HTMLElement);
this.hammer = new Hammer(this.refs().timelineHeader!);
this.hammer.get('swipe').set({
direction: Hammer.DIRECTION_VERTICAL,
threshold: 3,
@ -90,6 +90,15 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
container?: HTMLDivElement;
primary?: HTMLDivElement;
separator?: HTMLDivElement;
timelineHeader?: HTMLDivElement;
};
},
isVertical() {
return false; // for future
},
@ -98,13 +107,11 @@ export default defineComponent({
this.pointerDown = true;
// Get position of primary element
const primary = <HTMLDivElement>this.$refs.primary;
const rect = primary.getBoundingClientRect();
const rect = this.refs().primary!.getBoundingClientRect();
this.primaryPos = this.isVertical() ? rect.top : rect.left;
// Get size of container element
const container = <HTMLDivElement>this.$refs.container;
const cRect = container.getBoundingClientRect();
const cRect = this.refs().container!.getBoundingClientRect();
this.containerSize = this.isVertical() ? cRect.height : cRect.width;
// Let touch handle itself
@ -141,7 +148,7 @@ export default defineComponent({
const ref = this.isVertical() ? pos.clientY : pos.clientX;
const newSize = Math.max(ref - this.primaryPos, 50);
const pctSize = (newSize / this.containerSize) * 100;
(<HTMLDivElement>this.$refs.primary).style.flexBasis = `${pctSize}%`;
this.refs().primary!.style.flexBasis = `${pctSize}%`;
},
daysLoaded({ count }: { count: number }) {

View File

@ -32,7 +32,7 @@
<div class="mobile-header-top-gap"></div>
<!-- Route-specific top matter -->
<DynamicTopMatter ref="dtm" @load="scrollerManager().adjust()" />
<DynamicTopMatter ref="dtm" @load="refs().scrollerManager.adjust()" />
</div>
</template>
@ -41,7 +41,7 @@
v-if="item.type === 0"
:item="item"
:monthView="isMonthView"
@click="selectionManager().selectHead(item)"
@click="refs().selectionManager.selectHead(item)"
/>
<template v-else>
@ -56,11 +56,11 @@
}"
:data="photo"
:day="item.day"
@select="selectionManager().clickSelectionIcon(photo, $event, index)"
@pointerdown="selectionManager().clickPhoto(photo, $event, index)"
@touchstart="selectionManager().touchstartPhoto(photo, $event, index)"
@touchend="selectionManager().touchendPhoto(photo, $event, index)"
@touchmove="selectionManager().touchmovePhoto(photo, $event, index)"
@select="refs().selectionManager.clickSelectionIcon(photo, $event, index)"
@pointerdown="refs().selectionManager.clickPhoto(photo, $event, index)"
@touchstart="refs().selectionManager.touchstartPhoto(photo, $event, index)"
@touchend="refs().selectionManager.touchendPhoto(photo, $event, index)"
@touchmove="refs().selectionManager.touchmovePhoto(photo, $event, index)"
/>
</template>
</template>
@ -72,8 +72,8 @@
v-show="!showEmpty"
:rows="list"
:fullHeight="scrollerHeight"
:recycler="$refs.recycler"
:recyclerBefore="$refs.recyclerBefore"
:recycler="refs().recycler"
:recyclerBefore="refs().recyclerBefore"
@interactend="loadScrollView()"
/>
@ -82,7 +82,7 @@
:heads="heads"
:rows="list"
:isreverse="isMonthView"
:recycler="$refs.recycler?.$el"
:recycler="refs().recycler?.$el"
@updateLoading="updateLoading"
/>
</div>
@ -96,7 +96,6 @@ import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs';
import { getLayout } from '../services/layout';
import { IDay, IHeadRow, IPhoto, IRow, IRowType } from '../types';
import UserConfig from '../mixins/UserConfig';
import RowHead from './frame/RowHead.vue';
@ -115,6 +114,8 @@ import * as nativex from '../native';
import { API, DaysFilterType } from '../services/API';
import { IDay, IHeadRow, IPhoto, IRow, IRowType } from '../types';
const SCROLL_LOAD_DELAY = 100; // Delay in loading data when scrolling
const DESKTOP_ROW_HEIGHT = 200; // Height of row on desktop
const MOBILE_ROW_HEIGHT = 120; // Approx row height on mobile
@ -241,6 +242,18 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
container?: HTMLDivElement;
topmatter?: InstanceType<typeof TopMatter>;
dtm?: InstanceType<typeof DynamicTopMatter>;
recycler?: VueRecyclerType;
recyclerBefore?: HTMLDivElement;
selectionManager: InstanceType<typeof SelectionManager>;
scrollerManager: InstanceType<typeof ScrollerManager>;
};
},
async routeChange(to: Route, from?: Route) {
// Always do a hard refresh if the path changes
if (from?.path !== to.path) {
@ -282,7 +295,7 @@ export default defineComponent({
if (!from) {
const index = this.list.findIndex((r) => r.day.dayid === dayid && r.photos?.includes(photo));
if (index !== -1) {
(this.$refs.recycler as any).scrollToItem(index);
this.refs().recycler?.scrollToItem(index);
}
}
@ -309,14 +322,6 @@ export default defineComponent({
return _m.window.innerWidth <= 600 && !this.config.square_thumbs;
},
selectionManager() {
return this.$refs.selectionManager as InstanceType<typeof SelectionManager>;
},
scrollerManager() {
return this.$refs.scrollerManager as InstanceType<typeof ScrollerManager>;
},
/** Create new state */
async createState() {
// Wait for one tick before doing anything
@ -326,7 +331,7 @@ export default defineComponent({
this.recomputeSizes();
// Timeline recycler init
(this.$refs.recycler as any).$el.addEventListener('scroll', this.scrollPositionChange, { passive: true });
this.refs().recycler?.$el.addEventListener('scroll', this.scrollPositionChange, { passive: true });
// Get data
await this.fetchDays();
@ -334,8 +339,8 @@ export default defineComponent({
/** Reset all state */
async resetState() {
this.selectionManager().clearSelection();
this.scrollerManager().reset();
this.refs().selectionManager.clearSelection();
this.refs().scrollerManager.reset();
this.loading = 0;
this.list = [];
this.dtmContent = false;
@ -370,7 +375,7 @@ export default defineComponent({
* Do not pass this function as a callback directly.
*/
async softRefreshInternal(sync: boolean) {
this.selectionManager().clearSelection();
this.refs().selectionManager.clearSelection();
this.fetchDayQueue = []; // reset queue
// Fetch days
@ -389,7 +394,7 @@ export default defineComponent({
/** Recompute static sizes of containers */
recomputeSizes() {
// Size of outer container
const e = this.$refs.container as Element;
const e = this.refs().container!;
const height = e.clientHeight;
const width = e.clientWidth;
this.containerSize = [width, height];
@ -398,11 +403,11 @@ export default defineComponent({
this.scrollerHeight = height;
// Static top matter to exclude from recycler height
const topmatter = this.$refs.topmatter as any;
const tmHeight = topmatter.$el?.clientHeight || 0;
const topmatter = this.refs().topmatter;
const tmHeight = topmatter?.$el?.clientHeight || 0;
// Recycler height
const recycler = this.$refs.recycler as any;
const recycler = this.refs().recycler!;
const targetHeight = height - tmHeight - 4;
const targetWidth = this.isMobile() ? width : width - 40;
const heightChanged = recycler.$el.clientHeight !== targetHeight;
@ -444,7 +449,7 @@ export default defineComponent({
// At this point we're sure the size has changed, so we need
// to invalidate everything related to sizes
this.sizedDays.clear();
this.scrollerManager().adjust();
this.refs().scrollerManager.adjust();
// Explicitly request a scroll event
this.loadScrollView();
@ -457,7 +462,7 @@ export default defineComponent({
* the pixel position of the recycler has changed.
*/
scrollPositionChange(event?: Event) {
this.scrollerManager().recyclerScrolled(event ?? null);
this.refs().scrollerManager.recyclerScrolled(event ?? null);
},
/** Trigger when recycler view changes (for callback) */
@ -503,7 +508,7 @@ export default defineComponent({
}
// We only need to debounce loads if the user is dragging the scrollbar
const scrolling = this.scrollerManager().interacting;
const scrolling = this.refs().scrollerManager.interacting;
// Make sure we don't do this too often
this.currentStart = startIndex;
@ -658,10 +663,9 @@ export default defineComponent({
try {
this.updateLoading(1);
const state = this.state;
// @ts-ignore
const res = await this.$refs.dtm.refresh();
const res = await this.refs().dtm?.refresh();
if (this.state !== state) return;
this.dtmContent = res;
this.dtmContent = res ?? false;
} finally {
this.updateLoading(-1);
}
@ -849,7 +853,7 @@ export default defineComponent({
});
// Fix view height variable
await this.scrollerManager().reflow();
await this.refs().scrollerManager.reflow();
this.scrollPositionChange();
// Trigger a view refresh. This will load any new placeholders too.
@ -1086,7 +1090,7 @@ export default defineComponent({
let addedRows: IRow[] = [];
// Recycler scroll top
let scrollTop = (<any>this.$refs.recycler).$el.scrollTop;
let scrollTop = this.refs().recycler!.$el.scrollTop;
let needAdjust = false;
// Get index and Y position of header in O(n)
@ -1205,7 +1209,7 @@ export default defineComponent({
}
// Restore selection day
this.selectionManager().restoreDay(day);
this.refs().selectionManager.restoreDay(day);
// Rows that were removed
const removedRows: IRow[] = [];
@ -1247,14 +1251,14 @@ export default defineComponent({
if (headRemoved) {
// If the head was removed, we need a reflow,
// or adjust isn't going to work right
this.scrollerManager().reflow();
this.refs().scrollerManager.reflow();
} else {
// Otherwise just adjust the ticks
this.scrollerManager().adjust();
this.refs().scrollerManager.adjust();
}
// Scroll to new position
(<any>this.$refs.recycler).$el.scrollTop = scrollTop;
this.refs().recycler!.$el.scrollTop = scrollTop;
}
},
@ -1307,7 +1311,7 @@ export default defineComponent({
await new Promise((resolve) => setTimeout(resolve, 200));
// clear selection at this point
this.selectionManager().clearSelection(delPhotos);
this.refs().selectionManager.clearSelection(delPhotos);
// Reflow all touched days
for (const day of updatedDays) {

View File

@ -108,7 +108,7 @@ export default defineComponent({
},
methods: {
async placesSetup(event: Event) {
async placesSetup(event: React.FormEvent<HTMLFormElement>) {
// prevent the submit event
event.preventDefault();
event.stopPropagation();

View File

@ -75,6 +75,8 @@ import VideoIcon from 'vue-material-design-icons/PlayCircleOutline.vue';
import LivePhotoIcon from 'vue-material-design-icons/MotionPlayOutline.vue';
import LocalIcon from 'vue-material-design-icons/CloudOff.vue';
import type XImg from './XImg.vue';
export default defineComponent({
name: 'Photo',
components: {
@ -122,7 +124,7 @@ export default defineComponent({
this.faceSrc = null;
// Setup video hooks
const video = this.$refs.video as HTMLVideoElement;
const video = this.refs().video;
if (video) {
utils.setupLivePhotoHooks(video);
}
@ -169,6 +171,13 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
ximg?: InstanceType<typeof XImg> & { $el: HTMLImageElement };
video?: HTMLVideoElement;
};
},
/** Get url of the photo */
url() {
let base: 256 | 512 = 256;
@ -193,7 +202,8 @@ export default defineComponent({
async addFaceRect() {
if (!this.data.facerect || this.faceSrc) return;
const img = (this.$refs.ximg as any).$el as HTMLImageElement;
const img = this.refs().ximg?.$el;
if (!img) return;
// This is a hack to check if img is actually loaded.
// XImg loads an empty image, which may sometimes show up here
@ -245,8 +255,8 @@ export default defineComponent({
/** Start preview video */
playVideo() {
if (this.$refs.video && !(this.data.flag & this.c.FLAG_SELECTED)) {
const video = this.$refs.video as HTMLVideoElement;
const video = this.refs().video;
if (video && !(this.data.flag & this.c.FLAG_SELECTED)) {
video.currentTime = 0;
video.play();
}
@ -254,10 +264,7 @@ export default defineComponent({
/** Stop preview video */
stopVideo() {
if (this.$refs.video) {
const video = this.$refs.video as HTMLVideoElement;
video.pause();
}
this.refs().video?.pause();
},
},
});

View File

@ -243,12 +243,18 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
popover?: VueNcPopover;
};
},
/**
* Fetch possible collaborators.
*/
async searchCollaborators() {
if (this.searchText.length >= 1) {
(<any>this.$refs.popover).$refs.popover.show();
this.refs().popover?.$refs.popover.show();
}
try {
@ -399,7 +405,7 @@ export default defineComponent({
return;
}
(<any>this.$refs.popover).$refs.popover.hide();
this.refs().popover?.$refs.popover.hide();
this.selectedCollaboratorsKeys.push(collaboratorKey);
},

View File

@ -1,5 +1,5 @@
<template>
<form v-if="!showCollaboratorView" class="album-form" @submit.prevent="submit">
<form v-if="!showCollaboratorView" class="album-form" @submit.prevent>
<div class="form-inputs">
<NcTextField
ref="nameInput"
@ -53,6 +53,7 @@
</span>
</div>
</form>
<AlbumCollaborators
v-else
:album-name="albumName"
@ -163,11 +164,17 @@ export default defineComponent({
this.albumLocation = this.album.location;
}
this.$nextTick(() => {
(<any>this.$refs.nameInput)?.$el.getElementsByTagName('input')[0].focus();
this.refs().nameInput?.$el.getElementsByTagName('input')[0].focus();
});
},
methods: {
refs() {
return this.$refs as {
nameInput?: VueHTMLComponent;
};
},
submit(collaborators: any[] = []) {
if (this.albumName === '' || this.loading) {
return;

View File

@ -152,6 +152,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
albumsList?: VueHTMLComponent;
};
},
async albumCreatedHandler({ album }: { album: { basename: string } }) {
this.showAlbumCreationForm = false;
await this.loadAlbums(true);
@ -227,7 +233,7 @@ export default defineComponent({
forceUpdate() {
this.$forceUpdate(); // sets do not trigger reactivity
(<any>this.$refs.albumsList)?.$forceUpdate();
this.refs().albumsList?.$forceUpdate();
},
},
});

View File

@ -99,6 +99,15 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
editDate?: InstanceType<typeof EditDate>;
editTags?: InstanceType<typeof EditTags>;
editExif?: InstanceType<typeof EditExif>;
editLocation?: InstanceType<typeof EditLocation>;
};
},
async open(photos: IPhoto[], sections: number[] = [1, 2, 3, 4]) {
const state = (this.state = Math.random());
this.show = true;
@ -150,7 +159,7 @@ export default defineComponent({
async save() {
// Perform validation
try {
(<any>this.$refs.editDate)?.validate?.();
this.refs().editDate?.validate?.();
} catch (e) {
console.error(e);
showError(e);
@ -164,14 +173,14 @@ export default defineComponent({
// Get exif fields diff
const exifResult = {
...((<any>this.$refs.editExif)?.result?.() || {}),
...((<any>this.$refs.editLocation)?.result?.() || {}),
...(this.refs().editExif?.result?.() || {}),
...(this.refs().editLocation?.result?.() || {}),
};
// Tags may be created which might throw
let tagsResult: number[] | null = null;
let tagsResult: { add: number[]; remove: number[] } | null = null;
try {
tagsResult = (await (<any>this.$refs.editTags)?.result?.()) || null;
tagsResult = (await this.refs().editTags?.result?.()) ?? null;
} catch (e) {
this.processing = false;
console.error(e);
@ -186,7 +195,7 @@ export default defineComponent({
const raw: IExif = JSON.parse(JSON.stringify(exifResult));
// Date header
const date = (<any>this.$refs.editDate)?.result?.(p);
const date = this.refs().editDate?.result?.(p);
if (date) {
raw.DateTimeOriginal = date;
raw.CreateDate = date;

View File

@ -50,6 +50,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
selectTags: VueNcSelectTags;
};
},
init() {
let tagIds: number[] | null = null;
@ -93,7 +99,7 @@ export default defineComponent({
getAvailable(): dav.ITag[] {
// FIXME: this is extremely fragile
return (<any>this.$refs.selectTags).availableTags;
return this.refs().selectTags.availableTags;
},
handleCreate(newTag: dav.ITag) {

View File

@ -41,7 +41,7 @@
<NcActions :inline="isMobile ? 1 : 3">
<NcActionButton
:aria-label="t('memories', 'Create new album')"
@click="$refs.createModal?.open(false)"
@click="refs().createModal.open(false)"
close-after-click
v-if="isAlbumList"
>
@ -50,7 +50,7 @@
</NcActionButton>
<NcActionButton
:aria-label="t('memories', 'Share album')"
@click="$refs.shareModal?.open(false)"
@click="refs().shareModal.open()"
close-after-click
v-if="canEditAlbum"
>
@ -68,7 +68,7 @@
</NcActionButton>
<NcActionButton
:aria-label="t('memories', 'Edit album details')"
@click="$refs.createModal?.open(true)"
@click="refs().createModal.open(true)"
close-after-click
v-if="canEditAlbum"
>
@ -77,7 +77,7 @@
</NcActionButton>
<NcActionButton
:aria-label="t('memories', 'Remove album')"
@click="$refs.deleteModal?.open()"
@click="refs().deleteModal.open()"
close-after-click
v-if="!isAlbumList"
>
@ -167,6 +167,14 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
createModal: InstanceType<typeof AlbumCreateModal>;
deleteModal: InstanceType<typeof AlbumDeleteModal>;
shareModal: InstanceType<typeof AlbumShareModal>;
};
},
back() {
this.$router.go(-1);
},

View File

@ -62,11 +62,16 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
child?: { refresh?(): Promise<boolean> };
};
},
async refresh(): Promise<boolean> {
if (this.currentmatter) {
await this.$nextTick();
// @ts-ignore
return (await this.$refs.child?.refresh?.()) ?? false;
return (await this.refs().child?.refresh?.()) ?? false;
}
return false;

View File

@ -27,7 +27,7 @@
</NcActionButton>
<NcActionButton
:aria-label="t('memories', 'Merge with different person')"
@click="$refs.mergeModal?.open()"
@click="refs().mergeModal.open()"
close-after-click
>
{{ t('memories', 'Merge with different person') }}
@ -42,7 +42,7 @@
</NcActionCheckbox>
<NcActionButton
:aria-label="t('memories', 'Remove person')"
@click="$refs.deleteModal?.open()"
@click="refs().deleteModal.open()"
close-after-click
>
{{ t('memories', 'Remove person') }}
@ -117,12 +117,20 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
editModal: InstanceType<typeof FaceEditModal>;
deleteModal: InstanceType<typeof FaceDeleteModal>;
mergeModal: InstanceType<typeof FaceMergeModal>;
};
},
back() {
this.$router.go(-1);
},
rename() {
if (this.isReal) (<any>this.$refs.editModal)?.open();
if (this.isReal) this.refs().editModal.open();
},
openUnassigned() {

View File

@ -92,10 +92,8 @@ export default defineComponent({
}),
mounted() {
const map = this.$refs.map as LMap;
// Make sure the zoom control doesn't overlap with the navbar
map.mapObject.zoomControl.setPosition('topright');
this.refs().map.mapObject.zoomControl.setPosition('topright');
// Initialize
this.initialize();
@ -127,6 +125,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
map: LMap;
};
},
/**
* Get initial coordinates for display and set them.
* Then fetch clusters.
@ -150,7 +154,7 @@ export default defineComponent({
}>(API.MAP_INIT());
// Init data contains position information
const map = this.$refs.map as LMap;
const map = this.refs().map;
const pos = init?.data?.pos;
if (!pos?.lat || !pos?.lon) {
throw new Error('No position data');
@ -170,7 +174,7 @@ export default defineComponent({
},
async refresh() {
const map = this.$refs.map as LMap;
const map = this.refs().map;
if (!map || !map.mapObject) return;
// Get boundaries of the map
@ -271,7 +275,7 @@ export default defineComponent({
},
setBoundsFromQuery() {
const map = this.$refs.map as LMap;
const map = this.refs().map;
const { minLat, maxLat, minLon, maxLon } = this.boundsFromQuery();
map.mapObject.fitBounds([
[minLat, minLon],
@ -299,7 +303,7 @@ export default defineComponent({
}
// Zoom in
const map = this.$refs.map as LMap;
const map = this.refs().map;
const factor = globalThis.innerWidth >= 768 ? 2 : 1;
const zoom = map.mapObject.getZoom() + factor;
map.mapObject.setView(cluster.center, zoom, { animate: true });
@ -399,7 +403,7 @@ export default defineComponent({
},
handleContainerResize() {
(<any>this.$refs.map)?.mapObject?.invalidateSize(true);
this.refs().map?.mapObject?.invalidateSize(true);
},
},
});

View File

@ -72,7 +72,8 @@ export default defineComponent({
}),
mounted() {
const inner = this.$refs.inner as HTMLElement;
const inner = this.refs().inner!;
inner.addEventListener('scroll', this.onScroll.bind(this), {
passive: true,
});
@ -88,6 +89,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
inner?: HTMLDivElement;
};
},
onload() {
this.$emit('load');
},
@ -167,12 +174,12 @@ export default defineComponent({
},
moveLeft() {
const inner = this.$refs.inner as HTMLElement;
const inner = this.refs().inner!;
inner.scrollBy(-(this.scrollStack.pop() || inner.clientWidth), 0);
},
moveRight() {
const inner = this.$refs.inner as HTMLElement;
const inner = this.refs().inner!;
const innerRect = inner.getBoundingClientRect();
const nextChild = Array.from(inner.children)
.map((c) => c.getBoundingClientRect())
@ -185,7 +192,7 @@ export default defineComponent({
},
onScroll() {
const inner = this.$refs.inner as HTMLElement;
const inner = this.refs().inner;
if (!inner) return;
this.hasLeft = inner.scrollLeft > 0;
this.hasRight = inner.clientWidth + inner.scrollLeft < inner.scrollWidth - 20;

View File

@ -173,7 +173,8 @@ export default defineComponent({
async mounted() {
await loadFilerobot();
const div = <HTMLElement>this.$refs.editor;
const div = this.refs().editor!;
console.assert(div, 'ImageEditor container not found');
// Directly use an HTML element to make sure the resolution
// in the editor matches the original file, but we can work
@ -196,6 +197,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
editor?: HTMLDivElement;
};
},
async getImage(): Promise<HTMLImageElement> {
const img = new Image();
img.name = this.defaultSavedImageName;

View File

@ -388,6 +388,12 @@ export default defineComponent({
},
methods: {
refs() {
return this.$refs as {
inner: HTMLDivElement;
};
},
fetchDay(dayId: number) {
utils.bus.emit('memories:timeline:fetch-day', dayId);
},
@ -456,7 +462,7 @@ export default defineComponent({
loop: false,
wheelToZoom: true,
bgOpacity: 1,
appendToEl: this.$refs.inner as HTMLElement,
appendToEl: this.refs().inner!,
preload: [2, 2],
bgClickAction: 'toggle-controls',

25
src/globals.d.ts vendored
View File

@ -1,5 +1,8 @@
import type { Route } from 'vue-router';
import type { ComponentPublicInstance } from 'vue';
import type { translate, translatePlural } from '@nextcloud/l10n';
import type PlyrType from 'plyr';
import type videojsType from 'video.js';
@ -67,6 +70,28 @@ declare global {
innerHeight: number; // cache
};
};
// Typings for external libraries below
type VueRecyclerType = ComponentPublicInstance & {
$el: HTMLDivElement;
$refs: {
wrapper: HTMLDivElement;
};
scrollToPosition: (position: number) => void;
scrollToItem: (index: number) => void;
};
type VueNcPopover = ComponentPublicInstance & {
$refs: { popover: { show(): void; hide(): void } };
};
type VueNcSelectTags = ComponentPublicInstance & {
availableTags: any[];
};
type VueHTMLComponent = ComponentPublicInstance & {
$el: HTMLElement;
};
}
// GlobalMixin.ts types, present on all components