mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Add type of component options
This commit is contained in:
parent
b7626e3cbf
commit
4ec3f68964
@ -90,7 +90,7 @@ export const loadComponent = async (component: ComponentMetadata) => {
|
||||
metadata: component,
|
||||
coreApis,
|
||||
})
|
||||
loadedComponents[component.name] = data || {}
|
||||
loadedComponents[component.name] = data as any || {}
|
||||
}
|
||||
if (component.reload && component.unload) {
|
||||
addComponentListener(component.name, async (enable: boolean) => {
|
||||
@ -175,3 +175,4 @@ export const loadAllComponents = async () => {
|
||||
}
|
||||
|
||||
export * from './types'
|
||||
export * from './define'
|
||||
|
||||
24
src/components/define.ts
Normal file
24
src/components/define.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {
|
||||
ComponentMetadata,
|
||||
OptionalOptionsMetadata,
|
||||
OptionMetadata,
|
||||
OptionsMetadata,
|
||||
UnknownOptions,
|
||||
} from './types'
|
||||
|
||||
/** 从 OptionsMetadata 中获取 Options(即 OptionsMetadata 的类型参数) */
|
||||
export type OptionsOfMeta<M extends OptionsMetadata> = (
|
||||
M extends OptionsMetadata<infer O> ? O : never
|
||||
)
|
||||
|
||||
export const defOptionMeta = <T>(
|
||||
m: OptionMetadata<T>,
|
||||
): OptionMetadata<T> => m
|
||||
|
||||
export const defOptionsMeta = <
|
||||
O extends UnknownOptions
|
||||
>(m: OptionsMetadata<O>): OptionsMetadata<O> => m
|
||||
|
||||
export const defComponentMeta = <
|
||||
Om extends OptionalOptionsMetadata
|
||||
>(m: ComponentMetadata<Om>): ComponentMetadata<Om> => m
|
||||
@ -15,6 +15,7 @@ export type Author = {
|
||||
name: string
|
||||
link: string
|
||||
}
|
||||
|
||||
export interface FeatureBase {
|
||||
// TODO: 可在编译时转换 Markdown 以提高运行时性能
|
||||
/** 描述 (支持 markdown), 可以设置为对象提供多语言的描述 (`key: 语言代码`) */
|
||||
@ -26,9 +27,11 @@ export interface FeatureBase {
|
||||
/** 编译时的 core version, 由 Babel 注入, 不需要手动填写 */
|
||||
coreVersion?: string
|
||||
}
|
||||
|
||||
type Optional<Target, Props extends keyof Target> = {
|
||||
[P in Props]?: Target[P]
|
||||
} & Omit<Target, Props>
|
||||
|
||||
/** 组件标签 */
|
||||
export interface ComponentTag {
|
||||
/** 标签的名称 */
|
||||
@ -42,13 +45,18 @@ export interface ComponentTag {
|
||||
/** 设置面板中的呈现顺序 */
|
||||
order: number
|
||||
}
|
||||
|
||||
type ComponentOptionValidator<T> = (value: T, oldValue: T) => T | undefined | null
|
||||
/** 组件选项信息
|
||||
* @todo 需要 extends 出更具体的 Option 类型, 现在这样混一起太乱
|
||||
*/
|
||||
export interface ComponentOption {
|
||||
|
||||
// TODO: 参考 discussion #3041。当不兼容代码替换完成后将 any 改为 unknown
|
||||
export type UnknownOptions = Record<string, any>
|
||||
|
||||
export type EmptyOptions = Record<string, never>
|
||||
|
||||
/** 单个选项的信息 */
|
||||
export interface OptionMetadata<V = unknown> {
|
||||
/** 默认值 */
|
||||
defaultValue: unknown
|
||||
defaultValue: V
|
||||
/** 显示名称 */
|
||||
displayName?: string
|
||||
/** 如果希望这个选项显示为一个下拉框, 可以用相应的 `enum` 提供下拉框的选值, 或者也可以传入 `string[]` */
|
||||
@ -67,10 +75,15 @@ export interface ComponentOption {
|
||||
validator?: ComponentOptionValidator<Range<string>> |
|
||||
ComponentOptionValidator<string> | ComponentOptionValidator<number>
|
||||
}
|
||||
/** 组件选项信息 */
|
||||
export interface ComponentOptions {
|
||||
[key: string]: ComponentOption
|
||||
|
||||
/** 多个选项的信息 */
|
||||
export type OptionsMetadata<O extends UnknownOptions = UnknownOptions> = {
|
||||
[OptionName in keyof O]: OptionMetadata<O[OptionName]>
|
||||
}
|
||||
|
||||
// TODO: 参考 discussion #3041。当不兼容代码替换完成后删除
|
||||
export type ComponentOptions = OptionsMetadata
|
||||
|
||||
/** 组件标签 */
|
||||
export const componentsTags = {
|
||||
/** 视频 */
|
||||
@ -139,21 +152,42 @@ export const componentsTags = {
|
||||
order: 8,
|
||||
} as ComponentTag,
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 OptionalOptionsMetadata 中获取 Options 信息。
|
||||
* 当其为 undefined 时,返回类型 EmptyOptions。
|
||||
*/
|
||||
export type OptionsFromOptionalMeta<M extends OptionalOptionsMetadata> = (
|
||||
M extends undefined | Record<string, never> ? EmptyOptions :
|
||||
M extends OptionsMetadata<infer O> ? O : never
|
||||
)
|
||||
|
||||
/** 组件入口函数的参数 */
|
||||
export interface ComponentEntryContext<
|
||||
Om extends OptionalOptionsMetadata = OptionalOptionsMetadata
|
||||
> {
|
||||
/** 当前组件的设置 */
|
||||
settings: ComponentSettings<OptionsFromOptionalMeta<Om>>
|
||||
/** 当前组件的信息 */
|
||||
metadata: ComponentMetadata<Om>
|
||||
/** 核心 API */
|
||||
coreApis: CoreApis
|
||||
}
|
||||
|
||||
/** 组件入口函数 */
|
||||
export type ComponentEntry<T = unknown> = (
|
||||
context: {
|
||||
/** 当前组件的设置 */
|
||||
settings: ComponentSettings
|
||||
/** 当前组件的信息 */
|
||||
metadata: ComponentMetadata
|
||||
/** 核心 API */
|
||||
coreApis: CoreApis
|
||||
}
|
||||
export type ComponentEntry<
|
||||
Om extends OptionalOptionsMetadata = OptionalOptionsMetadata,
|
||||
T = unknown
|
||||
> = (
|
||||
context: ComponentEntryContext<Om>
|
||||
) => T | Promise<T>
|
||||
|
||||
/** 带有函数/复杂对象的组件信息 */
|
||||
export interface FunctionalMetadata {
|
||||
export interface FunctionalMetadata<
|
||||
Om extends OptionalOptionsMetadata = OptionalOptionsMetadata
|
||||
> {
|
||||
/** 主入口, 重新开启时不会再运行 */
|
||||
entry: ComponentEntry
|
||||
entry: ComponentEntry<Om>
|
||||
/** 导出小组件 */
|
||||
widget?: Omit<Widget, 'name'>
|
||||
/** 首屏样式, 会尽快注入 (before DCL) */
|
||||
@ -178,8 +212,16 @@ export interface FunctionalMetadata {
|
||||
/** 设置不匹配的URL, 不匹配则不运行此组件, 优先级高于`urlInclude` */
|
||||
urlExclude?: TestPattern
|
||||
}
|
||||
|
||||
/** ComponentMetadata 的类型参数,用于表达其可选参数 options 的类型 */
|
||||
export type OptionalOptionsMetadata<O extends UnknownOptions = UnknownOptions> = (
|
||||
OptionsMetadata<O> | undefined
|
||||
)
|
||||
|
||||
/** 组件基本信息 */
|
||||
export interface ComponentMetadata extends FunctionalMetadata, FeatureBase {
|
||||
export interface ComponentMetadata<
|
||||
Om extends OptionalOptionsMetadata = OptionalOptionsMetadata
|
||||
> extends FeatureBase, FunctionalMetadata<Om> {
|
||||
/** 组件名称 */
|
||||
name: string
|
||||
/** 显示名称 */
|
||||
@ -193,9 +235,10 @@ export interface ComponentMetadata extends FunctionalMetadata, FeatureBase {
|
||||
/** 是否在设置界面中隐藏 (代码仍可操作) */
|
||||
hidden?: boolean
|
||||
/** 组件子选项 */
|
||||
options?: ComponentOptions
|
||||
options?: Om
|
||||
/** i18n 数据 */
|
||||
i18n?: Record<string, LanguagePack>
|
||||
}
|
||||
|
||||
/** 用户组件的非函数基本信息, 用于直接保存为 JSON */
|
||||
export type UserComponentMetadata = Omit<ComponentMetadata, keyof FunctionalMetadata>
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { ComponentMetadata, componentsMap, ComponentOptions } from '@/components/component'
|
||||
import {
|
||||
ComponentMetadata,
|
||||
componentsMap,
|
||||
OptionalOptionsMetadata,
|
||||
OptionsFromOptionalMeta,
|
||||
OptionsMetadata,
|
||||
UnknownOptions,
|
||||
} from '@/components/component'
|
||||
import { PluginMetadata } from '@/plugins/plugin'
|
||||
// import serialize from 'serialize-javascript'
|
||||
import { settings, ComponentSettings } from '../settings'
|
||||
@ -8,19 +15,26 @@ import { matchUrlPattern } from '../utils'
|
||||
* 生成组件选项设置
|
||||
* @param options 组件选项定义
|
||||
*/
|
||||
export const componentOptionsToSettings = (options: ComponentOptions) => (
|
||||
lodash.fromPairs(Object.entries(options).map(([key, value]) => [key, value.defaultValue]))
|
||||
)
|
||||
export const metadataToOptions = <O extends UnknownOptions>(
|
||||
options: OptionsMetadata<O>,
|
||||
): O => lodash.mapValues(options, m => m.defaultValue) as O
|
||||
|
||||
/**
|
||||
* 生成组件设置
|
||||
* @param component 组件定义
|
||||
*/
|
||||
export const componentToSettings = (component: ComponentMetadata): ComponentSettings => (
|
||||
{
|
||||
export const componentToSettings = <
|
||||
Om extends OptionalOptionsMetadata
|
||||
>(component: ComponentMetadata<Om>): (
|
||||
ComponentSettings<OptionsFromOptionalMeta<Om>>
|
||||
) => {
|
||||
const { options: meta } = component
|
||||
return {
|
||||
enabled: component.enabledByDefault ?? true,
|
||||
options: component.options ? componentOptionsToSettings(component.options) : {},
|
||||
options: (meta ? metadataToOptions(meta as any) : {}) as any,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为自定义组件
|
||||
* @param component 组件定义
|
||||
@ -29,6 +43,7 @@ export const isUserComponent = (component: ComponentMetadata | string) => {
|
||||
const name = typeof component === 'string' ? component : component.name
|
||||
return Boolean(settings.userComponents[name])
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为自定义插件
|
||||
* @param plugin 插件定义
|
||||
@ -45,27 +60,42 @@ const emptySettings: ComponentSettings = {
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
// TODO: 参考 discussion #3041。
|
||||
// 当不兼容代码替换完成后将 R 的默认类型替换为 UnknownOptions
|
||||
/**
|
||||
* 获取组件的设置
|
||||
* 获取已加载组件的设置
|
||||
*
|
||||
* 若组件未安装,则返回一个默认的 ComponentSettings 对象:
|
||||
* {
|
||||
* enabled: false,
|
||||
* options new Proxy({}, { get: () => false })
|
||||
* }
|
||||
*
|
||||
* @param component 组件或组件名称
|
||||
*/
|
||||
export const getComponentSettings = (component: ComponentMetadata | string): ComponentSettings => {
|
||||
export const getComponentSettings = <R extends UnknownOptions = UnknownOptions>(
|
||||
component: ComponentMetadata | string,
|
||||
): ComponentSettings<R> => {
|
||||
let componentMetadata: ComponentMetadata
|
||||
if (typeof component === 'string') {
|
||||
const componentMetadata = componentsMap[component]
|
||||
if (componentMetadata === undefined) {
|
||||
if (componentsMap[component] === undefined) {
|
||||
if (settings.components.settingsPanel.options.devMode) {
|
||||
console.warn('No settings found for component:', component)
|
||||
}
|
||||
return emptySettings
|
||||
return emptySettings as any
|
||||
}
|
||||
component = componentMetadata
|
||||
componentMetadata = componentsMap[component]
|
||||
} else {
|
||||
componentMetadata = component
|
||||
}
|
||||
if (isUserComponent(component)) {
|
||||
const { name } = component
|
||||
return settings.userComponents[name]?.settings ?? emptySettings
|
||||
if (isUserComponent(componentMetadata)) {
|
||||
const { name } = componentMetadata
|
||||
return (settings.userComponents[name]?.settings ?? emptySettings) as any
|
||||
}
|
||||
return settings.components[component.name]
|
||||
return settings.components[componentMetadata.name] as any
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通用设置 (`settingsPanel`组件的`options`)
|
||||
*/
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { components, UserComponentMetadata } from '@/components/component'
|
||||
import {
|
||||
components,
|
||||
UnknownOptions,
|
||||
UserComponentMetadata,
|
||||
} from '@/components/component'
|
||||
import { UserStyle } from '@/plugins/style'
|
||||
import { PluginMetadata, plugins } from '@/plugins/plugin'
|
||||
import { componentToSettings, isUserComponent } from './helpers'
|
||||
|
||||
type Property = string | number | symbol
|
||||
/** 表示一个组件的设置 */
|
||||
export interface ComponentSettings<O = { [key: string]: any }> {
|
||||
export interface ComponentSettings<O extends UnknownOptions = UnknownOptions> {
|
||||
/** 是否启用此组件 */
|
||||
enabled: boolean
|
||||
/** 组件选项 */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user