Skip to content

Commit

Permalink
feat: vue i18n tab (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
webfansplz authored Jan 13, 2024
1 parent 8886a73 commit 3df27b9
Show file tree
Hide file tree
Showing 21 changed files with 394 additions and 1 deletion.
8 changes: 8 additions & 0 deletions packages/client/src/constants/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ export const builtinTab: [string, ModuleBuiltinTab[]][] = [
path: 'pinia',
title: 'Pinia',
},
{
icon: 'i-carbon-language',
name: 'i18n',
order: -100,
path: 'i18n',
title: 'I18n Resources',
},
]],
['advanced', [
{
Expand All @@ -79,6 +86,7 @@ type Detective = NonNullable<DevtoolsBridgeAppRecord['moduleDetectives']>
const moduleDetectivesMapping = {
pinia: 'pinia',
router: 'vueRouter',
i18n: 'vueI18n',
} satisfies Record<string, keyof Detective>

export function isDetected(moduleDetectives: Detective, tab: ModuleBuiltinTab) {
Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Components from '~/pages/components.vue'
import Overview from '~/pages/overview.vue'
import PiniaPage from '~/pages/pinia.vue'
import RouterPage from '~/pages/router.vue'
import I18nPage from '~/pages/i18n.vue'
import Timeline from '~/pages/timeline.vue'
import Pages from '~/pages/pages.vue'
import Assets from '~/pages/assets.vue'
Expand All @@ -40,6 +41,7 @@ const routes = [
{ path: '/components', component: Components },
{ path: '/pinia', component: PiniaPage },
{ path: '/router', component: RouterPage },
{ path: '/i18n', component: I18nPage },
{ path: '/timeline', component: Timeline },
{ path: '/pages', component: Pages },
{ path: '/assets', component: Assets },
Expand Down
92 changes: 92 additions & 0 deletions packages/client/src/pages/i18n.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<script setup lang="ts">
import { useDevToolsBridgeRpc } from '@vue/devtools-core'
// eslint-disable-next-line ts/no-import-type-side-effects
import { type InspectorNodeTag, type InspectorState } from '@vue/devtools-kit'
import { Pane, Splitpanes } from 'splitpanes'
const bridgeRpc = useDevToolsBridgeRpc()
const INSPECTOR_ID = 'vue-i18n-resource-inspector'
const selected = ref('')
const tree = ref<{ id: string, label: string, tags: InspectorNodeTag[] }[]>([])
const state = ref<{
inspectorId?: string
state?: InspectorState[]
getters?: InspectorState[]
}>({})
function getI18nState(nodeId: string) {
bridgeRpc.getInspectorState({ inspectorId: INSPECTOR_ID, nodeId }).then(({ data }) => {
state.value = data
})
}
function clearI18nState() {
state.value = {}
}
watch(selected, () => {
clearI18nState()
getI18nState(selected.value)
})
createCollapseContext('inspector-state')
onDevToolsClientConnected(() => {
bridgeRpc.getInspectorTree({ inspectorId: INSPECTOR_ID, filter: '' }).then(({ data }) => {
tree.value = data
if (!selected.value && data.length) {
selected.value = data[0].id
getI18nState(data[0].id)
}
})
bridgeRpc.on.inspectorTreeUpdated((data) => {
if (!data?.data.length)
return
tree.value = data.data
if (!selected.value && data.data.length) {
selected.value = data.data[0].id
getI18nState(data.data[0].id)
}
}, {
inspectorId: INSPECTOR_ID,
})
bridgeRpc.on.inspectorStateUpdated((data) => {
if (!data || !data.state.length)
return
state.value = data
}, {
inspectorId: INSPECTOR_ID,
})
})
onUnmounted(() => {
bridgeRpc.unhighlightElement()
})
</script>

<template>
<PanelGrids h-screen>
<Splitpanes>
<Pane flex flex-col border="r base">
<div h-screen select-none overflow-scroll p-2 class="no-scrollbar">
<InspectorTree v-model="selected" :data="tree" />
</div>
</Pane>
<Pane flex flex-col>
<div :key="selected" h-0 grow overflow-auto p-2 class="no-scrollbar">
<InspectorState
v-for="(item, key) in state" :id="key"
:key="key"
:inspector-id="INSPECTOR_ID"
:node-id="selected" :data="item" :name="`${key}`"
/>
</div>
</Pane>
</Splitpanes>
</PanelGrids>
</template>
2 changes: 2 additions & 0 deletions packages/core/src/bridge/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const bridgeRpcEvents = {
toggleApp: 'toggle-app',
isVueInspectorDetected: 'vue-inspector:detected',
enableVueInspector: 'vue-inspector:enable',
unhighlightElement: 'element:unhighlight',
} as const

export type BridgeRpcEvents = typeof bridgeRpcEvents
Expand Down Expand Up @@ -116,6 +117,7 @@ export interface BridgeRpcEventPayload {
[bridgeRpcEvents.toggleApp]: string
[bridgeRpcEvents.isVueInspectorDetected]: boolean
[bridgeRpcEvents.enableVueInspector]: null
[bridgeRpcEvents.unhighlightElement]: null
}

export class BridgeRpcCore {
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/bridge/devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export class BridgeRpc {
return devtoolsBridge.rpc.emit<{ data: { id: string } }>(bridgeRpcEvents.inspectComponentInspector)
}

static unhighlightElement() {
return devtoolsBridge.rpc.emit(bridgeRpcEvents.unhighlightElement)
}

static scrollToComponent(payload: BridgeRpcEventPayload['scroll-to-component']) {
return devtoolsBridge.rpc.emit(bridgeRpcEvents.scrollToComponent, payload)
}
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/bridge/user-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export function registerBridgeRpc(bridge: BridgeInstanceType) {
return devtools.api.inspectComponentInspector()
})

// unhighlight element
bridgeRpcCore.on(bridgeRpcEvents.unhighlightElement, () => {
devtools.api.unhighlightElement()
return Promise.resolve(JSON.stringify({}))
})

// scroll to component
bridgeRpcCore.on(bridgeRpcEvents.scrollToComponent, (payload) => {
devtools.api.scrollToComponent(payload!)
Expand Down
19 changes: 18 additions & 1 deletion packages/devtools-kit/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { addCustomCommand, removeCustomCommand } from '../core/custom-command'
import type { CustomCommand } from '../core/custom-command'

import { getVueInspector } from '../core/vue-inspector'
import { inspectComponentInspector, scrollToComponent, toggleComponentInspector } from '../core/component-inspector'
import { highlight as highlightElement, inspectComponentInspector, scrollToComponent, toggleComponentInspector, unhighlight as unhighlightElement } from '../core/component-inspector'
import { clear } from './off'
import type { DevToolsEvent } from './on'
import { DevToolsEvents, apiHooks, on } from './on'
Expand Down Expand Up @@ -178,6 +178,23 @@ export class DevToolsPluginApi {
openInEditor(payload)
}

highlightElement(instance) {
highlightElement(instance)
}

unhighlightElement() {
unhighlightElement()
}

async getComponentInstances(app) {
const appRecord = app.__VUE_DEVTOOLS_APP_RECORD__
const appId = appRecord.id.toString()
const instances = [...appRecord.instanceMap]
.filter(([key]) => key.split(':')[0] === appId)
.map(([,instance]) => instance)
return instances
}

// Vite only
getVueInspector() {
return getVueInspector()
Expand Down
2 changes: 2 additions & 0 deletions packages/devtools-kit/src/api/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ export async function registerPlugin(options: { app: VueAppInstance, api: DevToo
const globalProperties = record.app?.config?.globalProperties
if (!globalProperties)
return record

return {
...record,
moduleDetectives: {
vueRouter: !!globalProperties.$router,
pinia: !!globalProperties.$pinia,
vueI18n: !!globalProperties.$i18n,
},
}
})
Expand Down
13 changes: 13 additions & 0 deletions packages/devtools-kit/src/core/component-inspector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ export function toggleComponentInspector(options: ToggleComponentInspectorOption
}
}

export function highlight(instance: VueAppInstance) {
const bounds = getComponentBoundingRect(instance)
const name = getInstanceName(instance)
const container = getCotainerElement()
container ? update({ bounds, name }) : create({ bounds, name })
}

export function unhighlight() {
const el = getCotainerElement()
if (el)
el.style.display = 'none'
}

let inspectInstance: VueAppInstance = null!
function inspectFn(e: MouseEvent) {
const target = e.target as { __vueParentComponent?: VueAppInstance }
Expand Down
14 changes: 14 additions & 0 deletions packages/playground/locales/en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
button:
about: About
back: Back
go: GO
home: Home
toggle_dark: Toggle dark mode
toggle_langs: Change languages
intro:
desc: Opinionated Vite Starter Template
dynamic-route: Demo of dynamic route
hi: Hi, {name}!
aka: Also known as
whats-your-name: What's your name?
not-found: Not found
14 changes: 14 additions & 0 deletions packages/playground/locales/es.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
button:
about: Acerca de
back: Atrás
go: Ir
home: Inicio
toggle_dark: Alternar modo oscuro
toggle_langs: Cambiar idiomas
intro:
desc: Plantilla de Inicio de Vite Dogmática
dynamic-route: Demo de ruta dinámica
hi: ¡Hola, {name}!
aka: También conocido como
whats-your-name: ¿Cómo te llamas?
not-found: No se ha encontrado
14 changes: 14 additions & 0 deletions packages/playground/locales/fr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
button:
about: À propos
back: Retour
go: Essayer
home: Accueil
toggle_dark: Basculer en mode sombre
toggle_langs: Changer de langue
intro:
desc: Exemple d'application Vite
dynamic-route: Démo de route dynamique
hi: Salut, {name}!
aka: Aussi connu sous le nom de
whats-your-name: Comment t'appelles-tu ?
not-found: Page non trouvée
13 changes: 13 additions & 0 deletions packages/playground/locales/ja.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
button:
about: これは?
back: 戻る
go: 進む
home: ホーム
toggle_dark: ダークモード切り替え
toggle_langs: 言語切り替え
intro:
desc: 固執された Vite スターターテンプレート
dynamic-route: 動的ルートのデモ
hi: こんにちは、{name}!
whats-your-name: 君の名は。
not-found: 見つかりませんでした
14 changes: 14 additions & 0 deletions packages/playground/locales/zh-CN.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
button:
about: 关于
back: 返回
go: 确定
home: 首页
toggle_dark: 切换深色模式
toggle_langs: 切换语言
intro:
desc: 固执己见的 Vite 项目模板
dynamic-route: 动态路由演示
hi: 你好,{name}
aka: 也叫
whats-your-name: 输入你的名字
not-found: 未找到页面
2 changes: 2 additions & 0 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
"@vueuse/core": "^10.7.1",
"pinia": "^2.1.7",
"vue": "^3.4.4",
"vue-i18n": "^9.9.0",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@vitejs/plugin-vue": "^4.6.2",
"@vue/devtools": "workspace:*",
"@vue/devtools-api": "workspace:*",
Expand Down
21 changes: 21 additions & 0 deletions packages/playground/src/App.preview.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import STC from './components/ScrollToComponent.vue'
import { useAppStore } from './stores'
import { availableLocales, loadLanguageAsync } from './modules/i18n'
const age = ref(10)
const app = useAppStore()
const { t, locale } = useI18n()
async function toggleLocales() {
// change to some real logic
const locales = availableLocales
const newLocale = locales[(locales.indexOf(locale.value) + 1) % locales.length]
await loadLanguageAsync(newLocale)
locale.value = newLocale
}
</script>

<template>
<div class="container">
<RouterView />
<p>
<em text-sm opacity-75>{{ t('intro.desc') }}</em>
</p>
<p>
<a icon-btn :title="t('button.toggle_langs')" @click="toggleLocales()">
Toggle Langs
</a>
</p>
<p>
Age: {{ age }}
</p>
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Hello from './pages/Hello.vue'
import Hey from './pages/Hey.vue'
import './style.css'

import { install as installI18n } from './modules/i18n'

// connect to remote devtools
// devtools.connect('http://localhost', 8080)

Expand Down Expand Up @@ -89,6 +91,7 @@ addCustomCommand({
],
})

installI18n({ app })
app.use(router)
app.use(pinia)

Expand Down
Loading

0 comments on commit 3df27b9

Please sign in to comment.