Merge branch 'develop' into chat

This commit is contained in:
syuilo
2025-03-19 19:37:31 +09:00
196 changed files with 648 additions and 978 deletions

View File

@@ -8,6 +8,7 @@
- Feat: 設定の管理が強化されました - Feat: 設定の管理が強化されました
- 自動でバックアップされるように - 自動でバックアップされるように
- 任意の設定項目をデバイス間で同期できるように - 任意の設定項目をデバイス間で同期できるように
- Feat: 画面を重ねて表示するオプションを実装(実験的)
- Enhance: プラグインの管理が強化されました - Enhance: プラグインの管理が強化されました
- インストール/アンインストール/設定の変更時にリロード不要になりました - インストール/アンインストール/設定の変更時にリロード不要になりました
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように - Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
@@ -17,6 +18,7 @@
- Enhance: 投稿フォームの設定メニューを改良 - Enhance: 投稿フォームの設定メニューを改良
- 投稿フォームをリセットできるように - 投稿フォームをリセットできるように
- 文字数カウントを復活 - 文字数カウントを復活
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正 - Fix: テーマ切り替え時に一部の色が変わらない問題を修正
### Server ### Server

View File

@@ -273,7 +273,6 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
query?: Record<string, string>; query?: Record<string, string>;
loginRequired?: boolean; loginRequired?: boolean;
hash?: string; hash?: string;
globalCacheKey?: string;
children?: RouteDef[]; children?: RouteDef[];
} }
``` ```

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} està parlant sobre \"{word}\""
makeActive: "Activar" makeActive: "Activar"
display: "Veure" display: "Veure"
copy: "Copiar" copy: "Copiar"
copiedToClipboard: "Copiat al porta papers"
metrics: "Mètriques" metrics: "Mètriques"
overview: "Visió General" overview: "Visió General"
logs: "Registres" logs: "Registres"
@@ -1139,7 +1140,7 @@ channelArchiveConfirmDescription: "Un Canal arxivat no apareixerà a la llista d
thisChannelArchived: "Aquest Canal ha sigut arxivat." thisChannelArchived: "Aquest Canal ha sigut arxivat."
displayOfNote: "Mostrar notes" displayOfNote: "Mostrar notes"
initialAccountSetting: "Configuració del perfil" initialAccountSetting: "Configuració del perfil"
youFollowing: "Seguint" youFollowing: "Segueixes "
preventAiLearning: "Descartar l'ús d'aprenentatge automàtic (IA Generativa)" preventAiLearning: "Descartar l'ús d'aprenentatge automàtic (IA Generativa)"
preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automàtic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta." preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automàtic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta."
options: "Opcions" options: "Opcions"
@@ -1332,6 +1333,7 @@ preferenceSyncConflictChoiceCancel: "Cancel·lar l'activació de la sincronitzac
paste: "Pegar" paste: "Pegar"
emojiPalette: "Calaix d'emojis" emojiPalette: "Calaix d'emojis"
postForm: "Formulari de publicació" postForm: "Formulari de publicació"
textCount: "Nombre de caràcters "
_emojiPalette: _emojiPalette:
palettes: "Calaixos d'emojis" palettes: "Calaixos d'emojis"
enableSyncBetweenDevicesForPalettes: "Activa la sincronització dels calaixos d'emojis entre dispositius" enableSyncBetweenDevicesForPalettes: "Activa la sincronització dels calaixos d'emojis entre dispositius"
@@ -1355,6 +1357,8 @@ _settings:
appearanceBanner: "Pots configurar les preferències relacionades amb la visualització i l'aspecte del client segons el teu parer." appearanceBanner: "Pots configurar les preferències relacionades amb la visualització i l'aspecte del client segons el teu parer."
soundsBanner: "Configuració dels sons que reproduirà el client." soundsBanner: "Configuració dels sons que reproduirà el client."
timelineAndNote: "Línia de temps i nota" timelineAndNote: "Línia de temps i nota"
makeEveryTextElementsSelectable: "Fes que tots els elements del text siguin seleccionables"
makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions."
_preferencesProfile: _preferencesProfile:
profileName: "Nom del perfil" profileName: "Nom del perfil"
profileNameDescription: "Estableix un nom que identifiqui aquest dispositiu." profileNameDescription: "Estableix un nom que identifiqui aquest dispositiu."
@@ -2519,6 +2523,7 @@ _notification:
achievementEarned: "Assoliment desbloquejat" achievementEarned: "Assoliment desbloquejat"
exportCompleted: "Exportació completada" exportCompleted: "Exportació completada"
login: "Iniciar sessió" login: "Iniciar sessió"
createToken: "Creació de tokens d'accés "
test: "Prova la notificació" test: "Prova la notificació"
app: "Notificacions d'aplicacions" app: "Notificacions d'aplicacions"
_actions: _actions:

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} said something about \"{word}\""
makeActive: "Activate" makeActive: "Activate"
display: "Display" display: "Display"
copy: "Copy" copy: "Copy"
copiedToClipboard: "Copied to clipboard"
metrics: "Metrics" metrics: "Metrics"
overview: "Overview" overview: "Overview"
logs: "Logs" logs: "Logs"
@@ -1323,7 +1324,21 @@ untitled: "Untitled"
noName: "No name" noName: "No name"
skip: "Skip" skip: "Skip"
restore: "Restore" restore: "Restore"
syncBetweenDevices: "Sync between devices"
preferenceSyncConflictTitle: "The configured value exists on the server."
preferenceSyncConflictText: "The sync enabled settings will save their values to the server. However, there are existing values on the server. Which set of values would you like to overwrite?"
preferenceSyncConflictChoiceServer: "Configured value on server"
preferenceSyncConflictChoiceDevice: "Configured value on device"
preferenceSyncConflictChoiceCancel: "Cancel enabling sync"
paste: "Paste"
emojiPalette: "Emoji palette"
postForm: "Posting form" postForm: "Posting form"
textCount: "Character count"
_emojiPalette:
palettes: "Palette"
enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices"
paletteForMain: "Main palette"
paletteForReaction: "Reaction palette"
_settings: _settings:
driveBanner: "You can manage and configure the drive, check usage, and configure file upload settings." driveBanner: "You can manage and configure the drive, check usage, and configure file upload settings."
pluginBanner: "You can extend client features with plugins. You can install plugins, configure and manage individually." pluginBanner: "You can extend client features with plugins. You can install plugins, configure and manage individually."
@@ -1341,6 +1356,9 @@ _settings:
preferencesBanner: "You can configure the overall behavior of the client according to your preferences." preferencesBanner: "You can configure the overall behavior of the client according to your preferences."
appearanceBanner: "You can configure the appearance and display settings for the client according to your preferences." appearanceBanner: "You can configure the appearance and display settings for the client according to your preferences."
soundsBanner: "You can configure the sound settings for playback in the client." soundsBanner: "You can configure the sound settings for playback in the client."
timelineAndNote: "Timeline and note"
makeEveryTextElementsSelectable: "Make all text elements selectable"
makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations."
_preferencesProfile: _preferencesProfile:
profileName: "Profile name" profileName: "Profile name"
profileNameDescription: "Set a name that identifies this device." profileNameDescription: "Set a name that identifies this device."
@@ -2505,6 +2523,7 @@ _notification:
achievementEarned: "Achievement unlocked" achievementEarned: "Achievement unlocked"
exportCompleted: "The export has been completed" exportCompleted: "The export has been completed"
login: "Sign In" login: "Sign In"
createToken: "Create access token"
test: "Notification test" test: "Notification test"
app: "Notifications from linked apps" app: "Notifications from linked apps"
_actions: _actions:
@@ -2532,6 +2551,7 @@ _deck:
useSimpleUiForNonRootPages: "Use simple UI for navigated pages" useSimpleUiForNonRootPages: "Use simple UI for navigated pages"
usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled" usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled"
flexible: "Auto-adjust width" flexible: "Auto-adjust width"
enableSyncBetweenDevicesForProfiles: "Enable profile information sync between devices"
_columns: _columns:
main: "Main" main: "Main"
widgets: "Widgets" widgets: "Widgets"

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} ha Notato a riguardo di \"{word}\""
makeActive: "Attiva" makeActive: "Attiva"
display: "Visualizza" display: "Visualizza"
copy: "Copia" copy: "Copia"
copiedToClipboard: "Copiato negli appunti"
metrics: "Statistiche" metrics: "Statistiche"
overview: "Anteprima" overview: "Anteprima"
logs: "Log" logs: "Log"
@@ -973,7 +974,7 @@ check: "Verifica"
driveCapOverrideLabel: "Modificare la capienza del Drive per questo profilo" driveCapOverrideLabel: "Modificare la capienza del Drive per questo profilo"
driveCapOverrideCaption: "Se viene specificato meno di 0, viene annullato." driveCapOverrideCaption: "Se viene specificato meno di 0, viene annullato."
requireAdminForView: "Per visualizzarli, è necessario aver effettuato l'accesso con un profilo amministratore." requireAdminForView: "Per visualizzarli, è necessario aver effettuato l'accesso con un profilo amministratore."
isSystemAccount: "Questi profili vengono creati e gestiti automaticamente dal sistema" isSystemAccount: "Si tratta di un profilo creato e gestito automaticamente dal sistema."
typeToConfirm: "Digita {x} per continuare" typeToConfirm: "Digita {x} per continuare"
deleteAccount: "Eliminazione profilo" deleteAccount: "Eliminazione profilo"
document: "Documentazione" document: "Documentazione"
@@ -1323,7 +1324,21 @@ untitled: "Senza titolo"
noName: "Senza nome" noName: "Senza nome"
skip: "Salta" skip: "Salta"
restore: "Ripristina" restore: "Ripristina"
syncBetweenDevices: "Sincronizzazione tra i dispositivi"
preferenceSyncConflictTitle: "Sul server esiste già il valore impostato"
preferenceSyncConflictText: "Le impostazione sincronizzata salverà il valore sul server. Però, bada che esiste già un valore sul server. Quale vorresti sovrascrivere?"
preferenceSyncConflictChoiceServer: "Valore del server"
preferenceSyncConflictChoiceDevice: "Valore del dispositivo"
preferenceSyncConflictChoiceCancel: "Annulla la sincronizzazione"
paste: "Incolla"
emojiPalette: "Tavolozza emoji"
postForm: "Finestra di pubblicazione" postForm: "Finestra di pubblicazione"
textCount: "Il numero di caratteri"
_emojiPalette:
palettes: "Tavolozza"
enableSyncBetweenDevicesForPalettes: "Attiva la sincronizzazione tra dispositivi"
paletteForMain: "Tavolozza principale"
paletteForReaction: "Tavolozza per reazioni"
_settings: _settings:
driveBanner: "Permette di gestire e configurare il Drive, controllare il consumo di spazio e configurare il caricamento dei file." driveBanner: "Permette di gestire e configurare il Drive, controllare il consumo di spazio e configurare il caricamento dei file."
pluginBanner: "Consentono di migliorare le funzionalità. Le estensioni si possono configurare e gestire singolarmente." pluginBanner: "Consentono di migliorare le funzionalità. Le estensioni si possono configurare e gestire singolarmente."
@@ -1341,6 +1356,9 @@ _settings:
preferencesBanner: "Puoi personalizzare il comportamento del tuo dispositivo." preferencesBanner: "Puoi personalizzare il comportamento del tuo dispositivo."
appearanceBanner: "Puoi personalizzare l'aspetto nel dispositivo, in base alle tue preferenze." appearanceBanner: "Puoi personalizzare l'aspetto nel dispositivo, in base alle tue preferenze."
soundsBanner: "Puoi personalizzare i suoni emessi dagli eventi sul tuo dispositivo." soundsBanner: "Puoi personalizzare i suoni emessi dagli eventi sul tuo dispositivo."
timelineAndNote: "Note e Timeline"
makeEveryTextElementsSelectable: "Imposta ogni elemento come selezionabile"
makeEveryTextElementsSelectable_description: "Potrebbe ridurre l'usabilità in alcune situazioni."
_preferencesProfile: _preferencesProfile:
profileName: "Nome del profilo" profileName: "Nome del profilo"
profileNameDescription: "Impostare il nome che indentifica questo dispositivo." profileNameDescription: "Impostare il nome che indentifica questo dispositivo."
@@ -2505,6 +2523,7 @@ _notification:
achievementEarned: "Risultato raggiunto" achievementEarned: "Risultato raggiunto"
exportCompleted: "Esportazione completata" exportCompleted: "Esportazione completata"
login: "Accessi" login: "Accessi"
createToken: "Creare un token di accesso"
test: "Notifiche di test" test: "Notifiche di test"
app: "Notifiche da applicazioni" app: "Notifiche da applicazioni"
_actions: _actions:
@@ -2532,6 +2551,7 @@ _deck:
useSimpleUiForNonRootPages: "Visualizza sotto pagine con interfaccia web semplice" useSimpleUiForNonRootPages: "Visualizza sotto pagine con interfaccia web semplice"
usedAsMinWidthWhenFlexible: "Se \"larghezza flessibile\" è abilitato, questa diventa la larghezza minima" usedAsMinWidthWhenFlexible: "Se \"larghezza flessibile\" è abilitato, questa diventa la larghezza minima"
flexible: "Larghezza flessibile" flexible: "Larghezza flessibile"
enableSyncBetweenDevicesForProfiles: "Abilita la sincronizzazione delle informazioni profilo tra dispositivi"
_columns: _columns:
main: "Principale" main: "Principale"
widgets: "Riquadri" widgets: "Riquadri"

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} 说了关于「{word}」的什么"
makeActive: "启用" makeActive: "启用"
display: "显示" display: "显示"
copy: "复制" copy: "复制"
copiedToClipboard: "已复制到剪贴板"
metrics: "指标" metrics: "指标"
overview: "概览" overview: "概览"
logs: "日志" logs: "日志"
@@ -1332,6 +1333,7 @@ preferenceSyncConflictChoiceCancel: "取消同步"
paste: "粘贴" paste: "粘贴"
emojiPalette: "表情符号调色板" emojiPalette: "表情符号调色板"
postForm: "投稿窗口" postForm: "投稿窗口"
textCount: "字数"
_emojiPalette: _emojiPalette:
palettes: "调色板" palettes: "调色板"
enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步" enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步"

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} 說了一些關於「{word}」的話"
makeActive: "啟用" makeActive: "啟用"
display: "檢視" display: "檢視"
copy: "複製" copy: "複製"
copiedToClipboard: "已複製到剪貼簿"
metrics: "指標" metrics: "指標"
overview: "概覽" overview: "概覽"
logs: "日誌" logs: "日誌"
@@ -1332,6 +1333,7 @@ preferenceSyncConflictChoiceCancel: "取消啟用同步"
paste: "貼上" paste: "貼上"
emojiPalette: "表情符號調色盤" emojiPalette: "表情符號調色盤"
postForm: "發文視窗" postForm: "發文視窗"
textCount: "字數"
_emojiPalette: _emojiPalette:
palettes: "調色盤" palettes: "調色盤"
enableSyncBetweenDevicesForPalettes: "啟用裝置與裝置之間的調色盤同步化" enableSyncBetweenDevicesForPalettes: "啟用裝置與裝置之間的調色盤同步化"
@@ -1355,6 +1357,8 @@ _settings:
appearanceBanner: "您可以根據喜好設定與用戶端外觀和顯示方式相關的設定。" appearanceBanner: "您可以根據喜好設定與用戶端外觀和顯示方式相關的設定。"
soundsBanner: "您可以調整用戶端播放的聲音設定。" soundsBanner: "您可以調整用戶端播放的聲音設定。"
timelineAndNote: "時間軸及貼文" timelineAndNote: "時間軸及貼文"
makeEveryTextElementsSelectable: "允許選取所有文字"
makeEveryTextElementsSelectable_description: "啟用此功能後,可能會在某些情境下降低可用性。"
_preferencesProfile: _preferencesProfile:
profileName: "設定檔案名稱" profileName: "設定檔案名稱"
profileNameDescription: "設定一個名稱來識別此裝置。" profileNameDescription: "設定一個名稱來識別此裝置。"
@@ -2519,6 +2523,7 @@ _notification:
achievementEarned: "獲得成就" achievementEarned: "獲得成就"
exportCompleted: "已完成匯出。" exportCompleted: "已完成匯出。"
login: "登入" login: "登入"
createToken: "建立存取權杖"
test: "通知測試" test: "通知測試"
app: "應用程式通知" app: "應用程式通知"
_actions: _actions:

View File

@@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.3.2-beta.3", "version": "2025.3.2-beta.4",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -6,6 +6,7 @@
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { ZipReader } from 'slacc'; import { ZipReader } from 'slacc';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { EmojisRepository, DriveFilesRepository } from '@/models/_.js'; import type { EmojisRepository, DriveFilesRepository } from '@/models/_.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
@@ -86,6 +87,7 @@ export class ImportCustomEmojisProcessorService {
const emojiPath = outputPath + '/' + record.fileName; const emojiPath = outputPath + '/' + record.fileName;
await this.emojisRepository.delete({ await this.emojisRepository.delete({
name: emojiInfo.name, name: emojiInfo.name,
host: IsNull(),
}); });
try { try {

View File

@@ -12,6 +12,7 @@ const siteName = document.querySelector<HTMLMetaElement>('meta[property="og:site
export const host = address.host; export const host = address.host;
export const hostname = address.hostname; export const hostname = address.hostname;
export const url = address.origin; export const url = address.origin;
export const port = address.port;
export const apiUrl = location.origin + '/api'; export const apiUrl = location.origin + '/api';
export const wsOrigin = location.origin; export const wsOrigin = location.origin;
export const lang = localStorage.getItem('lang') ?? 'en-US'; export const lang = localStorage.getItem('lang') ?? 'en-US';

View File

@@ -64,12 +64,12 @@ initialize({
initLocalStorage(); initLocalStorage();
queueMicrotask(() => { queueMicrotask(() => {
Promise.all([ Promise.all([
import('../src/components'), import('../src/components/index.js'),
import('../src/directives'), import('../src/directives/index.js'),
import('../src/widgets'), import('../src/widgets/index.js'),
import('../src/theme'), import('../src/theme.js'),
import('../src/preferences'), import('../src/preferences.js'),
import('../src/os'), import('../src/os.js'),
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => { ]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => {
setup((app) => { setup((app) => {
moduleInitialized = true; moduleInitialized = true;

View File

@@ -26,8 +26,6 @@ import { deckStore } from '@/ui/deck/deck-store.js';
import { analytics, initAnalytics } from '@/analytics.js'; import { analytics, initAnalytics } from '@/analytics.js';
import { miLocalStorage } from '@/local-storage.js'; import { miLocalStorage } from '@/local-storage.js';
import { fetchCustomEmojis } from '@/custom-emojis.js'; import { fetchCustomEmojis } from '@/custom-emojis.js';
import { setupRouter } from '@/router/main.js';
import { createMainRouter } from '@/router/definition.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
@@ -267,8 +265,6 @@ export async function common(createVue: () => App<Element>) {
const app = createVue(); const app = createVue();
setupRouter(app, createMainRouter);
if (_DEV_) { if (_DEV_) {
app.config.performance = true; app.config.performance = true;
} }

View File

@@ -24,7 +24,7 @@ import { miLocalStorage } from '@/local-storage.js';
import { claimAchievement, claimedAchievements } from '@/utility/achievements.js'; import { claimAchievement, claimedAchievements } from '@/utility/achievements.js';
import { initializeSw } from '@/utility/initialize-sw.js'; import { initializeSw } from '@/utility/initialize-sw.js';
import { emojiPicker } from '@/utility/emoji-picker.js'; import { emojiPicker } from '@/utility/emoji-picker.js';
import { mainRouter } from '@/router/main.js'; import { mainRouter } from '@/router.js';
import { makeHotkey } from '@/utility/hotkey.js'; import { makeHotkey } from '@/utility/hotkey.js';
import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';

View File

@@ -88,9 +88,9 @@ import { i18n } from '@/i18n.js';
import { dateString } from '@/filters/date.js'; import { dateString } from '@/filters/date.js';
import MkFolder from '@/components/MkFolder.vue'; import MkFolder from '@/components/MkFolder.vue';
import RouterView from '@/components/global/RouterView.vue'; import RouterView from '@/components/global/RouterView.vue';
import { useRouterFactory } from '@/router/supplier';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { createRouter } from '@/router.js';
const props = defineProps<{ const props = defineProps<{
report: Misskey.entities.AdminAbuseUserReportsResponse[number]; report: Misskey.entities.AdminAbuseUserReportsResponse[number];
@@ -100,10 +100,9 @@ const emit = defineEmits<{
(ev: 'resolved', reportId: string): void; (ev: 'resolved', reportId: string): void;
}>(); }>();
const routerFactory = useRouterFactory(); const targetRouter = createRouter(`/admin/user/${props.report.targetUserId}`);
const targetRouter = routerFactory(`/admin/user/${props.report.targetUserId}`);
targetRouter.init(); targetRouter.init();
const reporterRouter = routerFactory(`/admin/user/${props.report.reporterId}`); const reporterRouter = createRouter(`/admin/user/${props.report.reporterId}`);
reporterRouter.init(); reporterRouter.init();
const moderationNote = ref(props.report.moderationNote ?? ''); const moderationNote = ref(props.report.moderationNote ?? '');

View File

@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, shallowRef } from 'vue'; import { ref, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkWindow from '@/components/MkWindow.vue'; import MkWindow from '@/components/MkWindow.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
@@ -47,7 +47,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const uiWindow = shallowRef<InstanceType<typeof MkWindow>>(); const uiWindow = useTemplateRef('uiWindow');
const comment = ref(props.initialComment ?? ''); const comment = ref(props.initialComment ?? '');
function send() { function send() {

View File

@@ -8,10 +8,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef } from 'vue'; import { onMounted, onUnmounted, useTemplateRef } from 'vue';
import isChromatic from 'chromatic/isChromatic'; import isChromatic from 'chromatic/isChromatic';
const canvasEl = shallowRef<HTMLCanvasElement>(); const canvasEl = useTemplateRef('canvasEl');
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
scale?: number; scale?: number;

View File

@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef } from 'vue'; import { onMounted, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
@@ -37,8 +37,8 @@ const props = withDefaults(defineProps<{
}>(), { }>(), {
}); });
const rootEl = shallowRef<HTMLDivElement>(); const rootEl = useTemplateRef('rootEl');
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
async function ok() { async function ok() {
if (props.announcement.needConfirmationToRead) { if (props.announcement.needConfirmationToRead) {

View File

@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import XAntennaEditor from '@/components/MkAntennaEditor.vue'; import XAntennaEditor from '@/components/MkAntennaEditor.vue';
@@ -40,7 +40,7 @@ const emit = defineEmits<{
(ev: 'closed'): void, (ev: 'closed'): void,
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
function onAntennaCreated(newAntenna: Misskey.entities.Antenna) { function onAntennaCreated(newAntenna: Misskey.entities.Antenna) {
emit('created', newAntenna); emit('created', newAntenna);

View File

@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts"> <script lang="ts">
import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import { markRaw, ref, useTemplateRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import sanitizeHtml from 'sanitize-html'; import sanitizeHtml from 'sanitize-html';
import { emojilist, getEmojiName } from '@@/js/emojilist.js'; import { emojilist, getEmojiName } from '@@/js/emojilist.js';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@@/js/emoji-base.js'; import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@@/js/emoji-base.js';
@@ -139,7 +139,7 @@ const emit = defineEmits<{
}>(); }>();
const suggests = ref<Element>(); const suggests = ref<Element>();
const rootEl = shallowRef<HTMLDivElement>(); const rootEl = useTemplateRef('rootEl');
const fetching = ref(true); const fetching = ref(true);
const users = ref<any[]>([]); const users = ref<any[]>([]);

View File

@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, shallowRef } from 'vue'; import { nextTick, onMounted, useTemplateRef } from 'vue';
const props = defineProps<{ const props = defineProps<{
type?: 'button' | 'submit' | 'reset'; type?: 'button' | 'submit' | 'reset';
@@ -64,8 +64,8 @@ const emit = defineEmits<{
(ev: 'click', payload: MouseEvent): void; (ev: 'click', payload: MouseEvent): void;
}>(); }>();
const el = shallowRef<HTMLElement | null>(null); const el = useTemplateRef('el');
const ripples = shallowRef<HTMLElement | null>(null); const ripples = useTemplateRef('ripples');
onMounted(() => { onMounted(() => {
if (props.autofocus) { if (props.autofocus) {

View File

@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue'; import { ref, useTemplateRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue';
import { store } from '@/store.js'; import { store } from '@/store.js';
// APIs provided by Captcha services // APIs provided by Captcha services
@@ -69,7 +69,7 @@ const emit = defineEmits<{
const available = ref(false); const available = ref(false);
const captchaEl = shallowRef<HTMLDivElement | undefined>(); const captchaEl = useTemplateRef('captchaEl');
const captchaWidgetId = ref<string | undefined>(undefined); const captchaWidgetId = ref<string | undefined>(undefined);
const testcaptchaInput = ref(''); const testcaptchaInput = ref('');
const testcaptchaPassed = ref(false); const testcaptchaPassed = ref(false);

View File

@@ -45,12 +45,8 @@ export type ChartSrc =
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
/* eslint-disable id-denylist --
Chart.js has a `data` attribute in most chart definitions, which triggers the import { onMounted, ref, useTemplateRef, watch } from 'vue';
id-denylist violation when setting it. This is causing about 60+ lint issues.
As this is part of Chart.js's API it makes sense to disable the check here.
*/
import { onMounted, ref, shallowRef, watch } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { misskeyApiGet } from '@/utility/misskey-api.js'; import { misskeyApiGet } from '@/utility/misskey-api.js';
@@ -96,7 +92,7 @@ const props = withDefaults(defineProps<{
nowForChromatic: undefined, nowForChromatic: undefined,
}); });
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>(); const legendEl = useTemplateRef('legendEl');
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b)); const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
const negate = arr => arr.map(x => -x); const negate = arr => arr.map(x => -x);
@@ -134,7 +130,7 @@ let chartData: {
bytes?: boolean; bytes?: boolean;
} | null = null; } | null = null;
const chartEl = shallowRef<HTMLCanvasElement | null>(null); const chartEl = useTemplateRef('chartEl');
const fetching = ref(true); const fetching = ref(true);
const getDate = (ago: number) => { const getDate = (ago: number) => {
@@ -849,7 +845,7 @@ watch(() => [props.src, props.span], fetchAndRender);
onMounted(() => { onMounted(() => {
fetchAndRender(); fetchAndRender();
}); });
/* eslint-enable id-denylist */
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View File

@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch, toRefs, shallowRef, nextTick } from 'vue'; import { ref, watch, toRefs, useTemplateRef, nextTick } from 'vue';
import { debounce } from 'throttle-debounce'; import { debounce } from 'throttle-debounce';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@@ -61,7 +61,7 @@ const { modelValue } = toRefs(props);
const v = ref<string>(modelValue.value ?? ''); const v = ref<string>(modelValue.value ?? '');
const focused = ref(false); const focused = ref(false);
const changed = ref(false); const changed = ref(false);
const inputEl = shallowRef<HTMLTextAreaElement>(); const inputEl = useTemplateRef('inputEl');
const focus = () => inputEl.value?.focus(); const focus = () => inputEl.value?.focus();

View File

@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, toRefs } from 'vue'; import { ref, useTemplateRef, toRefs } from 'vue';
const props = defineProps<{ const props = defineProps<{
modelValue: string | null; modelValue: string | null;
@@ -39,7 +39,7 @@ const emit = defineEmits<{
const { modelValue } = toRefs(props); const { modelValue } = toRefs(props);
const v = ref(modelValue.value); const v = ref(modelValue.value);
const inputEl = shallowRef<HTMLElement>(); const inputEl = useTemplateRef('inputEl');
const onInput = () => { const onInput = () => {
emit('update:modelValue', v.value ?? ''); emit('update:modelValue', v.value ?? '');

View File

@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; import { onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@@ -58,9 +58,9 @@ const props = withDefaults(defineProps<{
maxHeight: null, maxHeight: null,
}); });
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
const contentEl = shallowRef<HTMLElement>(); const contentEl = useTemplateRef('contentEl');
const headerEl = shallowRef<HTMLElement>(); const headerEl = useTemplateRef('headerEl');
const showBody = ref(props.expanded); const showBody = ref(props.expanded);
const ignoreOmit = ref(false); const ignoreOmit = ref(false);
const omitted = ref(false); const omitted = ref(false);

View File

@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue'; import { onMounted, onBeforeUnmount, useTemplateRef, ref } from 'vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
import contains from '@/utility/contains.js'; import contains from '@/utility/contains.js';
@@ -34,7 +34,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const rootEl = shallowRef<HTMLDivElement>(); const rootEl = useTemplateRef('rootEl');
const zIndex = ref<number>(os.claimZIndex('high')); const zIndex = ref<number>(os.claimZIndex('high'));

View File

@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef, ref } from 'vue'; import { onMounted, useTemplateRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@@ -56,8 +56,8 @@ const props = defineProps<{
}>(); }>();
const imgUrl = getProxiedImageUrl(props.file.url, undefined, true); const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialogEl = useTemplateRef('dialogEl');
const imgEl = shallowRef<HTMLImageElement>(); const imgEl = useTemplateRef('imgEl');
let cropper: Cropper | null = null; let cropper: Cropper | null = null;
const loading = ref(true); const loading = ref(true);

View File

@@ -57,14 +57,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import MkLink from '@/components/MkLink.vue'; import MkLink from '@/components/MkLink.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
const props = defineProps<{ const props = defineProps<{
emoji: Misskey.entities.EmojiDetailed, emoji: Misskey.entities.EmojiDetailed,
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@@ -73,7 +73,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialogEl = useTemplateRef('dialogEl');
function cancel() { function cancel() {
emit('cancel'); emit('cancel');

View File

@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, computed } from 'vue'; import { ref, useTemplateRef, computed } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@@ -117,7 +117,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const inputValue = ref<string | number | null>(props.input?.default ?? null); const inputValue = ref<string | number | null>(props.input?.default ?? null);
const selectedValue = ref(props.select?.default ?? null); const selectedValue = ref(props.select?.default ?? null);

View File

@@ -47,7 +47,7 @@ import { i18n } from '@/i18n.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js'; import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
import { deviceKind } from '@/utility/device-kind.js'; import { deviceKind } from '@/utility/device-kind.js';
import { useRouter } from '@/router/supplier.js'; import { useRouter } from '@/router.js';
const router = useRouter(); const router = useRouter();

View File

@@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
@@ -129,8 +129,8 @@ const emit = defineEmits<{
(ev: 'open-folder', v: Misskey.entities.DriveFolder): void; (ev: 'open-folder', v: Misskey.entities.DriveFolder): void;
}>(); }>();
const loadMoreFiles = shallowRef<InstanceType<typeof MkButton>>(); const loadMoreFiles = useTemplateRef('loadMoreFiles');
const fileInput = shallowRef<HTMLInputElement>(); const fileInput = useTemplateRef('fileInput');
const folder = ref<Misskey.entities.DriveFolder | null>(null); const folder = ref<Misskey.entities.DriveFolder | null>(null);
const files = ref<Misskey.entities.DriveFile[]>([]); const files = ref<Misskey.entities.DriveFile[]>([]);

View File

@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef } from 'vue'; import { ref, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import XDrive from '@/components/MkDrive.vue'; import XDrive from '@/components/MkDrive.vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
@@ -43,7 +43,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const selected = ref<Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]>([]); const selected = ref<Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]>([]);

View File

@@ -89,7 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { shallowRef, ref, computed, nextTick, onMounted, onDeactivated, onUnmounted } from 'vue'; import { useTemplateRef, ref, computed, nextTick, onMounted, onDeactivated, onUnmounted } from 'vue';
import { url } from '@@/js/config.js'; import { url } from '@@/js/config.js';
import { embedRouteWithScrollbar } from '@@/js/embed-page.js'; import { embedRouteWithScrollbar } from '@@/js/embed-page.js';
import type { EmbeddableEntity, EmbedParams } from '@@/js/embed-page.js'; import type { EmbeddableEntity, EmbedParams } from '@@/js/embed-page.js';
@@ -121,7 +121,7 @@ const props = defineProps<{
}>(); }>();
//#region Modalの制御 //#region Modalの制御
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialogEl = useTemplateRef('dialogEl');
function cancel() { function cancel() {
emit('cancel'); emit('cancel');
@@ -198,9 +198,9 @@ function doCopy() {
//#endregion //#endregion
//#region プレビューのリサイズ //#region プレビューのリサイズ
const resizerRootEl = shallowRef<HTMLDivElement>(); const resizerRootEl = useTemplateRef('resizerRootEl');
const iframeLoading = ref(true); const iframeLoading = ref(true);
const iframeEl = shallowRef<HTMLIFrameElement>(); const iframeEl = useTemplateRef('iframeEl');
const iframeHeight = ref(0); const iframeHeight = ref(0);
const iframeScale = ref(1); const iframeScale = ref(1);
const iframeStyle = computed(() => { const iframeStyle = computed(() => {

View File

@@ -115,7 +115,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, computed, watch, onMounted } from 'vue'; import { ref, useTemplateRef, computed, watch, onMounted } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { import {
emojilist, emojilist,
@@ -157,8 +157,8 @@ const emit = defineEmits<{
(ev: 'esc'): void; (ev: 'esc'): void;
}>(); }>();
const searchEl = shallowRef<HTMLInputElement>(); const searchEl = useTemplateRef('searchEl');
const emojisEl = shallowRef<HTMLDivElement>(); const emojisEl = useTemplateRef('emojisEl');
const { const {
emojiPickerScale, emojiPickerScale,

View File

@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
@@ -64,8 +64,8 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>(); const picker = useTemplateRef('picker');
function chosen(emoji: string) { function chosen(emoji: string) {
emit('done', emoji); emit('done', emoji);

View File

@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef, ref } from 'vue'; import { useTemplateRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
@@ -42,7 +42,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const caption = ref(props.default); const caption = ref(props.default);

View File

@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, shallowRef, watch } from 'vue'; import { onMounted, ref, useTemplateRef, watch } from 'vue';
import { miLocalStorage } from '@/local-storage.js'; import { miLocalStorage } from '@/local-storage.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { getBgColor } from '@/utility/get-bg-color.js'; import { getBgColor } from '@/utility/get-bg-color.js';
@@ -46,7 +46,7 @@ const props = withDefaults(defineProps<{
persistKey: null, persistKey: null,
}); });
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
const parentBg = ref<string | null>(null); const parentBg = ref<string | null>(null);
// eslint-disable-next-line vue/no-setup-props-reactivity-loss // eslint-disable-next-line vue/no-setup-props-reactivity-loss
const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded); const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded);

View File

@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, ref, shallowRef } from 'vue'; import { nextTick, onMounted, ref, useTemplateRef } from 'vue';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { getBgColor } from '@/utility/get-bg-color.js'; import { getBgColor } from '@/utility/get-bg-color.js';
@@ -74,7 +74,7 @@ const props = withDefaults(defineProps<{
spacerMax: 22, spacerMax: 22,
}); });
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
const bgSame = ref(false); const bgSame = ref(false);
const opened = ref(props.defaultOpen); const opened = ref(props.defaultOpen);
const openedAtLeastOnce = ref(props.defaultOpen); const openedAtLeastOnce = ref(props.defaultOpen);

View File

@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, shallowRef } from 'vue'; import { reactive, useTemplateRef } from 'vue';
import MkInput from './MkInput.vue'; import MkInput from './MkInput.vue';
import MkTextarea from './MkTextarea.vue'; import MkTextarea from './MkTextarea.vue';
import MkSwitch from './MkSwitch.vue'; import MkSwitch from './MkSwitch.vue';
@@ -99,7 +99,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const values = reactive({}); const values = reactive({});
for (const item in props.form) { for (const item in props.form) {

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, nextTick, watch, shallowRef, ref } from 'vue'; import { onMounted, nextTick, watch, useTemplateRef, ref } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
@@ -35,8 +35,8 @@ const props = withDefaults(defineProps<{
label: '', label: '',
}); });
const rootEl = shallowRef<HTMLDivElement | null>(null); const rootEl = useTemplateRef('rootEl');
const chartEl = shallowRef<HTMLCanvasElement | null>(null); const chartEl = useTemplateRef('chartEl');
const now = new Date(); const now = new Date();
let chartInstance: Chart | null = null; let chartInstance: Chart | null = null;
const fetching = ref(true); const fetching = ref(true);

View File

@@ -26,12 +26,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, computed, nextTick, watch } from 'vue'; import { ref, useTemplateRef, computed, nextTick, watch } from 'vue';
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
import { isHorizontalSwipeSwiping as isSwiping } from '@/utility/touch.js'; import { isHorizontalSwipeSwiping as isSwiping } from '@/utility/touch.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
const rootEl = shallowRef<HTMLDivElement>(); const rootEl = useTemplateRef('rootEl');
const tabModel = defineModel<string>('tab'); const tabModel = defineModel<string>('tab');

View File

@@ -83,7 +83,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch, ref } from 'vue'; import { computed, nextTick, onMounted, onUnmounted, useTemplateRef, watch, ref } from 'vue';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { render } from 'buraha'; import { render } from 'buraha';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
@@ -120,9 +120,9 @@ const props = withDefaults(defineProps<{
}); });
const viewId = uuid(); const viewId = uuid();
const canvas = shallowRef<HTMLCanvasElement>(); const canvas = useTemplateRef('canvas');
const root = shallowRef<HTMLDivElement>(); const root = useTemplateRef('root');
const img = shallowRef<HTMLImageElement>(); const img = useTemplateRef('img');
const loaded = ref(false); const loaded = ref(false);
const canvasWidth = ref(64); const canvasWidth = ref(64);
const canvasHeight = ref(64); const canvasHeight = ref(64);

View File

@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; import { onMounted, onUnmounted, nextTick, ref, useTemplateRef, watch, computed, toRefs } from 'vue';
import { debounce } from 'throttle-debounce'; import { debounce } from 'throttle-debounce';
import { useInterval } from '@@/js/use-interval.js'; import { useInterval } from '@@/js/use-interval.js';
import type { InputHTMLAttributes } from 'vue'; import type { InputHTMLAttributes } from 'vue';
@@ -92,9 +92,9 @@ const focused = ref(false);
const changed = ref(false); const changed = ref(false);
const invalid = ref(false); const invalid = ref(false);
const filled = computed(() => v.value !== '' && v.value != null); const filled = computed(() => v.value !== '' && v.value != null);
const inputEl = shallowRef<HTMLInputElement>(); const inputEl = useTemplateRef('inputEl');
const prefixEl = shallowRef<HTMLElement>(); const prefixEl = useTemplateRef('prefixEl');
const suffixEl = shallowRef<HTMLElement>(); const suffixEl = useTemplateRef('suffixEl');
const height = const height =
props.small ? 33 : props.small ? 33 :
props.large ? 39 : props.large ? 39 :

View File

@@ -84,8 +84,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, computed, shallowRef } from 'vue'; import { onMounted, ref, computed, useTemplateRef } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import type { HeatmapSource } from '@/components/MkHeatmap.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import MkChart from '@/components/MkChart.vue'; import MkChart from '@/components/MkChart.vue';
import { useChartTooltip } from '@/use/use-chart-tooltip.js'; import { useChartTooltip } from '@/use/use-chart-tooltip.js';
@@ -95,7 +96,6 @@ import { misskeyApiGet } from '@/utility/misskey-api.js';
import { instance } from '@/instance.js'; import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import MkHeatmap from '@/components/MkHeatmap.vue'; import MkHeatmap from '@/components/MkHeatmap.vue';
import type { HeatmapSource } from '@/components/MkHeatmap.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue'; import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue'; import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
@@ -109,8 +109,8 @@ const chartLimit = 500;
const chartSpan = ref<'hour' | 'day'>('hour'); const chartSpan = ref<'hour' | 'day'>('hour');
const chartSrc = ref('active-users'); const chartSrc = ref('active-users');
const heatmapSrc = ref<HeatmapSource>('active-users'); const heatmapSrc = ref<HeatmapSource>('active-users');
const subDoughnutEl = shallowRef<HTMLCanvasElement>(); const subDoughnutEl = useTemplateRef('subDoughnutEl');
const pubDoughnutEl = shallowRef<HTMLCanvasElement>(); const pubDoughnutEl = useTemplateRef('pubDoughnutEl');
const { handler: externalTooltipHandler1 } = useChartTooltip({ const { handler: externalTooltipHandler1 } = useChartTooltip({
position: 'middle', position: 'middle',

View File

@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { navbarItemDef } from '@/navbar.js'; import { navbarItemDef } from '@/navbar.js';
import { deviceKind } from '@/utility/device-kind.js'; import { deviceKind } from '@/utility/device-kind.js';
@@ -48,7 +48,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
deviceKind === 'smartphone' ? 'drawer' : deviceKind === 'smartphone' ? 'drawer' :
'dialog'; 'dialog';
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const menu = prefer.s.menu; const menu = prefer.s.menu;

View File

@@ -88,7 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; import { useTemplateRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
import type { Keymap } from '@/utility/hotkey.js'; import type { Keymap } from '@/utility/hotkey.js';
@@ -151,8 +151,8 @@ function hasFocus() {
return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement); return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
} }
const playerEl = shallowRef<HTMLDivElement>(); const playerEl = useTemplateRef('playerEl');
const audioEl = shallowRef<HTMLAudioElement>(); const audioEl = useTemplateRef('audioEl');
// eslint-disable-next-line vue/no-setup-props-reactivity-loss // eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore')); const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore'));

View File

@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, onUnmounted, shallowRef } from 'vue'; import { computed, onMounted, onUnmounted, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import PhotoSwipeLightbox from 'photoswipe/lightbox'; import PhotoSwipeLightbox from 'photoswipe/lightbox';
import PhotoSwipe from 'photoswipe'; import PhotoSwipe from 'photoswipe';
@@ -46,7 +46,7 @@ const props = defineProps<{
raw?: boolean; raw?: boolean;
}>(); }>();
const gallery = shallowRef<HTMLDivElement>(); const gallery = useTemplateRef('gallery');
const pswpZIndex = os.claimZIndex('middle'); const pswpZIndex = os.claimZIndex('middle');
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
const count = computed(() => props.mediaList.filter(media => previewable(media)).length); const count = computed(() => props.mediaList.filter(media => previewable(media)).length);

View File

@@ -109,7 +109,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, computed, watch, onDeactivated, onActivated, onMounted } from 'vue'; import { ref, useTemplateRef, computed, watch, onDeactivated, onActivated, onMounted } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
import type { Keymap } from '@/utility/hotkey.js'; import type { Keymap } from '@/utility/hotkey.js';
@@ -299,8 +299,8 @@ async function toggleSensitive(file: Misskey.entities.DriveFile) {
} }
// MediaControl: Video State // MediaControl: Video State
const videoEl = shallowRef<HTMLVideoElement>(); const videoEl = useTemplateRef('videoEl');
const playerEl = shallowRef<HTMLDivElement>(); const playerEl = useTemplateRef('playerEl');
const isHoverring = ref(false); const isHoverring = ref(false);
const controlsShowing = computed(() => { const controlsShowing = computed(() => {
if (!oncePlayed.value) return true; if (!oncePlayed.value) return true;

View File

@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, provide, shallowRef, watch } from 'vue'; import { nextTick, onMounted, onUnmounted, provide, useTemplateRef, watch } from 'vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
@@ -28,7 +28,7 @@ const emit = defineEmits<{
provide('isNestingMenu', true); provide('isNestingMenu', true);
const el = shallowRef<HTMLElement>(); const el = useTemplateRef('el');
const align = 'left'; const align = 'left';
const SCROLLBAR_THICKNESS = 16; const SCROLLBAR_THICKNESS = 16;

View File

@@ -214,7 +214,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineAsyncComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, unref, watch } from 'vue'; import { computed, defineAsyncComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, unref, watch } from 'vue';
import type { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio, MenuRadioOption, MenuParent } from '@/types/menu.js'; import type { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio, MenuRadioOption, MenuParent } from '@/types/menu.js';
import type { Keymap } from '@/utility/hotkey.js'; import type { Keymap } from '@/utility/hotkey.js';
import MkSwitchButton from '@/components/MkSwitch.button.vue'; import MkSwitchButton from '@/components/MkSwitch.button.vue';
@@ -247,11 +247,11 @@ const big = isTouchUsing;
const isNestingMenu = inject<boolean>('isNestingMenu', false); const isNestingMenu = inject<boolean>('isNestingMenu', false);
const itemsEl = shallowRef<HTMLElement>(); const itemsEl = useTemplateRef('itemsEl');
const items2 = ref<InnerMenuItem[]>(); const items2 = ref<InnerMenuItem[]>();
const child = shallowRef<InstanceType<typeof XChild>>(); const child = useTemplateRef('child');
const keymap = { const keymap = {
'up|k|shift+tab': { 'up|k|shift+tab': {
@@ -292,7 +292,7 @@ watch(() => props.items, () => {
}); });
const childMenu = ref<MenuItem[] | null>(); const childMenu = ref<MenuItem[] | null>();
const childTarget = shallowRef<HTMLElement | null>(); const childTarget = useTemplateRef('childTarget');
function closeChild() { function closeChild() {
childMenu.value = null; childMenu.value = null;

View File

@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch, ref, shallowRef, computed } from 'vue'; import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch, ref, useTemplateRef, computed } from 'vue';
import type { Keymap } from '@/utility/hotkey.js'; import type { Keymap } from '@/utility/hotkey.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { isTouchUsing } from '@/utility/touch.js'; import { isTouchUsing } from '@/utility/touch.js';
@@ -100,8 +100,8 @@ const maxHeight = ref<number>();
const fixed = ref(false); const fixed = ref(false);
const transformOrigin = ref('center'); const transformOrigin = ref('center');
const showing = ref(true); const showing = ref(true);
const modalRootEl = shallowRef<HTMLElement>(); const modalRootEl = useTemplateRef('modalRootEl');
const content = shallowRef<HTMLElement>(); const content = useTemplateRef('content');
const zIndex = os.claimZIndex(props.zPriority); const zIndex = os.claimZIndex(props.zPriority);
const useSendAnime = ref(false); const useSendAnime = ref(false);
const type = computed<ModalTypes>(() => { const type = computed<ModalTypes>(() => {

View File

@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef, ref } from 'vue'; import { onMounted, onUnmounted, useTemplateRef, ref } from 'vue';
import MkModal from './MkModal.vue'; import MkModal from './MkModal.vue';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@@ -47,9 +47,9 @@ const emit = defineEmits<{
(event: 'esc'): void; (event: 'esc'): void;
}>(); }>();
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
const headerEl = shallowRef<HTMLElement>(); const headerEl = useTemplateRef('headerEl');
const bodyWidth = ref(0); const bodyWidth = ref(0);
const bodyHeight = ref(0); const bodyHeight = ref(0);

View File

@@ -178,7 +178,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onMounted, ref, shallowRef, watch, provide } from 'vue'; import { computed, inject, onMounted, ref, useTemplateRef, watch, provide } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js'; import { isLink } from '@@/js/is-link.js';
@@ -271,14 +271,14 @@ if (noteViewInterruptors.length > 0) {
const isRenote = Misskey.note.isPureRenote(note.value); const isRenote = Misskey.note.isPureRenote(note.value);
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
const menuButton = shallowRef<HTMLElement>(); const menuButton = useTemplateRef('menuButton');
const renoteButton = shallowRef<HTMLElement>(); const renoteButton = useTemplateRef('renoteButton');
const renoteTime = shallowRef<HTMLElement>(); const renoteTime = useTemplateRef('renoteTime');
const reactButton = shallowRef<HTMLElement>(); const reactButton = useTemplateRef('reactButton');
const clipButton = shallowRef<HTMLElement>(); const clipButton = useTemplateRef('clipButton');
const appearNote = computed(() => getAppearNote(note.value)); const appearNote = computed(() => getAppearNote(note.value));
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>(); const galleryEl = useTemplateRef('galleryEl');
const isMyRenote = $i && ($i.id === note.value.userId); const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false); const showContent = ref(false);
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null); const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);

View File

@@ -211,7 +211,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onMounted, provide, ref, shallowRef } from 'vue'; import { computed, inject, onMounted, provide, ref, useTemplateRef } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js'; import { isLink } from '@@/js/is-link.js';
@@ -290,14 +290,14 @@ if (noteViewInterruptors.length > 0) {
const isRenote = Misskey.note.isPureRenote(note.value); const isRenote = Misskey.note.isPureRenote(note.value);
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
const menuButton = shallowRef<HTMLElement>(); const menuButton = useTemplateRef('menuButton');
const renoteButton = shallowRef<HTMLElement>(); const renoteButton = useTemplateRef('renoteButton');
const renoteTime = shallowRef<HTMLElement>(); const renoteTime = useTemplateRef('renoteTime');
const reactButton = shallowRef<HTMLElement>(); const reactButton = useTemplateRef('reactButton');
const clipButton = shallowRef<HTMLElement>(); const clipButton = useTemplateRef('clipButton');
const appearNote = computed(() => getAppearNote(note.value)); const appearNote = computed(() => getAppearNote(note.value));
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>(); const galleryEl = useTemplateRef('galleryEl');
const isMyRenote = $i && ($i.id === note.value.userId); const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false); const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);

View File

@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import type { Paging } from '@/components/MkPagination.vue'; import type { Paging } from '@/components/MkPagination.vue';
import MkNote from '@/components/MkNote.vue'; import MkNote from '@/components/MkNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
@@ -46,7 +46,7 @@ const props = defineProps<{
disableAutoLoad?: boolean; disableAutoLoad?: boolean;
}>(); }>();
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); const pagingComponent = useTemplateRef('pagingComponent');
defineExpose({ defineExpose({
pagingComponent, pagingComponent,

View File

@@ -30,13 +30,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef } from 'vue'; import { ref, useTemplateRef } from 'vue';
import type { Ref } from 'vue'; import { notificationTypes } from '@@/js/const.js';
import MkSwitch from './MkSwitch.vue'; import MkSwitch from './MkSwitch.vue';
import MkInfo from './MkInfo.vue'; import MkInfo from './MkInfo.vue';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import type { Ref } from 'vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import { notificationTypes } from '@@/js/const.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>; type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>;
@@ -52,7 +52,7 @@ const props = withDefaults(defineProps<{
excludeTypes: () => [], excludeTypes: () => [],
}); });
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const typesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as TypesMap); const typesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as TypesMap);

View File

@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated } from 'vue'; import { onUnmounted, onDeactivated, onMounted, computed, useTemplateRef, onActivated } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { notificationTypes } from '@@/js/const.js'; import type { notificationTypes } from '@@/js/const.js';
import MkPagination from '@/components/MkPagination.vue'; import MkPagination from '@/components/MkPagination.vue';
@@ -41,7 +41,7 @@ const props = defineProps<{
excludeTypes?: typeof notificationTypes[number][]; excludeTypes?: typeof notificationTypes[number][];
}>(); }>();
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); const pagingComponent = useTemplateRef('pagingComponent');
const pagination = computed(() => prefer.r.useGroupedNotifications.value ? { const pagination = computed(() => prefer.r.useGroupedNotifications.value ? {
endpoint: 'i/notifications-grouped' as const, endpoint: 'i/notifications-grouped' as const,

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef, ref } from 'vue'; import { onMounted, onUnmounted, useTemplateRef, ref } from 'vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@@ -22,7 +22,7 @@ const props = withDefaults(defineProps<{
maxHeight: 200, maxHeight: 200,
}); });
const content = shallowRef<HTMLElement>(); const content = useTemplateRef('content');
const omitted = ref(false); const omitted = ref(false);
const ignoreOmit = ref(false); const ignoreOmit = ref(false);

View File

@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue'; import { computed, onMounted, onUnmounted, provide, ref, useTemplateRef } from 'vue';
import { url } from '@@/js/config.js'; import { url } from '@@/js/config.js';
import type { PageMetadata } from '@/page.js'; import type { PageMetadata } from '@/page.js';
import RouterView from '@/components/global/RouterView.vue'; import RouterView from '@/components/global/RouterView.vue';
@@ -41,8 +41,7 @@ import { i18n } from '@/i18n.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js'; import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import { openingWindowsCount } from '@/os.js'; import { openingWindowsCount } from '@/os.js';
import { claimAchievement } from '@/utility/achievements.js'; import { claimAchievement } from '@/utility/achievements.js';
import { useRouterFactory } from '@/router/supplier.js'; import { createRouter, mainRouter } from '@/router.js';
import { mainRouter } from '@/router/main.js';
import { analytics } from '@/analytics.js'; import { analytics } from '@/analytics.js';
import { DI } from '@/di.js'; import { DI } from '@/di.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
@@ -55,14 +54,12 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const routerFactory = useRouterFactory(); const windowRouter = createRouter(props.initialPath);
const windowRouter = routerFactory(props.initialPath);
const pageMetadata = ref<null | PageMetadata>(null); const pageMetadata = ref<null | PageMetadata>(null);
const windowEl = shallowRef<InstanceType<typeof MkWindow>>(); const windowEl = useTemplateRef('windowEl');
const history = ref<{ path: string; key: string; }[]>([{ const history = ref<{ path: string; }[]>([{
path: windowRouter.getCurrentPath(), path: windowRouter.getCurrentFullPath(),
key: windowRouter.getCurrentKey(),
}]); }]);
const buttonsLeft = computed(() => { const buttonsLeft = computed(() => {
const buttons: Record<string, unknown>[] = []; const buttons: Record<string, unknown>[] = [];
@@ -100,20 +97,20 @@ function getSearchMarker(path: string) {
const searchMarkerId = ref<string | null>(getSearchMarker(props.initialPath)); const searchMarkerId = ref<string | null>(getSearchMarker(props.initialPath));
windowRouter.addListener('push', ctx => { windowRouter.addListener('push', ctx => {
history.value.push({ path: ctx.path, key: ctx.key }); history.value.push({ path: ctx.fullPath });
}); });
windowRouter.addListener('replace', ctx => { windowRouter.addListener('replace', ctx => {
history.value.pop(); history.value.pop();
history.value.push({ path: ctx.path, key: ctx.key }); history.value.push({ path: ctx.fullPath });
}); });
windowRouter.addListener('change', ctx => { windowRouter.addListener('change', ctx => {
if (_DEV_) console.log('windowRouter: change', ctx.path); if (_DEV_) console.log('windowRouter: change', ctx.fullPath);
searchMarkerId.value = getSearchMarker(ctx.path); searchMarkerId.value = getSearchMarker(ctx.fullPath);
analytics.page({ analytics.page({
path: ctx.path, path: ctx.fullPath,
title: ctx.path, title: ctx.fullPath,
}); });
}); });
@@ -142,20 +139,20 @@ const contextmenu = computed(() => ([{
icon: 'ti ti-external-link', icon: 'ti ti-external-link',
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
action: () => { action: () => {
window.open(url + windowRouter.getCurrentPath(), '_blank', 'noopener'); window.open(url + windowRouter.getCurrentFullPath(), '_blank', 'noopener');
windowEl.value?.close(); windowEl.value?.close();
}, },
}, { }, {
icon: 'ti ti-link', icon: 'ti ti-link',
text: i18n.ts.copyLink, text: i18n.ts.copyLink,
action: () => { action: () => {
copyToClipboard(url + windowRouter.getCurrentPath()); copyToClipboard(url + windowRouter.getCurrentFullPath());
}, },
}])); }]));
function back() { function back() {
history.value.pop(); history.value.pop();
windowRouter.replace(history.value.at(-1)!.path, history.value.at(-1)!.key); windowRouter.replace(history.value.at(-1)!.path);
} }
function reload() { function reload() {
@@ -167,12 +164,12 @@ function close() {
} }
function expand() { function expand() {
mainRouter.push(windowRouter.getCurrentPath(), 'forcePage'); mainRouter.push(windowRouter.getCurrentFullPath(), 'forcePage');
windowEl.value?.close(); windowEl.value?.close();
} }
function popout() { function popout() {
_popout(windowRouter.getCurrentPath(), windowEl.value?.$el); _popout(windowRouter.getCurrentFullPath(), windowEl.value?.$el);
windowEl.value?.close(); windowEl.value?.close();
} }

View File

@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue'; import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { useDocumentVisibility } from '@@/js/use-document-visibility.js'; import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@@/js/scroll.js'; import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@@/js/scroll.js';
@@ -106,7 +106,7 @@ const emit = defineEmits<{
(ev: 'status', error: boolean): void; (ev: 'status', error: boolean): void;
}>(); }>();
const rootEl = shallowRef<HTMLElement>(); const rootEl = useTemplateRef('rootEl');
// 遡り中かどうか // 遡り中かどうか
const backed = ref(false); const backed = ref(false);

View File

@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef, ref } from 'vue'; import { onMounted, useTemplateRef, ref } from 'vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
@@ -54,8 +54,8 @@ const emit = defineEmits<{
(ev: 'cancelled'): void; (ev: 'cancelled'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const passwordInput = shallowRef<InstanceType<typeof MkInput>>(); const passwordInput = useTemplateRef('passwordInput');
const password = ref(''); const password = ref('');
const isBackupCode = ref(false); const isBackupCode = ref(false);
const token = ref<string | null>(null); const token = ref<string | null>(null);

View File

@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef } from 'vue'; import { ref, useTemplateRef } from 'vue';
import MkModal from './MkModal.vue'; import MkModal from './MkModal.vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
@@ -28,7 +28,7 @@ const emit = defineEmits<{
(ev: 'closing'): void; (ev: 'closing'): void;
}>(); }>();
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const manualShowing = ref(true); const manualShowing = ref(true);
const hiding = ref(false); const hiding = ref(false);

View File

@@ -99,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue'; import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
@@ -165,11 +165,11 @@ const emit = defineEmits<{
(ev: 'fileChangeSensitive', fileId: string, to: boolean): void; (ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
}>(); }>();
const textareaEl = shallowRef<HTMLTextAreaElement | null>(null); const textareaEl = useTemplateRef('textareaEl');
const cwInputEl = shallowRef<HTMLInputElement | null>(null); const cwInputEl = useTemplateRef('cwInputEl');
const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null); const hashtagsInputEl = useTemplateRef('hashtagsInputEl');
const visibilityButton = shallowRef<HTMLElement>(); const visibilityButton = useTemplateRef('visibilityButton');
const otherSettingsButton = shallowRef<HTMLElement>(); const otherSettingsButton = useTemplateRef('otherSettingsButton');
const posting = ref(false); const posting = ref(false);
const posted = ref(false); const posted = ref(false);

View File

@@ -25,10 +25,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import type { PostFormProps } from '@/types/post-form.js';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkPostForm from '@/components/MkPostForm.vue'; import MkPostForm from '@/components/MkPostForm.vue';
import type { PostFormProps } from '@/types/post-form.js';
const props = withDefaults(defineProps<PostFormProps & { const props = withDefaults(defineProps<PostFormProps & {
instant?: boolean; instant?: boolean;
@@ -42,8 +42,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const form = shallowRef<InstanceType<typeof MkPostForm>>();
function onPosted() { function onPosted() {
modal.value?.close({ modal.value?.close({

View File

@@ -23,9 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'; import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
import { i18n } from '@/i18n.js';
import { getScrollContainer } from '@@/js/scroll.js'; import { getScrollContainer } from '@@/js/scroll.js';
import { i18n } from '@/i18n.js';
import { isHorizontalSwipeSwiping } from '@/utility/touch.js'; import { isHorizontalSwipeSwiping } from '@/utility/touch.js';
const SCROLL_STOP = 10; const SCROLL_STOP = 10;
@@ -43,7 +43,7 @@ const pullDistance = ref(0);
let supportPointerDesktop = false; let supportPointerDesktop = false;
let startScreenY: number | null = null; let startScreenY: number | null = null;
const rootEl = shallowRef<HTMLDivElement>(); const rootEl = useTemplateRef('rootEl');
let scrollEl: HTMLElement | null = null; let scrollEl: HTMLElement | null = null;
let disabled = false; let disabled = false;

View File

@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
import { isTouchUsing } from '@/utility/touch.js'; import { isTouchUsing } from '@/utility/touch.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@@ -58,8 +58,8 @@ const emit = defineEmits<{
(ev: 'dragEnded', value: number): void; (ev: 'dragEnded', value: number): void;
}>(); }>();
const containerEl = shallowRef<HTMLElement>(); const containerEl = useTemplateRef('containerEl');
const thumbEl = shallowRef<HTMLElement>(); const thumbEl = useTemplateRef('thumbEl');
const rawValue = ref((props.modelValue - props.min) / (props.max - props.min)); const rawValue = ref((props.modelValue - props.min) / (props.max - props.min));
const steppedRawValue = computed(() => { const steppedRawValue = computed(() => {

View File

@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, shallowRef } from 'vue'; import { defineAsyncComponent, useTemplateRef } from 'vue';
import { useTooltip } from '@/use/use-tooltip.js'; import { useTooltip } from '@/use/use-tooltip.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@@ -20,7 +20,7 @@ const props = defineProps<{
withTooltip?: boolean; withTooltip?: boolean;
}>(); }>();
const elRef = shallowRef(); const elRef = useTemplateRef('elRef');
if (props.withTooltip) { if (props.withTooltip) {
useTooltip(elRef, (showing) => { useTooltip(elRef, (showing) => {

View File

@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onMounted, shallowRef, watch } from 'vue'; import { computed, inject, onMounted, useTemplateRef, watch } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { getUnicodeEmoji } from '@@/js/emojilist.js'; import { getUnicodeEmoji } from '@@/js/emojilist.js';
import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue'; import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue';
@@ -50,7 +50,7 @@ const emit = defineEmits<{
(ev: 'reactionToggled', emoji: string, newCount: number): void; (ev: 'reactionToggled', emoji: string, newCount: number): void;
}>(); }>();
const buttonEl = shallowRef<HTMLElement>(); const buttonEl = useTemplateRef('buttonEl');
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, '')); const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction)); const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction));

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, nextTick, shallowRef, ref } from 'vue'; import { onMounted, nextTick, useTemplateRef, ref } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
import { store } from '@/store.js'; import { store } from '@/store.js';
@@ -23,8 +23,8 @@ import { initChart } from '@/utility/init-chart.js';
initChart(); initChart();
const rootEl = shallowRef<HTMLDivElement | null>(null); const rootEl = useTemplateRef('rootEl');
const chartEl = shallowRef<HTMLCanvasElement | null>(null); const chartEl = useTemplateRef('chartEl');
let chartInstance: Chart | null = null; let chartInstance: Chart | null = null;
const fetching = ref(true); const fetching = ref(true);

View File

@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef } from 'vue'; import { onMounted, useTemplateRef } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { store } from '@/store.js'; import { store } from '@/store.js';
@@ -20,7 +20,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
initChart(); initChart();
const chartEl = shallowRef<HTMLCanvasElement | null>(null); const chartEl = useTemplateRef('chartEl');
const { handler: externalTooltipHandler } = useChartTooltip(); const { handler: externalTooltipHandler } = useChartTooltip();

View File

@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { shallowRef } from 'vue'; import { useTemplateRef } from 'vue';
import type { OpenOnRemoteOptions } from '@/utility/please-login.js'; import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
import MkSignin from '@/components/MkSignin.vue'; import MkSignin from '@/components/MkSignin.vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
@@ -46,7 +46,7 @@ const emit = defineEmits<{
(ev: 'cancelled'): void; (ev: 'cancelled'): void;
}>(); }>();
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
function onClose() { function onClose() {
emit('cancelled'); emit('cancelled');

View File

@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef, ref } from 'vue'; import { useTemplateRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import XSignup from '@/components/MkSignupDialog.form.vue'; import XSignup from '@/components/MkSignupDialog.form.vue';
import XServerRules from '@/components/MkSignupDialog.rules.vue'; import XServerRules from '@/components/MkSignupDialog.rules.vue';
@@ -52,7 +52,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const isAcceptedServerRule = ref(false); const isAcceptedServerRule = ref(false);

View File

@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'; import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
const particles = ref<{ const particles = ref<{
id: string, id: string,
@@ -66,7 +66,7 @@ const particles = ref<{
dur: number, dur: number,
color: string color: string
}[]>([]); }[]>([]);
const el = shallowRef<HTMLElement>(); const el = useTemplateRef('el');
const width = ref(0); const width = ref(0);
const height = ref(0); const height = ref(0);
const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202']; const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202'];

View File

@@ -98,7 +98,7 @@ import type { SearchIndexItem } from '@/utility/autogen/settings-search-index.js
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { getScrollContainer } from '@@/js/scroll.js'; import { getScrollContainer } from '@@/js/scroll.js';
import { useRouter } from '@/router/supplier.js'; import { useRouter } from '@/router.js';
import { initIntlString, compareStringIncludes } from '@/utility/intl-string.js'; import { initIntlString, compareStringIncludes } from '@/utility/intl-string.js';
const props = defineProps<{ const props = defineProps<{

View File

@@ -92,15 +92,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref, shallowRef, toRefs } from 'vue'; import { computed, onMounted, ref, useTemplateRef, toRefs } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import type { import type {
MkSystemWebhookEditorProps, MkSystemWebhookEditorProps,
MkSystemWebhookResult, MkSystemWebhookResult,
SystemWebhookEventType, SystemWebhookEventType,
} from '@/components/MkSystemWebhookEditor.impl.js'; } from '@/components/MkSystemWebhookEditor.impl.js';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
@@ -122,7 +122,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialogEl = useTemplateRef('dialogEl');
const props = defineProps<MkSystemWebhookEditorProps>(); const props = defineProps<MkSystemWebhookEditorProps>();

View File

@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch, onBeforeUnmount, ref, shallowRef } from 'vue'; import { onMounted, watch, onBeforeUnmount, ref, useTemplateRef } from 'vue';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
const loaded = !!window.TagCanvas; const loaded = !!window.TagCanvas;
@@ -24,9 +24,9 @@ const computedStyle = getComputedStyle(document.documentElement);
const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
const available = ref(false); const available = ref(false);
const rootEl = shallowRef<HTMLElement | null>(null); const rootEl = useTemplateRef('rootEl');
const canvasEl = shallowRef<HTMLCanvasElement | null>(null); const canvasEl = useTemplateRef('canvasEl');
const tagsEl = shallowRef<HTMLElement | null>(null); const tagsEl = useTemplateRef('tagsEl');
const width = ref(300); const width = ref(300);
watch(available, () => { watch(available, () => {

View File

@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue'; import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, useTemplateRef } from 'vue';
import { debounce } from 'throttle-debounce'; import { debounce } from 'throttle-debounce';
import type { SuggestionType } from '@/utility/autocomplete.js'; import type { SuggestionType } from '@/utility/autocomplete.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@@ -75,7 +75,7 @@ const focused = ref(false);
const changed = ref(false); const changed = ref(false);
const invalid = ref(false); const invalid = ref(false);
const filled = computed(() => v.value !== '' && v.value != null); const filled = computed(() => v.value !== '' && v.value != null);
const inputEl = shallowRef<HTMLTextAreaElement>(); const inputEl = useTemplateRef('inputEl');
const preview = ref(false); const preview = ref(false);
let autocompleteWorker: Autocomplete | null = null; let autocompleteWorker: Autocomplete | null = null;

View File

@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue'; import { computed, watch, onUnmounted, provide, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { BasicTimelineType } from '@/timelines.js'; import type { BasicTimelineType } from '@/timelines.js';
import type { Paging } from '@/components/MkPagination.vue'; import type { Paging } from '@/components/MkPagination.vue';
@@ -67,8 +67,8 @@ type TimelineQueryType = {
roleId?: string roleId?: string
}; };
const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>(); const prComponent = useTemplateRef('prComponent');
const tlComponent = shallowRef<InstanceType<typeof MkNotes>>(); const tlComponent = useTemplateRef('tlComponent');
let tlNotesCount = 0; let tlNotesCount = 0;

View File

@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef, ref } from 'vue'; import { useTemplateRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkInput from './MkInput.vue'; import MkInput from './MkInput.vue';
import MkSwitch from './MkSwitch.vue'; import MkSwitch from './MkSwitch.vue';
@@ -77,7 +77,7 @@ const emit = defineEmits<{
const defaultPermissions = Misskey.permissions.filter(p => !p.startsWith('read:admin') && !p.startsWith('write:admin')); const defaultPermissions = Misskey.permissions.filter(p => !p.startsWith('read:admin') && !p.startsWith('write:admin'));
const adminPermissions = Misskey.permissions.filter(p => p.startsWith('read:admin') || p.startsWith('write:admin')); const adminPermissions = Misskey.permissions.filter(p => p.startsWith('read:admin') || p.startsWith('write:admin'));
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
const name = ref(props.initialName); const name = ref(props.initialName);
const permissionSwitches = ref({} as Record<(typeof Misskey.permissions)[number], boolean>); const permissionSwitches = ref({} as Record<(typeof Misskey.permissions)[number], boolean>);
const permissionSwitchesForAdmin = ref({} as Record<(typeof Misskey.permissions)[number], boolean>); const permissionSwitchesForAdmin = ref({} as Record<(typeof Misskey.permissions)[number], boolean>);

View File

@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, shallowRef } from 'vue'; import { nextTick, onMounted, onUnmounted, useTemplateRef } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { calcPopupPosition } from '@/utility/popup-position.js'; import { calcPopupPosition } from '@/utility/popup-position.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
@@ -51,7 +51,7 @@ const emit = defineEmits<{
// タイミングによっては最初から showing = false な場合があり、その場合に closed 扱いにしないと永久にDOMに残ることになる // タイミングによっては最初から showing = false な場合があり、その場合に closed 扱いにしないと永久にDOMに残ることになる
if (!props.showing) emit('closed'); if (!props.showing) emit('closed');
const el = shallowRef<HTMLElement>(); const el = useTemplateRef('el');
const zIndex = os.claimZIndex('high'); const zIndex = os.claimZIndex('high');
function setPosition() { function setPosition() {

View File

@@ -148,7 +148,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, watch } from 'vue'; import { ref, useTemplateRef, watch } from 'vue';
import { host } from '@@/js/config.js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import XNote from '@/components/MkTutorialDialog.Note.vue'; import XNote from '@/components/MkTutorialDialog.Note.vue';
@@ -158,7 +159,6 @@ import XSensitive from '@/components/MkTutorialDialog.Sensitive.vue';
import MkAnimBg from '@/components/MkAnimBg.vue'; import MkAnimBg from '@/components/MkAnimBg.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js'; import { instance } from '@/instance.js';
import { host } from '@@/js/config.js';
import { claimAchievement } from '@/utility/achievements.js'; import { claimAchievement } from '@/utility/achievements.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@@ -170,7 +170,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
// eslint-disable-next-line vue/no-setup-props-reactivity-loss // eslint-disable-next-line vue/no-setup-props-reactivity-loss
const page = ref(props.initialPage ?? 0); const page = ref(props.initialPage ?? 0);

View File

@@ -15,15 +15,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef } from 'vue'; import { onMounted, useTemplateRef } from 'vue';
import { version } from '@@/js/config.js';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkSparkle from '@/components/MkSparkle.vue'; import MkSparkle from '@/components/MkSparkle.vue';
import { version } from '@@/js/config.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { confetti } from '@/utility/confetti.js'; import { confetti } from '@/utility/confetti.js';
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
function whatIsNew() { function whatIsNew() {
modal.value?.close(); modal.value?.close();

View File

@@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, computed, shallowRef } from 'vue'; import { onMounted, ref, computed, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { host as currentHost, hostname } from '@@/js/config.js'; import { host as currentHost, hostname } from '@@/js/config.js';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@@ -94,7 +94,7 @@ const host = ref('');
const users = ref<Misskey.entities.UserLite[]>([]); const users = ref<Misskey.entities.UserLite[]>([]);
const recentUsers = ref<Misskey.entities.UserDetailed[]>([]); const recentUsers = ref<Misskey.entities.UserDetailed[]>([]);
const selected = ref<Misskey.entities.UserLite | null>(null); const selected = ref<Misskey.entities.UserLite | null>(null);
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialogEl = useTemplateRef('dialogEl');
function search() { function search() {
if (username.value === '' && host.value === '') { if (username.value === '' && host.value === '') {

View File

@@ -128,7 +128,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, shallowRef, watch, nextTick, defineAsyncComponent } from 'vue'; import { ref, useTemplateRef, watch, nextTick, defineAsyncComponent } from 'vue';
import { host } from '@@/js/config.js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import XProfile from '@/components/MkUserSetupDialog.Profile.vue'; import XProfile from '@/components/MkUserSetupDialog.Profile.vue';
@@ -137,7 +138,6 @@ import XPrivacy from '@/components/MkUserSetupDialog.Privacy.vue';
import MkAnimBg from '@/components/MkAnimBg.vue'; import MkAnimBg from '@/components/MkAnimBg.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js'; import { instance } from '@/instance.js';
import { host } from '@@/js/config.js';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { store } from '@/store.js'; import { store } from '@/store.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@@ -146,9 +146,8 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = useTemplateRef('dialog');
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
const page = ref(store.s.accountSetupWizard); const page = ref(store.s.accountSetupWizard);
watch(page, () => { watch(page, () => {

View File

@@ -42,12 +42,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, shallowRef, ref } from 'vue'; import { nextTick, useTemplateRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
currentVisibility: typeof Misskey.noteVisibilities[number]; currentVisibility: typeof Misskey.noteVisibilities[number];

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef, ref, nextTick } from 'vue'; import { onMounted, useTemplateRef, ref, nextTick } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient'; import gradient from 'chartjs-plugin-gradient';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@@ -25,7 +25,7 @@ import { initChart } from '@/utility/init-chart.js';
initChart(); initChart();
const chartEl = shallowRef<HTMLCanvasElement | null>(null); const chartEl = useTemplateRef('chartEl');
const now = new Date(); const now = new Date();
let chartInstance: Chart | null = null; let chartInstance: Chart | null = null;
const chartLimit = 30; const chartLimit = 30;

View File

@@ -14,10 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, shallowRef } from 'vue'; import { watch, useTemplateRef } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = useTemplateRef('modal');
const props = defineProps<{ const props = defineProps<{
success: boolean; success: boolean;

View File

@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue'; import { onBeforeUnmount, onMounted, provide, useTemplateRef, ref } from 'vue';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
import contains from '@/utility/contains.js'; import contains from '@/utility/contains.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@@ -114,7 +114,7 @@ const emit = defineEmits<{
provide('inWindow', true); provide('inWindow', true);
const rootEl = shallowRef<HTMLElement | null>(); const rootEl = useTemplateRef('rootEl');
const showing = ref(true); const showing = ref(true);
let beforeClickedAt = 0; let beforeClickedAt = 0;
const maximized = ref(false); const maximized = ref(false);

View File

@@ -14,12 +14,12 @@ export type MkABehavior = 'window' | 'browser' | null;
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, shallowRef } from 'vue'; import { computed, inject, useTemplateRef } from 'vue';
import { url } from '@@/js/config.js'; import { url } from '@@/js/config.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { useRouter } from '@/router/supplier.js'; import { useRouter } from '@/router.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
to: string; to: string;
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
const behavior = props.behavior ?? inject<MkABehavior>('linkNavigationBehavior', null); const behavior = props.behavior ?? inject<MkABehavior>('linkNavigationBehavior', null);
const el = shallowRef<HTMLElement>(); const el = useTemplateRef('el');
defineExpose({ $el: el }); defineExpose({ $el: el });

View File

@@ -11,9 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, onActivated, onBeforeUnmount, ref, shallowRef } from 'vue'; import { nextTick, onMounted, onActivated, onBeforeUnmount, ref, useTemplateRef } from 'vue';
const rootEl = shallowRef<HTMLDivElement>(); const rootEl = useTemplateRef('rootEl');
const showing = ref(false); const showing = ref(false);
const observer = new IntersectionObserver( const observer = new IntersectionObserver(

View File

@@ -53,7 +53,7 @@ export type Tab = {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue'; import { nextTick, onMounted, onUnmounted, useTemplateRef, watch } from 'vue';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@@ -69,9 +69,9 @@ const emit = defineEmits<{
(ev: 'tabClick', key: string); (ev: 'tabClick', key: string);
}>(); }>();
const el = shallowRef<HTMLElement | null>(null); const el = useTemplateRef('el');
const tabHighlightEl = useTemplateRef('tabHighlightEl');
const tabRefs: Record<string, HTMLElement | null> = {}; const tabRefs: Record<string, HTMLElement | null> = {};
const tabHighlightEl = shallowRef<HTMLElement | null>(null);
function onTabMousedown(tab: Tab, ev: MouseEvent): void { function onTabMousedown(tab: Tab, ev: MouseEvent): void {
// ユーザビリティの観点からmousedown時にはonClickは呼ばない // ユーザビリティの観点からmousedown時にはonClickは呼ばない

View File

@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, ref, inject, shallowRef, computed } from 'vue'; import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'vue';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { scrollToTop } from '@@/js/scroll.js'; import { scrollToTop } from '@@/js/scroll.js';
import XTabs from './MkPageHeader.tabs.vue'; import XTabs from './MkPageHeader.tabs.vue';
@@ -75,7 +75,7 @@ const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMe
const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle); const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle);
const thin_ = props.thin || inject('shouldHeaderThin', false); const thin_ = props.thin || inject('shouldHeaderThin', false);
const el = shallowRef<HTMLElement | undefined>(undefined); const el = useTemplateRef('el');
const bg = ref<string | undefined>(undefined); const bg = ref<string | undefined>(undefined);
const narrow = ref(false); const narrow = ref(false);
const hasTabs = computed(() => props.tabs.length > 0); const hasTabs = computed(() => props.tabs.length > 0);

View File

@@ -23,9 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, provide, inject, ref, watch, useTemplateRef } from 'vue'; import { onMounted, onUnmounted, provide, inject, ref, watch, useTemplateRef } from 'vue';
import type { Ref } from 'vue';
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@@/js/const.js'; import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@@/js/const.js';
import type { Ref } from 'vue';
const rootEl = useTemplateRef('rootEl'); const rootEl = useTemplateRef('rootEl');
const headerEl = useTemplateRef('headerEl'); const headerEl = useTemplateRef('headerEl');

View File

@@ -14,13 +14,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onBeforeUnmount, provide, ref, shallowRef } from 'vue'; import { inject, provide, ref, shallowRef } from 'vue';
import type { IRouter, Resolved } from '@/nirax.js'; import type { Router } from '@/router.js';
import type { PathResolvedResult } from '@/lib/nirax.js';
import MkLoadingPage from '@/pages/_loading_.vue'; import MkLoadingPage from '@/pages/_loading_.vue';
import { DI } from '@/di.js'; import { DI } from '@/di.js';
const props = defineProps<{ const props = defineProps<{
router?: IRouter; router?: Router;
}>(); }>();
const router = props.router ?? inject(DI.router); const router = props.router ?? inject(DI.router);
@@ -32,7 +33,7 @@ if (router == null) {
const currentDepth = inject(DI.routerCurrentDepth, 0); const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1); provide(DI.routerCurrentDepth, currentDepth + 1);
function resolveNested(current: Resolved, d = 0): Resolved | null { function resolveNested(current: PathResolvedResult, d = 0): PathResolvedResult | null {
if (d === currentDepth) { if (d === currentDepth) {
return current; return current;
} else { } else {
@@ -47,19 +48,13 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
const current = resolveNested(router.current)!; const current = resolveNested(router.current)!;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props); const currentPageProps = ref(current.props);
const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props))); const key = ref(router.getCurrentFullPath());
function onChange({ resolved, key: newKey }) { router.useListener('change', ({ resolved }) => {
const current = resolveNested(resolved); const current = resolveNested(resolved);
if (current == null || 'redirect' in current.route) return; if (current == null || 'redirect' in current.route) return;
currentPageComponent.value = current.route.component; currentPageComponent.value = current.route.component;
currentPageProps.value = current.props; currentPageProps.value = current.props;
key.value = newKey + JSON.stringify(Object.fromEntries(current.props)); key.value = router.getCurrentFullPath();
}
router.addListener('change', onChange);
onBeforeUnmount(() => {
router.removeListener('change', onChange);
}); });
</script> </script>

View File

@@ -5,10 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div class="_pageContainer" style="height: 100%;"> <div class="_pageContainer" style="height: 100%;">
<KeepAlive <KeepAlive :max="prefer.s.numberOfPageCache">
:max="prefer.s.numberOfPageCache"
:exclude="pageCacheController"
>
<Suspense :timeout="0"> <Suspense :timeout="0">
<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
@@ -21,15 +18,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onBeforeUnmount, provide, ref, shallowRef, computed, nextTick } from 'vue'; import { inject, provide, ref, shallowRef } from 'vue';
import type { IRouter, Resolved, RouteDef } from '@/nirax.js'; import type { Router } from '@/router.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { globalEvents } from '@/events.js';
import MkLoadingPage from '@/pages/_loading_.vue'; import MkLoadingPage from '@/pages/_loading_.vue';
import { DI } from '@/di.js'; import { DI } from '@/di.js';
const props = defineProps<{ const props = defineProps<{
router?: IRouter; router?: Router;
}>(); }>();
const router = props.router ?? inject(DI.router); const router = props.router ?? inject(DI.router);
@@ -44,45 +40,12 @@ provide(DI.routerCurrentDepth, currentDepth + 1);
const current = router.current!; const current = router.current!;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props); const currentPageProps = ref(current.props);
const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props))); const key = ref(router.getCurrentFullPath());
function onChange({ resolved, key: newKey }) { router.useListener('change', ({ resolved }) => {
if (resolved == null || 'redirect' in resolved.route) return; if (resolved == null || 'redirect' in resolved.route) return;
currentPageComponent.value = resolved.route.component; currentPageComponent.value = resolved.route.component;
currentPageProps.value = resolved.props; currentPageProps.value = resolved.props;
key.value = newKey + JSON.stringify(Object.fromEntries(resolved.props)); key.value = router.getCurrentFullPath();
nextTick(() => {
// ページ遷移完了後に再びキャッシュを有効化
if (clearCacheRequested.value) {
clearCacheRequested.value = false;
}
});
}
router.addListener('change', onChange);
// #region キャッシュ制御
/**
* キャッシュクリアが有効になったら、全キャッシュをクリアする
*
* keepAlive側にwatcherがあるのですぐ消えるとはおもうけど、念のためページ遷移完了まではキャッシュを無効化しておく。
* キャッシュ有効時向けにexcludeを使いたい場合は、pageCacheControllerに並列に突っ込むのではなく、下に追記すること
*/
const pageCacheController = computed(() => clearCacheRequested.value ? /.*/ : undefined);
const clearCacheRequested = ref(false);
globalEvents.on('requestClearPageCache', () => {
if (_DEV_) console.log('clear page cache requested');
if (!clearCacheRequested.value) {
clearCacheRequested.value = true;
}
});
// #endregion
onBeforeUnmount(() => {
router.removeListener('change', onChange);
}); });
</script> </script>

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:duration="200" :duration="200"
tag="div" :class="$style.tabs" tag="div" :class="$style.tabs"
> >
<div v-for="(tab, i) in tabs" :key="tab.key" :class="$style.tab" :style="{ '--i': i - 1 }"> <div v-for="(tab, i) in tabs" :key="tab.fullPath" :class="$style.tab" :style="{ '--i': i - 1 }">
<div v-if="i > 0" :class="$style.tabBg" @click="back()"></div> <div v-if="i > 0" :class="$style.tabBg" @click="back()"></div>
<div :class="$style.tabFg" @click.stop="back()"> <div :class="$style.tabFg" @click.stop="back()">
<div v-if="i > 0" :class="$style.tabMenu"> <div v-if="i > 0" :class="$style.tabMenu">
@@ -41,16 +41,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onBeforeUnmount, provide, ref, shallowRef, computed, nextTick } from 'vue'; import { inject, provide, shallowRef } from 'vue';
import type { IRouter, Resolved, RouteDef } from '@/nirax.js'; import type { Router } from '@/router.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { globalEvents } from '@/events.js';
import MkLoadingPage from '@/pages/_loading_.vue'; import MkLoadingPage from '@/pages/_loading_.vue';
import { DI } from '@/di.js'; import { DI } from '@/di.js';
import { deepEqual } from '@/utility/deep-equal.js'; import { deepEqual } from '@/utility/deep-equal.js';
const props = defineProps<{ const props = defineProps<{
router?: IRouter; router?: Router;
}>(); }>();
const router = props.router ?? inject(DI.router); const router = props.router ?? inject(DI.router);
@@ -63,26 +62,36 @@ const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1); provide(DI.routerCurrentDepth, currentDepth + 1);
const tabs = shallowRef([{ const tabs = shallowRef([{
key: router.getCurrentPath(), fullPath: router.getCurrentFullPath(),
path: router.getCurrentPath(), routePath: router.current.route.path,
route: router.current.route.path,
component: 'component' in router.current.route ? router.current.route.component : MkLoadingPage, component: 'component' in router.current.route ? router.current.route.component : MkLoadingPage,
props: router.current.props, props: router.current.props,
}]); }]);
function onChange({ resolved }) { function mount() {
const currentTab = tabs.value[tabs.value.length - 1]; const currentTab = tabs.value[tabs.value.length - 1];
const route = resolved.route.path; tabs.value = [currentTab];
if (resolved == null || 'redirect' in resolved.route) return; }
if (resolved.route.path === currentTab.path && deepEqual(resolved.props, currentTab.props)) return;
const fullPath = router.getCurrentPath();
if (tabs.value.some(tab => tab.route === route && deepEqual(resolved.props, tab.props))) { function back() {
const prev = tabs.value[tabs.value.length - 2];
tabs.value = [...tabs.value.slice(0, tabs.value.length - 1)];
router.replace(prev.fullPath);
}
router.useListener('change', ({ resolved }) => {
const currentTab = tabs.value[tabs.value.length - 1];
const routePath = resolved.route.path;
if (resolved == null || 'redirect' in resolved.route) return;
if (resolved.route.path === currentTab.routePath && deepEqual(resolved.props, currentTab.props)) return;
const fullPath = router.getCurrentFullPath();
if (tabs.value.some(tab => tab.routePath === routePath && deepEqual(resolved.props, tab.props))) {
const newTabs = []; const newTabs = [];
for (const tab of tabs.value) { for (const tab of tabs.value) {
newTabs.push(tab); newTabs.push(tab);
if (tab.route === route && deepEqual(resolved.props, tab.props)) { if (tab.routePath === routePath && deepEqual(resolved.props, tab.props)) {
break; break;
} }
} }
@@ -93,45 +102,23 @@ function onChange({ resolved }) {
tabs.value = tabs.value.length >= prefer.s.numberOfPageCache ? [ tabs.value = tabs.value.length >= prefer.s.numberOfPageCache ? [
...tabs.value.slice(1), ...tabs.value.slice(1),
{ {
key: fullPath, fullPath: fullPath,
path: fullPath, routePath,
route,
component: resolved.route.component, component: resolved.route.component,
props: resolved.props, props: resolved.props,
}, },
] : [...tabs.value, { ] : [...tabs.value, {
key: fullPath, fullPath: fullPath,
path: fullPath, routePath,
route,
component: resolved.route.component, component: resolved.route.component,
props: resolved.props, props: resolved.props,
}]; }];
} });
function onReplace({ path }) { router.useListener('replace', ({ fullPath }) => {
const currentTab = tabs.value[tabs.value.length - 1]; const currentTab = tabs.value[tabs.value.length - 1];
console.log('replace', currentTab.path, path); currentTab.fullPath = fullPath;
currentTab.path = path;
tabs.value = [...tabs.value.slice(0, tabs.value.length - 1), currentTab]; tabs.value = [...tabs.value.slice(0, tabs.value.length - 1), currentTab];
}
function mount() {
const currentTab = tabs.value[tabs.value.length - 1];
tabs.value = [currentTab];
}
function back() {
const prev = tabs.value[tabs.value.length - 2];
tabs.value = [...tabs.value.slice(0, tabs.value.length - 1)];
router.replace(prev.path, prev.key);
}
router.addListener('replace', onReplace);
router.addListener('change', onChange);
onBeforeUnmount(() => {
router.removeListener('replace', onReplace);
router.removeListener('change', onChange);
}); });
</script> </script>

View File

@@ -39,10 +39,12 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ cell.value }} {{ cell.value }}
</div> </div>
<div v-else-if="cellType === 'boolean'"> <div v-else-if="cellType === 'boolean'">
<div :class="[$style.bool, { <div
[$style.boolTrue]: cell.value === true, :class="[$style.bool, {
'ti ti-check': cell.value === true, [$style.boolTrue]: cell.value === true,
}]"></div> 'ti ti-check': cell.value === true,
}]"
></div>
</div> </div>
<div v-else-if="cellType === 'image'"> <div v-else-if="cellType === 'image'">
<img <img
@@ -88,14 +90,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref, shallowRef, toRefs, watch } from 'vue'; import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref, useTemplateRef, toRefs, watch } from 'vue';
import type { Size } from '@/components/grid/grid.js';
import type { CellValue, GridCell } from '@/components/grid/cell.js';
import type { GridRowSetting } from '@/components/grid/row.js';
import { GridEventEmitter } from '@/components/grid/grid.js'; import { GridEventEmitter } from '@/components/grid/grid.js';
import { useTooltip } from '@/use/use-tooltip.js'; import { useTooltip } from '@/use/use-tooltip.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { equalCellAddress, getCellAddress } from '@/components/grid/grid-utils.js'; import { equalCellAddress, getCellAddress } from '@/components/grid/grid-utils.js';
import type { Size } from '@/components/grid/grid.js';
import type { CellValue, GridCell } from '@/components/grid/cell.js';
import type { GridRowSetting } from '@/components/grid/row.js';
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'operation:beginEdit', sender: GridCell): void; (ev: 'operation:beginEdit', sender: GridCell): void;
@@ -111,9 +113,9 @@ const props = defineProps<{
const { cell, bus } = toRefs(props); const { cell, bus } = toRefs(props);
const rootEl = shallowRef<InstanceType<typeof HTMLTableCellElement>>(); const rootEl = useTemplateRef('rootEl');
const contentAreaEl = shallowRef<InstanceType<typeof HTMLDivElement>>(); const contentAreaEl = useTemplateRef('contentAreaEl');
const inputAreaEl = shallowRef<InstanceType<typeof HTMLDivElement>>(); const inputAreaEl = useTemplateRef('inputAreaEl');
/** 値が編集中かどうか */ /** 値が編集中かどうか */
const editing = ref<boolean>(false); const editing = ref<boolean>(false);

View File

@@ -4,11 +4,11 @@
*/ */
import type { InjectionKey, Ref } from 'vue'; import type { InjectionKey, Ref } from 'vue';
import type { IRouter } from '@/nirax.js'; import type { Router } from '@/router.js';
export const DI = { export const DI = {
routerCurrentDepth: Symbol() as InjectionKey<number>, routerCurrentDepth: Symbol() as InjectionKey<number>,
router: Symbol() as InjectionKey<IRouter>, router: Symbol() as InjectionKey<Router>,
mock: Symbol() as InjectionKey<boolean>, mock: Symbol() as InjectionKey<boolean>,
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>, pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>,
}; };

View File

@@ -10,5 +10,4 @@ export const globalEvents = new EventEmitter<{
themeChanging: () => void; themeChanging: () => void;
themeChanged: () => void; themeChanged: () => void;
clientNotification: (notification: Misskey.entities.Notification) => void; clientNotification: (notification: Misskey.entities.Notification) => void;
requestClearPageCache: () => void;
}>(); }>();

View File

@@ -5,7 +5,7 @@
// NIRAX --- A lightweight router // NIRAX --- A lightweight router
import { onMounted, shallowRef } from 'vue'; import { onBeforeUnmount, onMounted, shallowRef } from 'vue';
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import type { Component, ShallowRef } from 'vue'; import type { Component, ShallowRef } from 'vue';
@@ -23,7 +23,6 @@ interface RouteDefBase {
loginRequired?: boolean; loginRequired?: boolean;
name?: string; name?: string;
hash?: string; hash?: string;
globalCacheKey?: string;
children?: RouteDef[]; children?: RouteDef[];
} }
@@ -46,31 +45,28 @@ type ParsedPath = (string | {
optional?: boolean; optional?: boolean;
})[]; })[];
export type RouterEvent = { export type RouterEvents = {
change: (ctx: { change: (ctx: {
beforePath: string; beforeFullPath: string;
path: string; fullPath: string;
resolved: Resolved; resolved: PathResolvedResult;
key: string;
}) => void; }) => void;
replace: (ctx: { replace: (ctx: {
path: string; fullPath: string;
key: string;
}) => void; }) => void;
push: (ctx: { push: (ctx: {
beforePath: string; beforeFullPath: string;
path: string; fullPath: string;
route: RouteDef | null; route: RouteDef | null;
props: Map<string, string> | null; props: Map<string, string> | null;
key: string;
}) => void; }) => void;
same: () => void; same: () => void;
}; };
export type Resolved = { export type PathResolvedResult = {
route: RouteDef; route: RouteDef;
props: Map<string, string | boolean>; props: Map<string, string | boolean>;
child?: Resolved; child?: PathResolvedResult;
redirected?: boolean; redirected?: boolean;
/** @internal */ /** @internal */
@@ -106,124 +102,39 @@ function parsePath(path: string): ParsedPath {
return res; return res;
} }
export interface IRouter extends EventEmitter<RouterEvent> { export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
current: Resolved; private routes: DEF;
currentRef: ShallowRef<Resolved>; public current: PathResolvedResult;
currentRoute: ShallowRef<RouteDef>; public currentRef: ShallowRef<PathResolvedResult>;
navHook: ((path: string, flag?: RouterFlag) => boolean) | null;
/**
* eventListenerの定義後に必ず呼び出すこと
*/
init(): void;
resolve(path: string): Resolved | null;
getCurrentPath(): string;
getCurrentKey(): string;
push(path: string, flag?: RouterFlag): void;
replace(path: string, key?: string | null): void;
/** @see EventEmitter */
eventNames(): Array<EventEmitter.EventNames<RouterEvent>>;
/** @see EventEmitter */
listeners<T extends EventEmitter.EventNames<RouterEvent>>(
event: T
): Array<EventEmitter.EventListener<RouterEvent, T>>;
/** @see EventEmitter */
listenerCount(
event: EventEmitter.EventNames<RouterEvent>
): number;
/** @see EventEmitter */
emit<T extends EventEmitter.EventNames<RouterEvent>>(
event: T,
...args: EventEmitter.EventArgs<RouterEvent, T>
): boolean;
/** @see EventEmitter */
on<T extends EventEmitter.EventNames<RouterEvent>>(
event: T,
fn: EventEmitter.EventListener<RouterEvent, T>,
context?: any
): this;
/** @see EventEmitter */
addListener<T extends EventEmitter.EventNames<RouterEvent>>(
event: T,
fn: EventEmitter.EventListener<RouterEvent, T>,
context?: any
): this;
/** @see EventEmitter */
once<T extends EventEmitter.EventNames<RouterEvent>>(
event: T,
fn: EventEmitter.EventListener<RouterEvent, T>,
context?: any
): this;
/** @see EventEmitter */
removeListener<T extends EventEmitter.EventNames<RouterEvent>>(
event: T,
fn?: EventEmitter.EventListener<RouterEvent, T>,
context?: any,
once?: boolean | undefined
): this;
/** @see EventEmitter */
off<T extends EventEmitter.EventNames<RouterEvent>>(
event: T,
fn?: EventEmitter.EventListener<RouterEvent, T>,
context?: any,
once?: boolean | undefined
): this;
/** @see EventEmitter */
removeAllListeners(
event?: EventEmitter.EventNames<RouterEvent>
): this;
}
export class Router extends EventEmitter<RouterEvent> implements IRouter {
private routes: RouteDef[];
public current: Resolved;
public currentRef: ShallowRef<Resolved>;
public currentRoute: ShallowRef<RouteDef>; public currentRoute: ShallowRef<RouteDef>;
private currentPath: string; private currentFullPath: string; // /foo/bar?baz=qux#hash
private isLoggedIn: boolean; private isLoggedIn: boolean;
private notFoundPageComponent: Component; private notFoundPageComponent: Component;
private currentKey = Date.now().toString();
private redirectCount = 0; private redirectCount = 0;
public navHook: ((path: string, flag?: RouterFlag) => boolean) | null = null; public navHook: ((fullPath: string, flag?: RouterFlag) => boolean) | null = null;
constructor(routes: Router['routes'], currentPath: Router['currentPath'], isLoggedIn: boolean, notFoundPageComponent: Component) { constructor(routes: DEF, currentFullPath: Nirax<DEF>['currentFullPath'], isLoggedIn: boolean, notFoundPageComponent: Component) {
super(); super();
this.routes = routes; this.routes = routes;
this.current = this.resolve(currentPath)!; this.current = this.resolve(currentFullPath)!;
this.currentRef = shallowRef(this.current); this.currentRef = shallowRef(this.current);
this.currentRoute = shallowRef(this.current.route); this.currentRoute = shallowRef(this.current.route);
this.currentPath = currentPath; this.currentFullPath = currentFullPath;
this.isLoggedIn = isLoggedIn; this.isLoggedIn = isLoggedIn;
this.notFoundPageComponent = notFoundPageComponent; this.notFoundPageComponent = notFoundPageComponent;
} }
public init() { public init() {
const res = this.navigate(this.currentPath, null, false); const res = this.navigate(this.currentFullPath, false);
this.emit('replace', { this.emit('replace', {
path: res._parsedRoute.fullPath, fullPath: res._parsedRoute.fullPath,
key: this.currentKey,
}); });
} }
public resolve(path: string): Resolved | null { public resolve(fullPath: string): PathResolvedResult | null {
const fullPath = path; let path = fullPath;
let queryString: string | null = null; let queryString: string | null = null;
let hash: string | null = null; let hash: string | null = null;
if (path[0] === '/') path = path.substring(1); if (path[0] === '/') path = path.substring(1);
@@ -242,7 +153,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
hash, hash,
}; };
function check(routes: RouteDef[], _parts: string[]): Resolved | null { function check(routes: RouteDef[], _parts: string[]): PathResolvedResult | null {
forEachRouteLoop: forEachRouteLoop:
for (const route of routes) { for (const route of routes) {
let parts = [..._parts]; let parts = [..._parts];
@@ -345,14 +256,14 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
return check(this.routes, _parts); return check(this.routes, _parts);
} }
private navigate(path: string, key: string | null | undefined, emitChange = true, _redirected = false): Resolved { private navigate(fullPath: string, emitChange = true, _redirected = false): PathResolvedResult {
const beforePath = this.currentPath; const beforeFullPath = this.currentFullPath;
this.currentPath = path; this.currentFullPath = fullPath;
const res = this.resolve(this.currentPath); const res = this.resolve(this.currentFullPath);
if (res == null) { if (res == null) {
throw new Error('no route found for: ' + path); throw new Error('no route found for: ' + fullPath);
} }
if ('redirect' in res.route) { if ('redirect' in res.route) {
@@ -366,7 +277,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
if (_redirected && this.redirectCount++ > 10) { if (_redirected && this.redirectCount++ > 10) {
throw new Error('redirect loop detected'); throw new Error('redirect loop detected');
} }
return this.navigate(redirectPath, null, emitChange, true); return this.navigate(redirectPath, emitChange, true);
} }
if (res.route.loginRequired && !this.isLoggedIn) { if (res.route.loginRequired && !this.isLoggedIn) {
@@ -374,19 +285,15 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
res.props.set('showLoginPopup', true); res.props.set('showLoginPopup', true);
} }
const isSamePath = beforePath === path;
if (isSamePath && key == null) key = this.currentKey;
this.current = res; this.current = res;
this.currentRef.value = res; this.currentRef.value = res;
this.currentRoute.value = res.route; this.currentRoute.value = res.route;
this.currentKey = res.route.globalCacheKey ?? key ?? path;
if (emitChange && res.route.path !== '/:(*)') { if (emitChange && res.route.path !== '/:(*)') {
this.emit('change', { this.emit('change', {
beforePath, beforeFullPath,
path, fullPath,
resolved: res, resolved: res,
key: this.currentKey,
}); });
} }
@@ -397,70 +304,45 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
}; };
} }
public getCurrentPath() { public getCurrentFullPath() {
return this.currentPath; return this.currentFullPath;
} }
public getCurrentKey() { public push(fullPath: string, flag?: RouterFlag) {
return this.currentKey; const beforeFullPath = this.currentFullPath;
} if (fullPath === beforeFullPath) {
public push(path: string, flag?: RouterFlag) {
const beforePath = this.currentPath;
if (path === beforePath) {
this.emit('same'); this.emit('same');
return; return;
} }
if (this.navHook) { if (this.navHook) {
const cancel = this.navHook(path, flag); const cancel = this.navHook(fullPath, flag);
if (cancel) return; if (cancel) return;
} }
const res = this.navigate(path, null); const res = this.navigate(fullPath);
if (res.route.path === '/:(*)') { if (res.route.path === '/:(*)') {
location.href = path; location.href = fullPath;
} else { } else {
this.emit('push', { this.emit('push', {
beforePath, beforeFullPath,
path: res._parsedRoute.fullPath, fullPath: res._parsedRoute.fullPath,
route: res.route, route: res.route,
props: res.props, props: res.props,
key: this.currentKey,
}); });
} }
} }
public replace(path: string, key?: string | null) { public replace(fullPath: string) {
const res = this.navigate(path, key); const res = this.navigate(fullPath);
this.emit('replace', { this.emit('replace', {
path: res._parsedRoute.fullPath, fullPath: res._parsedRoute.fullPath,
key: this.currentKey, });
}
public useListener<E extends keyof RouterEvents, L = RouterEvents[E]>(event: E, listener: L) {
this.addListener(event, listener);
onBeforeUnmount(() => {
this.removeListener(event, listener);
}); });
} }
} }
export function useScrollPositionManager(getScrollContainer: () => HTMLElement | null, router: IRouter) {
const scrollPosStore = new Map<string, number>();
onMounted(() => {
const scrollContainer = getScrollContainer();
if (scrollContainer == null) return;
scrollContainer.addEventListener('scroll', () => {
scrollPosStore.set(router.getCurrentKey(), scrollContainer.scrollTop);
}, { passive: true });
router.addListener('change', ctx => {
const scrollPos = scrollPosStore.get(ctx.key) ?? 0;
scrollContainer.scroll({ top: scrollPos, behavior: 'instant' });
if (scrollPos !== 0) {
window.setTimeout(() => { // 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール
scrollContainer.scroll({ top: scrollPos, behavior: 'instant' });
}, 100);
}
});
router.addListener('same', () => {
scrollContainer.scroll({ top: 0, behavior: 'smooth' });
});
});
}

Some files were not shown because too many files have changed in this diff Show More