Perfect the type of switch options

This commit is contained in:
timongh 2022-10-02 03:07:43 +08:00
parent 982491557d
commit 3f9cf020a0
3 changed files with 364 additions and 94 deletions

View File

@ -1,12 +1,16 @@
import { createSwitchOptions, SwitchOptions } from '@/components/switch-options'
import { ComponentMetadata } from '@/components/types'
import {
newSwitchComponentBuilder,
defineSwitchMetadata,
defineIncompleteSwitchComponentMetadata,
} from '@/components/switch-options'
import { addComponentListener, getComponentSettings } from '@/core/settings'
import { sq } from '@/core/spin-query'
import { addStyle } from '@/core/style'
import { getCookieValue } from '@/core/utils'
import { mainSiteUrls } from '@/core/utils/urls'
const switchOptions: SwitchOptions = {
const switchMetadata = defineSwitchMetadata({
name: 'simplifyOptions',
dimAt: 'checked',
switchProps: {
@ -43,8 +47,9 @@ const switchOptions: SwitchOptions = {
displayName: '右侧分区导航(旧)',
},
},
}
const metadata: ComponentMetadata = {
})
const metadata = defineIncompleteSwitchComponentMetadata({
name: 'simplifyHome',
displayName: '简化首页',
description: {
@ -81,13 +86,17 @@ const metadata: ComponentMetadata = {
() => dqa('.proxy-box > div'),
elements => elements.length > 0 || isNotHome,
)
return Object.fromEntries(categoryElements.map(it => ([
it.id.replace(/^bili_/, ''),
{
displayName: it.querySelector('header .name')?.textContent?.trim() ?? '未知分区',
defaultValue: false,
},
])))
return Object.fromEntries(
categoryElements.map(it => [
it.id.replace(/^bili_/, ''),
{
displayName:
it.querySelector('header .name')?.textContent?.trim()
?? '未知分区',
defaultValue: false,
},
]),
)
}
const skipIds = ['推广']
@ -123,39 +132,47 @@ const metadata: ComponentMetadata = {
}
return null
})
.filter((it): it is [string, SimplifyHomeOption] => it !== null) ?? []
.filter((it): it is [string, SimplifyHomeOption] => it !== null)
?? []
return Object.fromEntries(entries)
})()
const generatedSwitches: Record<string, unknown> = {}
Object.entries(generatedOptions).forEach(([key, { displayName, defaultValue }]) => {
const option = {
defaultValue,
displayName,
}
const optionKey = `switch-${key}`
if (options[optionKey] === undefined) {
options[optionKey] = defaultValue
}
const switchKey = `switch-${key}`
addComponentListener(
`${metadata.name}.${switchKey}`,
(value: boolean) => {
document.body.classList.toggle(`${metadata.name}-${switchKey}`, value)
},
true,
)
switchOptions.switches[key] = option
generatedSwitches[key] = option
})
options.simplifyOptions.switches = generatedSwitches
const generatedStyles = Object.keys(generatedOptions).map(name => `
Object.entries(generatedOptions).forEach(
([key, { displayName, defaultValue }]) => {
const option = {
defaultValue,
displayName,
}
const optionKey = `switch-${key}`
if (options[optionKey] === undefined) {
options[optionKey] = defaultValue
}
const switchKey = `switch-${key}`
addComponentListener(
`${metadata.name}.${switchKey}`,
(value: boolean) => {
document.body.classList.toggle(
`${metadata.name}-${switchKey}`,
value,
)
},
true,
)
switchMetadata.switches[key] = option
generatedSwitches[key] = option
},
);
(options.simplifyOptions as any).switches = generatedSwitches
const generatedStyles = Object.keys(generatedOptions)
.map(name => `
body.simplifyHome-switch-${name} .bili-layout .bili-grid[data-area="${name}"],
body.simplifyHome-switch-${name} .storey-box .proxy-box #bili_${name} {
display: none !important;
}
`.trim()).join('\n')
`.trim())
.join('\n')
addStyle(generatedStyles, 'simplify-home-generated')
},
}
})
export const component = createSwitchOptions(switchOptions)(metadata)
export const component = newSwitchComponentBuilder(switchMetadata)(metadata)

View File

@ -1,8 +1,8 @@
import { createSwitchOptions } from '@/components/switch-options'
import { newSwitchComponentBuilder } from '@/components/switch-options'
import { styledComponentEntry } from '@/components/styled-component'
import { liveUrls } from '@/core/utils/urls'
export const component = createSwitchOptions({
export const component = newSwitchComponentBuilder({
name: 'simplifyOptions',
dimAt: 'checked',
switchProps: {

View File

@ -1,71 +1,324 @@
import { getComponentSettings, addComponentListener } from '@/core/settings'
import { ComponentMetadata, ComponentOptions } from './component'
/**
* Switch Options
* @module components/switch-options
*
* ComponentMetadata
* Widget Widget
*
* {@link newSwitchComponentBuilder}
*/
type Switches = {
[key: string]: {
displayName: string
defaultValue: boolean
}
import { getComponentSettings, addComponentListener } from '@/core/settings'
import {
ComponentEntry,
ComponentMetadata,
OptionsMetadata,
OptionsOfMetadata,
UnknownOptions,
} from './component'
import { Widget } from './widget'
/**
*
*/
export interface SwitchItemMetadata {
displayName: string
defaultValue: boolean
}
export interface SwitchOptions {
name: string
switches: Switches
radio?: boolean
dimAt?: 'checked' | 'notChecked'
/**
* options
*/
export type SwitchItemsMetadata<S extends string> = {
[key in S]: SwitchItemMetadata
}
/**
* SwitchItemsMetadata SwitchItem
*/
export type SwitchItemNamesOfItemsMetadata<C extends SwitchItemsMetadata<string>> = keyof C
/**
* SwitchItemsMetadata
*/
export const defineSwitchItemsMetadata = <S extends string>(
c: SwitchItemsMetadata<S>,
): SwitchItemsMetadata<S> => c
/**
* API
*
* {@link SwitchItemsMetadata} {@link defineSwitchItemsMetadata}
*/
export interface SwitchMetadata<N extends string, S extends string> {
// 注入 options 时会使用
name: N
// 每个开关的单独配置,其键名会被用于生成注入 options 的名称,见 {@link SwitchItemNames}
switches: SwitchItemsMetadata<S>
// 是否单选 default: false
radio?: undefined | boolean
// default: 'checked'
dimAt?: undefined | 'checked' | 'notChecked'
// 配置每个开关的图标
switchProps?: {
checkedIcon?: string
notCheckedIcon?: string
iconPosition?: 'left' | 'right'
}
}
export const createSwitchOptions = (options: SwitchOptions) => {
if (options.radio === undefined) {
options.radio = false
/**
* SwitchMetadata name
*/
export type SwitchNameOfMetadata<C extends SwitchMetadata<string, string>> = C['name']
/**
* SwitchMetadata SwitchItem
*/
export type SwitchItemNamesOfMetadata<C extends SwitchMetadata<string, string>> =
keyof C['switches']
/**
* {@link SwitchMetadata}
*/
export const defineSwitchMetadata = <N extends string, S extends string>(
c: SwitchMetadata<N, S>,
): SwitchMetadata<N, S> => c
/**
* Widget options
*/
export interface SwitchWidgetOptions<N extends string, S extends string> {
name: N
switches: SwitchItemsMetadata<S>
radio: boolean
dimAt: 'checked' | 'notChecked'
switchProps?: {
checkedIcon?: string
notCheckedIcon?: string
iconPosition?: 'left' | 'right'
}
const { name: optionName, switches } = options
const extendComponentOptions: ComponentOptions = {}
Object.entries(switches).forEach(([key, { displayName, defaultValue }]) => {
extendComponentOptions[`switch-${key}`] = {
componentName: string
optionDisplayName: string
}
/**
* SwitchWidgetOptions
*/
const newSwitchWidgetOptions = <N extends string, S extends string>(
metadata: SwitchMetadata<N, S>,
componentName: string,
optionDisplayName,
) => ({
...metadata,
radio: metadata.radio === undefined ? false : metadata.radio,
dimAt: metadata.dimAt === undefined ? 'checked' : metadata.dimAt,
componentName,
optionDisplayName,
})
/**
* options SwitchItemsMetadata 'switch-'
*/
export type SwitchItemNames<S extends string> = `switch-${S}`
/**
* API options
*/
export type SwitchItemOptions<S extends string> = Record<SwitchItemNames<S>, string>
/**
* Options API
*/
export type SwitchOptions<O extends UnknownOptions, N extends string, S extends string> = O & {
N: SwitchWidgetOptions<N, S>
} & SwitchItemOptions<S>
/**
* SwitchOptions
*/
export type SwitchOptionsOfSwitchMetadata<
O extends UnknownOptions,
C extends SwitchMetadata<string, string>,
> = C extends SwitchMetadata<infer N, infer S> ? SwitchOptions<O, N, S> : never
/**
* SwitchOptions
*/
export type SwitchOptionsOfMetadata<
M extends OptionsMetadata,
C extends SwitchMetadata<string, string>,
> = SwitchOptionsOfSwitchMetadata<OptionsOfMetadata<M>, C>
/**
* OptionsMetadata API
*/
export type SwitchOptionsMetadata<
O extends UnknownOptions,
N extends string,
S extends string,
> = OptionsMetadata<SwitchOptions<O, N, S>>
/**
* extender
*
* extender OptionsMetadata
*
* OptionsMetadata
*/
const newSwitchOptionsMetadataExtender = <S extends string>(
itemsMetadata: SwitchItemsMetadata<S>,
): (<O extends UnknownOptions, N extends string>(
options: OptionsMetadata<O>,
widgetOptions: SwitchWidgetOptions<N, S>,
) => SwitchOptionsMetadata<O, N, S>) => {
const optionsToExtend = {}
const entries = Object.entries<SwitchItemMetadata>(itemsMetadata)
for (const [key, { displayName, defaultValue }] of entries) {
optionsToExtend[`switch-${key}`] = {
defaultValue,
displayName,
hidden: true,
}
})
return (component: ComponentMetadata) => {
const optionDisplayName = `${component.displayName}选项`
const selfOption = {
componentName: component.name,
optionDisplayName,
}
Object.assign(options, selfOption)
extendComponentOptions[optionName] = {
}
return <O extends UnknownOptions, N extends string>(
options: OptionsMetadata<O>,
widgetOptions: SwitchWidgetOptions<N, S>,
) => {
optionsToExtend[widgetOptions.name as string] = {
defaultValue: options,
displayName: optionDisplayName,
displayName: widgetOptions.optionDisplayName,
}
component.options = { ...component.options, ...extendComponentOptions }
if (!component.widget) {
component.widget = {
component: () => import('./SwitchOptions.vue').then(m => m.default),
options,
}
}
const originalEntry = component.entry
component.entry = async (...args) => {
originalEntry?.(...args)
const { name } = component
const componentOptions = getComponentSettings(name).options
Object.keys(componentOptions).forEach(key => {
if (key.startsWith('switch-')) {
addComponentListener(
`${name}.${key}`,
(value: boolean) => {
document.body.classList.toggle(`${name}-${key}`, value)
},
true,
)
}
})
}
return component
Object.assign(options, optionsToExtend)
return options as SwitchOptionsMetadata<O, N, S>
}
}
/**
* Widget
*/
const newWidget = <N extends string, S extends string>(
options: SwitchWidgetOptions<N, S>,
): Omit<Widget, 'name'> => ({
component: () => import('./SwitchOptions.vue').then(m => m.default),
options,
})
/**
* entry API `options`
*/
export type SwitchEntry<
O extends UnknownOptions,
N extends string,
S extends string,
> = ComponentEntry<SwitchOptions<O, N, S>>
/**
* `ComponentMetadata`
*
* 使 {@link defineIncompleteSwitchComponentMetadata}
*
* API `options`
* `entry`
*/
export type IncompleteSwitchComponentMetadata<
O extends UnknownOptions,
N extends string,
S extends string,
> = {
[K in keyof ComponentMetadata<O>]: K extends 'entry'
? SwitchEntry<O, N, S>
: ComponentMetadata<O>[K]
}
/**
* {@link IncompleteSwitchComponentMetadata}
* {@link ./define.ts defineComponentMetadata}
*/
export const defineIncompleteSwitchComponentMetadata = <
O extends UnknownOptions,
N extends string,
S extends string,
>(
m: IncompleteSwitchComponentMetadata<O, N, S>,
): IncompleteSwitchComponentMetadata<O, N, S> => m
/**
* entry
*/
const newSwitchEntry = <O extends UnknownOptions, N extends string, S extends string>(
component: ComponentMetadata<O> | IncompleteSwitchComponentMetadata<O, N, S>,
): SwitchEntry<O, N, S> => (...args) => {
const result = component.entry(...args)
const componentOptions = getComponentSettings(component.name).options
Object.keys(componentOptions).forEach(key => {
if (key.startsWith('switch-')) {
addComponentListener(
`${component.name}.${key}`,
(value: boolean) => {
document.body.classList.toggle(`${component.name}-${key}`, value)
},
true,
)
}
})
return result
}
/**
* `ComponentMetadata`
*/
export type SwitchComponentMetadata<
O extends UnknownOptions,
N extends string,
S extends string,
> = ComponentMetadata<SwitchOptions<O, N, S>>
/**
* builder
*
* builder `ComponentMetadata` `options`
* Widget Widget
*
* {@link SwitchMetadata} 使 {@link defineSwitchMetadata}
*
* options {@link SwitchOptions}
* Widget {@link SwitchWidgetOptions}
*
* `ComponentMetadata` `options`
* 使 {@link defineIncompleteSwitchComponentMetadata}
*
* builder `entry`
* `entry`
*
* `ComponentMetadata`
*
*/
export const newSwitchComponentBuilder = <N extends string, S extends string>(
metadata: SwitchMetadata<N, S>,
): (<O extends UnknownOptions>(
component: ComponentMetadata<O> | IncompleteSwitchComponentMetadata<O, N, S>,
) => SwitchComponentMetadata<O, N, S>) => {
const extendOptions = newSwitchOptionsMetadataExtender(metadata.switches)
// 返回的 builder
return <O extends UnknownOptions>(
component: ComponentMetadata<O> | IncompleteSwitchComponentMetadata<O, N, S>,
) => {
const widgetOptions = newSwitchWidgetOptions(
metadata,
component.name,
`${component.displayName}选项`,
)
// 若没有 Widget则注入一个
if (!component.widget) {
component.widget = newWidget(widgetOptions)
}
// 扩展组件 options
extendOptions(component.options, widgetOptions)
// 包装 entry 函数
component.entry = newSwitchEntry(component)
return component as SwitchComponentMetadata<O, N, S>
}
}