68 lines
3.0 KiB
TypeScript
68 lines
3.0 KiB
TypeScript
import { h, render } from 'vue'
|
||
// 启动台应用 — 从 js/apps3.js 手动转写
|
||
import { el, iconImg } from '../../utils'
|
||
import { Apps, stdMenus } from '../../composables/useApps'
|
||
import LaunchpadComponent from './Launchpad.vue'
|
||
import { ui as UI } from '../../composables/useUI'
|
||
|
||
let _AppStoreApp: any = null
|
||
export function setAppStoreForLaunchpad(a: any) { _AppStoreApp = a }
|
||
|
||
export const Launchpad = {
|
||
overlay: null as HTMLElement | null,
|
||
show() {
|
||
if (this.overlay) return this.hide()
|
||
const ov = el('div', { class: 'launchpad', tabindex: '0' })
|
||
const search = el('input', { class: 'lp-search', type: 'search', placeholder: '搜索 App' }) as HTMLInputElement
|
||
const grid = el('div', { class: 'lp-grid' })
|
||
ov.append(el('div', { class: 'lp-search-wrap' }, search), grid)
|
||
const renderGrid = (q: string) => {
|
||
grid.innerHTML = ''
|
||
const apps = Object.values(Apps.registry)
|
||
.filter((a: any) => a.id !== 'launchpad' && (!a.storeApp || (_AppStoreApp && _AppStoreApp.isInstalled(a.id))))
|
||
.filter((a: any) => !q || a.name.toLowerCase().includes(q.toLowerCase()))
|
||
if (!apps.length) grid.append(el('div', { class: 'lp-empty', text: '没有匹配的 App' }))
|
||
;(apps as any[]).sort((a: any, b: any) => a.name.localeCompare(b.name, 'zh-Hans-CN')).forEach((app: any) => {
|
||
const c = el('div', { class: 'lp-cell' }, iconImg(app.icon, '', app.name), el('div', { class: 'lp-name', text: app.name }))
|
||
c.addEventListener('click', () => { this.hide(); Apps.open(app.id) })
|
||
grid.append(c)
|
||
})
|
||
}
|
||
renderGrid('')
|
||
search.addEventListener('input', () => renderGrid(search.value.trim()))
|
||
search.addEventListener('keydown', (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === 'Escape') this.hide() })
|
||
ov.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key === 'Escape') this.hide() })
|
||
ov.addEventListener('click', (e: Event) => { if (e.target === ov) this.hide() })
|
||
document.body.append(ov)
|
||
this.overlay = ov
|
||
setTimeout(() => { ov.classList.add('show'); search.focus() }, 10)
|
||
},
|
||
hide() {
|
||
if (!this.overlay) return
|
||
const ov = this.overlay; this.overlay = null
|
||
ov.classList.remove('show'); setTimeout(() => ov.remove(), 250)
|
||
}
|
||
};
|
||
(window as any).Launchpad = Launchpad
|
||
|
||
// 注册启动台应用
|
||
Apps.register({
|
||
id: 'launchpad', name: '启动台', icon: '/assets/icons/launchpad.png',
|
||
menus() { return stdMenus(this) },
|
||
render() { /* 不走 WM 窗口 */ },
|
||
open() { Launchpad.show() }
|
||
})
|
||
|
||
// 扩展 Apps.open:拦截 launchpad/stickies/storeApp
|
||
const _origOpen = Apps.open.bind(Apps)
|
||
Apps.open = function (id: string, args?: any) {
|
||
if (id === 'launchpad') { Launchpad.show(); return null }
|
||
if (id === 'stickies' && !(args && args.noteId)) { (Apps.get('stickies') as any)?.openAll(); return null }
|
||
const app = this.get(id)
|
||
if (app && app.storeApp && !(_AppStoreApp && _AppStoreApp.isInstalled(id))) {
|
||
UI.alert('尚未安装', `请先在 App Store 中获取"${app.name}"。`, '/assets/icons/appstore.png')
|
||
return null
|
||
}
|
||
return _origOpen(id, args)
|
||
}
|