mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge pull request #3715 from timongh/tools-to-define-api
define API 收尾工作完成
This commit is contained in:
commit
df95d9dfe7
@ -1,8 +1,8 @@
|
|||||||
import { ComponentMetadata } from '@/components/types'
|
import { defineComponentMetadata } from '@/components/define'
|
||||||
import { LifeCycleEventTypes } from '@/core/life-cycle'
|
import { LifeCycleEventTypes } from '@/core/life-cycle'
|
||||||
import { darkExcludes } from '../dark-urls'
|
import { darkExcludes } from '../dark-urls'
|
||||||
|
|
||||||
export const component: ComponentMetadata = {
|
export const component = defineComponentMetadata({
|
||||||
name: 'darkModeFollowSystem',
|
name: 'darkModeFollowSystem',
|
||||||
displayName: '夜间模式跟随系统',
|
displayName: '夜间模式跟随系统',
|
||||||
entry: () => {
|
entry: () => {
|
||||||
@ -33,4 +33,4 @@ export const component: ComponentMetadata = {
|
|||||||
> 注:在某些浏览器 (如 \`Microsoft Edge\`) 中,夜间模式仅会同步浏览器的亮 / 暗主题.
|
> 注:在某些浏览器 (如 \`Microsoft Edge\`) 中,夜间模式仅会同步浏览器的亮 / 暗主题.
|
||||||
`.trim(),
|
`.trim(),
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ComponentMetadata } from '@/components/types'
|
import { defineComponentMetadata, defineOptionsMetadata, OptionsOfMetadata } from '@/components/define'
|
||||||
import { fullyLoaded } from '@/core/life-cycle'
|
import { fullyLoaded } from '@/core/life-cycle'
|
||||||
import { ComponentSettings, getComponentSettings } from '@/core/settings'
|
import { ComponentSettings, getComponentSettings } from '@/core/settings'
|
||||||
import { Range } from '@/ui/range'
|
import { Range } from '@/ui/range'
|
||||||
@ -91,7 +91,33 @@ class ScheduleTime {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const checkTime = (settings: ComponentSettings) => {
|
|
||||||
|
const options = defineOptionsMetadata({
|
||||||
|
range: {
|
||||||
|
defaultValue: {
|
||||||
|
start: '18:00',
|
||||||
|
end: '6:00',
|
||||||
|
},
|
||||||
|
displayName: '时间段',
|
||||||
|
validator: (range: Range<string>) => {
|
||||||
|
const { start, end } = range
|
||||||
|
const regex = /^(\d{1,2}):(\d{1,2})$/
|
||||||
|
if (!regex.test(start) || !regex.test(end)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const startTime = new ScheduleTime(range.start)
|
||||||
|
const endTime = new ScheduleTime(range.end)
|
||||||
|
return {
|
||||||
|
start: startTime.toString(),
|
||||||
|
end: endTime.toString(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
type Options = OptionsOfMetadata<typeof options>
|
||||||
|
|
||||||
|
const checkTime = (settings: ComponentSettings<Options>) => {
|
||||||
const start = new ScheduleTime(settings.options.range.start)
|
const start = new ScheduleTime(settings.options.range.start)
|
||||||
const end = new ScheduleTime(settings.options.range.end)
|
const end = new ScheduleTime(settings.options.range.end)
|
||||||
const now = new ScheduleTime()
|
const now = new ScheduleTime()
|
||||||
@ -110,7 +136,8 @@ const checkTime = (settings: ComponentSettings) => {
|
|||||||
setTimeout(() => checkTime(settings), timeout)
|
setTimeout(() => checkTime(settings), timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const component: ComponentMetadata = {
|
|
||||||
|
export const component = defineComponentMetadata({
|
||||||
name: 'darkModeSchedule',
|
name: 'darkModeSchedule',
|
||||||
displayName: '夜间模式计划时段',
|
displayName: '夜间模式计划时段',
|
||||||
description: '设置一个使用夜间模式的时间段, 进入 / 离开此时间段时, 会自动开启 / 关闭夜间模式. 结束时间小于起始时间时将视为次日, 如 `18:00` 至 `6:00` 表示晚上 18:00 到次日 6:00. 请勿和 \`夜间模式跟随系统\` 一同使用.',
|
description: '设置一个使用夜间模式的时间段, 进入 / 离开此时间段时, 会自动开启 / 关闭夜间模式. 结束时间小于起始时间时将视为次日, 如 `18:00` 至 `6:00` 表示晚上 18:00 到次日 6:00. 请勿和 \`夜间模式跟随系统\` 一同使用.',
|
||||||
@ -120,26 +147,5 @@ export const component: ComponentMetadata = {
|
|||||||
],
|
],
|
||||||
entry: ({ settings }) => fullyLoaded(() => checkTime(settings)),
|
entry: ({ settings }) => fullyLoaded(() => checkTime(settings)),
|
||||||
urlExclude: darkExcludes,
|
urlExclude: darkExcludes,
|
||||||
options: {
|
options,
|
||||||
range: {
|
})
|
||||||
defaultValue: {
|
|
||||||
start: '18:00',
|
|
||||||
end: '6:00',
|
|
||||||
},
|
|
||||||
displayName: '时间段',
|
|
||||||
validator: (range: Range<string>) => {
|
|
||||||
const { start, end } = range
|
|
||||||
const regex = /^(\d{1,2}):(\d{1,2})$/
|
|
||||||
if (!regex.test(start) || !regex.test(end)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const startTime = new ScheduleTime(range.start)
|
|
||||||
const endTime = new ScheduleTime(range.end)
|
|
||||||
return {
|
|
||||||
start: startTime.toString(),
|
|
||||||
end: endTime.toString(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { createSwitchOptions, SwitchOptions } from '@/components/switch-options'
|
import {
|
||||||
import { ComponentMetadata } from '@/components/types'
|
newSwitchComponentWrapper,
|
||||||
|
defineSwitchMetadata,
|
||||||
|
defineIncompleteSwitchComponentMetadata,
|
||||||
|
} from '@/components/switch-options'
|
||||||
|
|
||||||
import { addComponentListener, getComponentSettings } from '@/core/settings'
|
import { addComponentListener, getComponentSettings } from '@/core/settings'
|
||||||
import { sq } from '@/core/spin-query'
|
import { sq } from '@/core/spin-query'
|
||||||
import { addStyle } from '@/core/style'
|
import { addStyle } from '@/core/style'
|
||||||
import { getCookieValue } from '@/core/utils'
|
import { getCookieValue } from '@/core/utils'
|
||||||
import { mainSiteUrls } from '@/core/utils/urls'
|
import { mainSiteUrls } from '@/core/utils/urls'
|
||||||
|
|
||||||
const switchOptions: SwitchOptions = {
|
const switchMetadata = defineSwitchMetadata({
|
||||||
name: 'simplifyOptions',
|
name: 'simplifyOptions',
|
||||||
dimAt: 'checked',
|
dimAt: 'checked',
|
||||||
switchProps: {
|
switchProps: {
|
||||||
@ -43,8 +47,9 @@ const switchOptions: SwitchOptions = {
|
|||||||
displayName: '右侧分区导航(旧)',
|
displayName: '右侧分区导航(旧)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
const metadata: ComponentMetadata = {
|
|
||||||
|
const metadata = defineIncompleteSwitchComponentMetadata({
|
||||||
name: 'simplifyHome',
|
name: 'simplifyHome',
|
||||||
displayName: '简化首页',
|
displayName: '简化首页',
|
||||||
description: {
|
description: {
|
||||||
@ -81,13 +86,17 @@ const metadata: ComponentMetadata = {
|
|||||||
() => dqa('.proxy-box > div'),
|
() => dqa('.proxy-box > div'),
|
||||||
elements => elements.length > 0 || isNotHome,
|
elements => elements.length > 0 || isNotHome,
|
||||||
)
|
)
|
||||||
return Object.fromEntries(categoryElements.map(it => ([
|
return Object.fromEntries(
|
||||||
it.id.replace(/^bili_/, ''),
|
categoryElements.map(it => [
|
||||||
{
|
it.id.replace(/^bili_/, ''),
|
||||||
displayName: it.querySelector('header .name')?.textContent?.trim() ?? '未知分区',
|
{
|
||||||
defaultValue: false,
|
displayName:
|
||||||
},
|
it.querySelector('header .name')?.textContent?.trim()
|
||||||
])))
|
?? '未知分区',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const skipIds = ['推广']
|
const skipIds = ['推广']
|
||||||
@ -123,39 +132,47 @@ const metadata: ComponentMetadata = {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
.filter((it): it is [string, SimplifyHomeOption] => it !== null) ?? []
|
.filter((it): it is [string, SimplifyHomeOption] => it !== null)
|
||||||
|
?? []
|
||||||
return Object.fromEntries(entries)
|
return Object.fromEntries(entries)
|
||||||
})()
|
})()
|
||||||
const generatedSwitches: Record<string, unknown> = {}
|
const generatedSwitches: Record<string, unknown> = {}
|
||||||
Object.entries(generatedOptions).forEach(([key, { displayName, defaultValue }]) => {
|
Object.entries(generatedOptions).forEach(
|
||||||
const option = {
|
([key, { displayName, defaultValue }]) => {
|
||||||
defaultValue,
|
const option = {
|
||||||
displayName,
|
defaultValue,
|
||||||
}
|
displayName,
|
||||||
const optionKey = `switch-${key}`
|
}
|
||||||
if (options[optionKey] === undefined) {
|
const optionKey = `switch-${key}`
|
||||||
options[optionKey] = defaultValue
|
if (options[optionKey] === undefined) {
|
||||||
}
|
options[optionKey] = defaultValue
|
||||||
const switchKey = `switch-${key}`
|
}
|
||||||
addComponentListener(
|
const switchKey = `switch-${key}`
|
||||||
`${metadata.name}.${switchKey}`,
|
addComponentListener(
|
||||||
(value: boolean) => {
|
`${metadata.name}.${switchKey}`,
|
||||||
document.body.classList.toggle(`${metadata.name}-${switchKey}`, value)
|
(value: boolean) => {
|
||||||
},
|
document.body.classList.toggle(
|
||||||
true,
|
`${metadata.name}-${switchKey}`,
|
||||||
)
|
value,
|
||||||
switchOptions.switches[key] = option
|
)
|
||||||
generatedSwitches[key] = option
|
},
|
||||||
})
|
true,
|
||||||
options.simplifyOptions.switches = generatedSwitches
|
)
|
||||||
const generatedStyles = Object.keys(generatedOptions).map(name => `
|
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} .bili-layout .bili-grid[data-area="${name}"],
|
||||||
body.simplifyHome-switch-${name} .storey-box .proxy-box #bili_${name} {
|
body.simplifyHome-switch-${name} .storey-box .proxy-box #bili_${name} {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
`.trim()).join('\n')
|
`.trim())
|
||||||
|
.join('\n')
|
||||||
addStyle(generatedStyles, 'simplify-home-generated')
|
addStyle(generatedStyles, 'simplify-home-generated')
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
export const component = createSwitchOptions(switchOptions)(metadata)
|
export const component = newSwitchComponentWrapper(switchMetadata)(metadata)
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { createSwitchOptions } from '@/components/switch-options'
|
import { newSwitchComponentWrapper } from '@/components/switch-options'
|
||||||
import { styledComponentEntry } from '@/components/styled-component'
|
import { styledComponentEntry } from '@/components/styled-component'
|
||||||
import { liveUrls } from '@/core/utils/urls'
|
import { liveUrls } from '@/core/utils/urls'
|
||||||
|
|
||||||
export const component = createSwitchOptions({
|
export const component = newSwitchComponentWrapper({
|
||||||
name: 'simplifyOptions',
|
name: 'simplifyOptions',
|
||||||
dimAt: 'checked',
|
dimAt: 'checked',
|
||||||
switchProps: {
|
switchProps: {
|
||||||
|
|||||||
@ -67,7 +67,7 @@ export const enableTouchMove = (element: HTMLElement, options?: TouchMoveOptions
|
|||||||
// let startTouch: Touch
|
// let startTouch: Touch
|
||||||
let lastTouch: Touch
|
let lastTouch: Touch
|
||||||
let move: boolean
|
let move: boolean
|
||||||
const minMoveDistance = lodash.get(options, 'minMoveDistance', getComponentSettings('touchMiniPlayer').options.touchMoveDistance)
|
const minMoveDistance = lodash.get(options, 'minMoveDistance', getComponentSettings('touchMiniPlayer').options.touchMoveDistance) as number
|
||||||
// const scroll = lodash.get(options, 'scroll', false)
|
// const scroll = lodash.get(options, 'scroll', false)
|
||||||
const touchstart = (e: TouchEvent) => {
|
const touchstart = (e: TouchEvent) => {
|
||||||
if (e.touches.length < 1) {
|
if (e.touches.length < 1) {
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const createPosition = (e: TouchEvent, element: HTMLElement) => {
|
|||||||
* - `cancel`: 请求取消调整
|
* - `cancel`: 请求取消调整
|
||||||
*/
|
*/
|
||||||
export class SwipeAction extends EventTarget {
|
export class SwipeAction extends EventTarget {
|
||||||
minSwipeDistance = getComponentSettings('touchPlayerGestures').options.swiperDistance
|
minSwipeDistance = getComponentSettings('touchPlayerGestures').options.swiperDistance as number
|
||||||
startPosition: Position = null
|
startPosition: Position = null
|
||||||
lastAction: {
|
lastAction: {
|
||||||
type: 'brightness' | 'volume' | 'progress'
|
type: 'brightness' | 'volume' | 'progress'
|
||||||
|
|||||||
@ -6,8 +6,11 @@ import { Toast } from '@/core/toast'
|
|||||||
import { matchUrlPattern, retrieveImageUrl } from '@/core/utils'
|
import { matchUrlPattern, retrieveImageUrl } from '@/core/utils'
|
||||||
import { formatTitle } from '@/core/utils/title'
|
import { formatTitle } from '@/core/utils/title'
|
||||||
import { feedsUrls } from '@/core/utils/urls'
|
import { feedsUrls } from '@/core/utils/urls'
|
||||||
|
import { Options } from '.'
|
||||||
|
|
||||||
export const setupFeedImageExporter: ComponentEntry = async ({ settings: { options } }) => {
|
export const setupFeedImageExporter: ComponentEntry<Options> = async ({
|
||||||
|
settings: { options },
|
||||||
|
}) => {
|
||||||
if (!feedsUrls.some(url => matchUrlPattern(url))) {
|
if (!feedsUrls.some(url => matchUrlPattern(url))) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -18,24 +21,28 @@ export const setupFeedImageExporter: ComponentEntry = async ({ settings: { optio
|
|||||||
text: '导出图片',
|
text: '导出图片',
|
||||||
action: async () => {
|
action: async () => {
|
||||||
const imageUrls: { url: string; extension: string }[] = []
|
const imageUrls: { url: string; extension: string }[] = []
|
||||||
dqa(card.element, '.main-content .img-content, .bili-album__preview__picture__img').forEach((img: HTMLImageElement | HTMLDivElement) => {
|
dqa(card.element, '.main-content .img-content, .bili-album__preview__picture__img').forEach(
|
||||||
const urlData = retrieveImageUrl(img)
|
(img: HTMLImageElement | HTMLDivElement) => {
|
||||||
if (urlData && !imageUrls.some(({ url }) => url === urlData.url)) {
|
const urlData = retrieveImageUrl(img)
|
||||||
imageUrls.push(urlData)
|
if (urlData && !imageUrls.some(({ url }) => url === urlData.url)) {
|
||||||
}
|
imageUrls.push(urlData)
|
||||||
})
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
if (imageUrls.length === 0) {
|
if (imageUrls.length === 0) {
|
||||||
Toast.info('此条动态没有检测到任何图片.', '导出图片')
|
Toast.info('此条动态没有检测到任何图片.', '导出图片')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const toast = Toast.info('下载中...', '导出图片')
|
const toast = Toast.info('下载中...', '导出图片')
|
||||||
let downloadedCount = 0
|
let downloadedCount = 0
|
||||||
const imageBlobs = await Promise.all(imageUrls.map(async ({ url }) => {
|
const imageBlobs = await Promise.all(
|
||||||
const blob = await getBlob(url)
|
imageUrls.map(async ({ url }) => {
|
||||||
downloadedCount++
|
const blob = await getBlob(url)
|
||||||
toast.message = `下载中... (${downloadedCount}/${imageUrls.length})`
|
downloadedCount++
|
||||||
return blob
|
toast.message = `下载中... (${downloadedCount}/${imageUrls.length})`
|
||||||
}))
|
return blob
|
||||||
|
}),
|
||||||
|
)
|
||||||
const pack = new DownloadPackage()
|
const pack = new DownloadPackage()
|
||||||
const { feedFormat } = options
|
const { feedFormat } = options
|
||||||
imageBlobs.forEach((blob, index) => {
|
imageBlobs.forEach((blob, index) => {
|
||||||
@ -45,7 +52,10 @@ export const setupFeedImageExporter: ComponentEntry = async ({ settings: { optio
|
|||||||
originalUser: (card as RepostFeedsCard).repostUsername ?? card.username,
|
originalUser: (card as RepostFeedsCard).repostUsername ?? card.username,
|
||||||
n: (index + 1).toString(),
|
n: (index + 1).toString(),
|
||||||
}
|
}
|
||||||
pack.add(`${formatTitle(feedFormat, false, titleData)}${imageUrls[index].extension}`, blob)
|
pack.add(
|
||||||
|
`${formatTitle(feedFormat, false, titleData)}${imageUrls[index].extension}`,
|
||||||
|
blob,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
toast.close()
|
toast.close()
|
||||||
const packTitleData = {
|
const packTitleData = {
|
||||||
|
|||||||
@ -1,8 +1,21 @@
|
|||||||
import { defineComponentMetadata } from '@/components/define'
|
import { defineComponentMetadata, defineOptionsMetadata, OptionsOfMetadata } from '@/components/define'
|
||||||
import { matchUrlPattern } from '@/core/utils'
|
import { matchUrlPattern } from '@/core/utils'
|
||||||
import { columnUrls, feedsUrls } from '@/core/utils/urls'
|
import { columnUrls, feedsUrls } from '@/core/utils/urls'
|
||||||
import { setupFeedImageExporter } from './feed'
|
import { setupFeedImageExporter } from './feed'
|
||||||
|
|
||||||
|
const options = defineOptionsMetadata({
|
||||||
|
columnFormat: {
|
||||||
|
defaultValue: '[title][ - n]',
|
||||||
|
displayName: '专栏图片命名格式',
|
||||||
|
},
|
||||||
|
feedFormat: {
|
||||||
|
defaultValue: '[user][ - id][ - n]',
|
||||||
|
displayName: '动态图片命名格式',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Options = OptionsOfMetadata<typeof options>
|
||||||
|
|
||||||
export const component = defineComponentMetadata({
|
export const component = defineComponentMetadata({
|
||||||
name: 'imageExporter',
|
name: 'imageExporter',
|
||||||
displayName: '图片批量导出',
|
displayName: '图片批量导出',
|
||||||
@ -21,14 +34,5 @@ export const component = defineComponentMetadata({
|
|||||||
...feedsUrls,
|
...feedsUrls,
|
||||||
...columnUrls,
|
...columnUrls,
|
||||||
],
|
],
|
||||||
options: {
|
options,
|
||||||
columnFormat: {
|
|
||||||
defaultValue: '[title][ - n]',
|
|
||||||
displayName: '专栏图片命名格式',
|
|
||||||
},
|
|
||||||
feedFormat: {
|
|
||||||
defaultValue: '[user][ - id][ - n]',
|
|
||||||
displayName: '动态图片命名格式',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
import { defineComponentMetadata } from '@/components/define'
|
import { defineComponentMetadata, defineOptionsMetadata, OptionsOfMetadata } from '@/components/define'
|
||||||
import { startResolution } from './resolution'
|
import { startResolution } from './resolution'
|
||||||
|
|
||||||
|
const options = defineOptionsMetadata({
|
||||||
|
scale: {
|
||||||
|
displayName: '缩放级别',
|
||||||
|
defaultValue: 'auto',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Options = OptionsOfMetadata<typeof options>
|
||||||
|
|
||||||
export const component = defineComponentMetadata({
|
export const component = defineComponentMetadata({
|
||||||
name: 'imageResolution',
|
name: 'imageResolution',
|
||||||
displayName: '高分辨率图片',
|
displayName: '高分辨率图片',
|
||||||
@ -12,11 +22,5 @@ export const component = defineComponentMetadata({
|
|||||||
description: {
|
description: {
|
||||||
'zh-CN': '根据屏幕 DPI 请求更高分辨率的图片, 例如 DPI 缩放 200% 则请求 2 倍的分辨率, 加载时间也会相应变长一些. (也会导致某些浏览器里出现图片闪动, 因为本质上是更换了图片源)',
|
'zh-CN': '根据屏幕 DPI 请求更高分辨率的图片, 例如 DPI 缩放 200% 则请求 2 倍的分辨率, 加载时间也会相应变长一些. (也会导致某些浏览器里出现图片闪动, 因为本质上是更换了图片源)',
|
||||||
},
|
},
|
||||||
options: {
|
options,
|
||||||
scale: {
|
|
||||||
displayName: '缩放级别',
|
|
||||||
defaultValue: 'auto',
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { styledComponentEntry } from '@/components/styled-component'
|
import { styledComponentEntry } from '@/components/styled-component'
|
||||||
|
import { Options } from '.'
|
||||||
|
|
||||||
const resizeRegex = /@(\d+)[Ww]_(\d+)[Hh]/
|
const resizeRegex = /@(\d+)[Ww]_(\d+)[Hh]/
|
||||||
const excludeSelectors = [
|
const excludeSelectors = [
|
||||||
@ -59,7 +60,7 @@ export const imageResolution = async (dpi: number, element: HTMLElement) => {
|
|||||||
replaceSource(e => e.style.backgroundImage, (e, v) => (e.style.backgroundImage = v))
|
replaceSource(e => e.style.backgroundImage, (e, v) => (e.style.backgroundImage = v))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export const startResolution = styledComponentEntry(() => import('./fix.scss'),
|
export const startResolution = styledComponentEntry<Options>(() => import('./fix.scss'),
|
||||||
async ({ settings }) => {
|
async ({ settings }) => {
|
||||||
const { allMutations } = await import('@/core/observer')
|
const { allMutations } = await import('@/core/observer')
|
||||||
const dpi = settings.options.scale === 'auto'
|
const dpi = settings.options.scale === 'auto'
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { playerAgent } from '@/components/video/player-agent'
|
import { playerAgent } from '@/components/video/player-agent'
|
||||||
import { getComponentSettings } from '@/core/settings'
|
import { getComponentSettings } from '@/core/settings'
|
||||||
import { registerAndGetData } from '@/plugins/data'
|
import { registerAndGetData } from '@/plugins/data'
|
||||||
|
import { Options } from '.'
|
||||||
import { KeyBindingAction, KeyBindingActionContext } from './bindings'
|
import { KeyBindingAction, KeyBindingActionContext } from './bindings'
|
||||||
|
|
||||||
export const clickElement = (
|
export const clickElement = (
|
||||||
@ -172,11 +173,11 @@ export const builtInActions: Record<string, KeyBindingAction> = {
|
|||||||
},
|
},
|
||||||
longJumpBackward: {
|
longJumpBackward: {
|
||||||
displayName: '长倒退',
|
displayName: '长倒退',
|
||||||
run: () => playerAgent.changeTime(-(getComponentSettings('keymap').options.longJumpSeconds)),
|
run: () => playerAgent.changeTime(-(getComponentSettings<Options>('keymap').options.longJumpSeconds)),
|
||||||
},
|
},
|
||||||
longJumpForward: {
|
longJumpForward: {
|
||||||
displayName: '长前进',
|
displayName: '长前进',
|
||||||
run: () => playerAgent.changeTime(getComponentSettings('keymap').options.longJumpSeconds),
|
run: () => playerAgent.changeTime(getComponentSettings<Options>('keymap').options.longJumpSeconds),
|
||||||
},
|
},
|
||||||
jumpBackward: {
|
jumpBackward: {
|
||||||
displayName: '倒退',
|
displayName: '倒退',
|
||||||
|
|||||||
@ -1,89 +1,100 @@
|
|||||||
import { LaunchBarActionProvider } from '@/components/launch-bar/launch-bar-action'
|
import { LaunchBarActionProvider } from '@/components/launch-bar/launch-bar-action'
|
||||||
import { styledComponentEntry } from '@/components/styled-component'
|
import { styledComponentEntry } from '@/components/styled-component'
|
||||||
import { ComponentEntry } from '@/components/types'
|
import {
|
||||||
import { defineComponentMetadata } from '@/components/define'
|
defineComponentMetadata,
|
||||||
|
defineOptionsMetadata,
|
||||||
|
OptionsOfMetadata,
|
||||||
|
} from '@/components/define'
|
||||||
import { addComponentListener } from '@/core/settings'
|
import { addComponentListener } from '@/core/settings'
|
||||||
import { actions } from './actions'
|
import { actions } from './actions'
|
||||||
import { KeyBinding, KeyBindingConfig, loadKeyBindings } from './bindings'
|
import { KeyBinding, KeyBindingConfig, loadKeyBindings } from './bindings'
|
||||||
import { presetBase, presets } from './presets'
|
import { presetBase, presets } from './presets'
|
||||||
|
|
||||||
let config: KeyBindingConfig = null
|
const options = defineOptionsMetadata({
|
||||||
const parseBindings = (bindings: Record<string, string>) => (
|
longJumpSeconds: {
|
||||||
Object.entries(bindings).map(([actionName, keyString]) => {
|
defaultValue: 85,
|
||||||
const keys = keyString.split(' ').filter(it => it !== '')
|
displayName: '长跳跃秒数',
|
||||||
return {
|
},
|
||||||
keys,
|
customKeyBindings: {
|
||||||
action: actions[actionName] || none,
|
defaultValue: {} as Record<string, string>,
|
||||||
} as KeyBinding
|
displayName: '自定义键位',
|
||||||
})
|
hidden: true,
|
||||||
)
|
},
|
||||||
const entry: ComponentEntry = styledComponentEntry(() => import('./playback-tip.scss'), async ({ settings }) => {
|
preset: {
|
||||||
const update = () => {
|
defaultValue: 'Default',
|
||||||
const presetName = settings.options.preset
|
displayName: '预设',
|
||||||
const preset = presets[presetName] || {}
|
hidden: true,
|
||||||
const bindings = parseBindings(
|
},
|
||||||
{ ...presetBase, ...preset, ...settings.options.customKeyBindings },
|
|
||||||
)
|
|
||||||
if (config) {
|
|
||||||
config.bindings = bindings
|
|
||||||
} else {
|
|
||||||
config = loadKeyBindings(bindings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addComponentListener('keymap.preset', update, true)
|
|
||||||
addComponentListener('keymap.customKeyBindings', update)
|
|
||||||
})
|
})
|
||||||
|
export type Options = OptionsOfMetadata<typeof options>
|
||||||
|
let config: KeyBindingConfig = null
|
||||||
|
const parseBindings = (bindings: Record<string, string>): KeyBinding[] => {
|
||||||
|
const parseBinding = (actionName: string, keyString: string) => {
|
||||||
|
const keys = keyString.split(' ').filter(it => it !== '')
|
||||||
|
return { keys, action: actions[actionName] }
|
||||||
|
}
|
||||||
|
return Object.entries(bindings).map(([n, k]) => parseBinding(n, k))
|
||||||
|
}
|
||||||
|
const entry = styledComponentEntry<Options>(
|
||||||
|
() => import('./playback-tip.scss'),
|
||||||
|
async ({ settings }) => {
|
||||||
|
const update = () => {
|
||||||
|
const presetName = settings.options.preset
|
||||||
|
const preset = presets[presetName] || {}
|
||||||
|
const bindings = parseBindings({
|
||||||
|
...presetBase,
|
||||||
|
...preset,
|
||||||
|
...settings.options.customKeyBindings,
|
||||||
|
})
|
||||||
|
if (config) {
|
||||||
|
config.bindings = bindings
|
||||||
|
} else {
|
||||||
|
config = loadKeyBindings(bindings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addComponentListener('keymap.preset', update, true)
|
||||||
|
addComponentListener('keymap.customKeyBindings', update)
|
||||||
|
},
|
||||||
|
)
|
||||||
export const component = defineComponentMetadata({
|
export const component = defineComponentMetadata({
|
||||||
name: 'keymap',
|
name: 'keymap',
|
||||||
displayName: '快捷键扩展',
|
displayName: '快捷键扩展',
|
||||||
tags: [
|
tags: [componentsTags.video, componentsTags.utils],
|
||||||
componentsTags.video,
|
|
||||||
componentsTags.utils,
|
|
||||||
],
|
|
||||||
// urlInclude: [
|
// urlInclude: [
|
||||||
// ...videoAndBangumiUrls,
|
// ...videoAndBangumiUrls,
|
||||||
// ...cheeseUrls,
|
// ...cheeseUrls,
|
||||||
// ...mediaListUrls,
|
// ...mediaListUrls,
|
||||||
// ],
|
// ],
|
||||||
entry,
|
entry,
|
||||||
unload: () => { config && (config.enable = false) },
|
unload: () => {
|
||||||
reload: () => { config && (config.enable = true) },
|
config && (config.enable = false)
|
||||||
|
},
|
||||||
|
reload: () => {
|
||||||
|
config && (config.enable = true)
|
||||||
|
},
|
||||||
description: {
|
description: {
|
||||||
'zh-CN': '为脚本的功能和 b 站的功能启用键盘快捷键支持, 快捷键列表可在`快捷键设置`中查看和配置.',
|
'zh-CN': '为脚本的功能和 b 站的功能启用键盘快捷键支持, 快捷键列表可在`快捷键设置`中查看和配置.',
|
||||||
},
|
},
|
||||||
extraOptions: () => import('./settings/ExtraOptions.vue').then(m => m.default),
|
extraOptions: () => import('./settings/ExtraOptions.vue').then(m => m.default),
|
||||||
options: {
|
options,
|
||||||
longJumpSeconds: {
|
|
||||||
defaultValue: 85,
|
|
||||||
displayName: '长跳跃秒数',
|
|
||||||
},
|
|
||||||
customKeyBindings: {
|
|
||||||
defaultValue: {},
|
|
||||||
displayName: '自定义键位',
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
preset: {
|
|
||||||
defaultValue: 'Default',
|
|
||||||
displayName: '预设',
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugin: {
|
plugin: {
|
||||||
displayName: '快捷键扩展 - 搜索支持',
|
displayName: '快捷键扩展 - 搜索支持',
|
||||||
setup: ({ addData }) => {
|
setup: ({ addData }) => {
|
||||||
addData('launchBar.actions', (providers: LaunchBarActionProvider[]) => {
|
addData('launchBar.actions', (providers: LaunchBarActionProvider[]) => {
|
||||||
providers.push({
|
providers.push({
|
||||||
name: 'keymapSettings',
|
name: 'keymapSettings',
|
||||||
getActions: async () => [{
|
getActions: async () => [
|
||||||
name: '快捷键扩展设置',
|
{
|
||||||
description: 'Keymap Settings',
|
name: '快捷键扩展设置',
|
||||||
icon: 'mdi-keyboard-settings-outline',
|
description: 'Keymap Settings',
|
||||||
action: async () => {
|
icon: 'mdi-keyboard-settings-outline',
|
||||||
const { toggleKeymapSettings } = await import('./settings/vm')
|
action: async () => {
|
||||||
toggleKeymapSettings()
|
const { toggleKeymapSettings } = await import('./settings/vm')
|
||||||
|
toggleKeymapSettings()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
ComponentEntry,
|
ComponentEntry,
|
||||||
ComponentMetadata,
|
ComponentMetadata,
|
||||||
ComponentOption,
|
OptionMetadata,
|
||||||
|
UnknownOptions,
|
||||||
} from '@/components/types'
|
} from '@/components/types'
|
||||||
import { CoreApis } from '@/core/core-apis'
|
import { CoreApis } from '@/core/core-apis'
|
||||||
import { addComponentListener, ComponentSettings } from '@/core/settings'
|
import { addComponentListener, ComponentSettings } from '@/core/settings'
|
||||||
@ -31,7 +32,7 @@ export type EntryContext = Parameters<ComponentEntry>[0]
|
|||||||
|
|
||||||
export type OptionSubjects<O> = O & { [K in keyof O as `${Exclude<K, symbol>}$`]: Subject<O[K]> }
|
export type OptionSubjects<O> = O & { [K in keyof O as `${Exclude<K, symbol>}$`]: Subject<O[K]> }
|
||||||
|
|
||||||
export class EntrySpeedComponent<O = Record<string, unknown>>
|
export class EntrySpeedComponent<O extends UnknownOptions = UnknownOptions>
|
||||||
implements EntryContext {
|
implements EntryContext {
|
||||||
static create: <
|
static create: <
|
||||||
OO extends Record<string, any> = unknown
|
OO extends Record<string, any> = unknown
|
||||||
@ -39,7 +40,7 @@ implements EntryContext {
|
|||||||
ComponentMetadata,
|
ComponentMetadata,
|
||||||
'entry' | 'reload' | 'unload' | 'options'
|
'entry' | 'reload' | 'unload' | 'options'
|
||||||
> & {
|
> & {
|
||||||
options?: { [K in keyof OO]: ComponentOption }
|
options?: { [K in keyof OO]: OptionMetadata }
|
||||||
}) => ComponentMetadata;
|
}) => ComponentMetadata;
|
||||||
|
|
||||||
static contextMap: Partial<Record<keyof EntrySpeedComponent, keyof SpeedContext | string>> = {
|
static contextMap: Partial<Record<keyof EntrySpeedComponent, keyof SpeedContext | string>> = {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export const ERROR_MESSAGE_DURATION = 5000
|
|||||||
/** 计算菜单项 order */
|
/** 计算菜单项 order */
|
||||||
export const calcOrder = (value: number) => ((MAX_BROWSER_SPEED_VALUE - value) * 10000).toString()
|
export const calcOrder = (value: number) => ((MAX_BROWSER_SPEED_VALUE - value) * 10000).toString()
|
||||||
|
|
||||||
export interface Options {
|
export type Options = {
|
||||||
/** 最大菜单高度 */
|
/** 最大菜单高度 */
|
||||||
maxMenuHeight: boolean
|
maxMenuHeight: boolean
|
||||||
/** 隐藏进度条 */
|
/** 隐藏进度条 */
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { EntrySpeedComponent, VideoIdObject } from '../common/speed'
|
|||||||
import { NoSuchSpeedMenuItemElementError, SpeedContext } from '../common/speed/context'
|
import { NoSuchSpeedMenuItemElementError, SpeedContext } from '../common/speed/context'
|
||||||
import { formatSpeedText } from '../common/speed/utils'
|
import { formatSpeedText } from '../common/speed/utils'
|
||||||
|
|
||||||
export interface Options {
|
export type Options = {
|
||||||
/** 全局倍速 */
|
/** 全局倍速 */
|
||||||
globalSpeed: number
|
globalSpeed: number
|
||||||
/** 固定全局倍速 */
|
/** 固定全局倍速 */
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ComponentMetadata } from '@/components/types'
|
import { defineComponentMetadata } from '@/components/define'
|
||||||
import { cdnRoots } from '@/core/cdn-types'
|
import { cdnRoots } from '@/core/cdn-types'
|
||||||
import { branches } from '@/core/meta'
|
import { branches } from '@/core/meta'
|
||||||
import { getComponentsDoc } from './components-doc'
|
import { getComponentsDoc } from './components-doc'
|
||||||
@ -90,7 +90,7 @@ ${getDocText(pluginsDoc.title, pluginsDoc.items)}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const doc: ComponentMetadata = {
|
export const doc = defineComponentMetadata({
|
||||||
name: 'featureDocsGenerator',
|
name: 'featureDocsGenerator',
|
||||||
displayName: '功能文档生成器',
|
displayName: '功能文档生成器',
|
||||||
entry,
|
entry,
|
||||||
@ -99,4 +99,4 @@ export const doc: ComponentMetadata = {
|
|||||||
delete unsafeWindow.generateDocs
|
delete unsafeWindow.generateDocs
|
||||||
},
|
},
|
||||||
tags: [componentsTags.utils],
|
tags: [componentsTags.utils],
|
||||||
}
|
})
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { CustomNavbarOptions } from 'registry/lib/components/style/custom-navbar'
|
||||||
import { PluginMetadata } from '@/plugins/plugin'
|
import { PluginMetadata } from '@/plugins/plugin'
|
||||||
import { getComponentSettings } from '@/core/settings'
|
import { getComponentSettings } from '@/core/settings'
|
||||||
import type { CustomNavbarItemInit } from '../../../components/style/custom-navbar/custom-navbar-item'
|
import type { CustomNavbarItemInit } from '../../../components/style/custom-navbar/custom-navbar-item'
|
||||||
@ -11,7 +12,7 @@ export const plugin: PluginMetadata = {
|
|||||||
const defaultLink = 'https://www.bilibili.com/v/channel/'
|
const defaultLink = 'https://www.bilibili.com/v/channel/'
|
||||||
const name = 'channel'
|
const name = 'channel'
|
||||||
const isOpenInNewTab = () => {
|
const isOpenInNewTab = () => {
|
||||||
const { options } = getComponentSettings('customNavbar')
|
const { options } = getComponentSettings('customNavbar') as { options: CustomNavbarOptions }
|
||||||
if (name in options.openInNewTabOverrides) {
|
if (name in options.openInNewTabOverrides) {
|
||||||
return options.openInNewTabOverrides[name]
|
return options.openInNewTabOverrides[name]
|
||||||
}
|
}
|
||||||
@ -23,7 +24,10 @@ export const plugin: PluginMetadata = {
|
|||||||
content: () => import('./NavbarChannel.vue'),
|
content: () => import('./NavbarChannel.vue'),
|
||||||
clickAction: () => {
|
clickAction: () => {
|
||||||
const channelId = dq('.navbar-channel[data-channel-id]').getAttribute('data-channel-id')
|
const channelId = dq('.navbar-channel[data-channel-id]').getAttribute('data-channel-id')
|
||||||
window.open(channelId ? `${defaultLink}${channelId}` : defaultLink, isOpenInNewTab() ? '_blank' : '_self')
|
window.open(
|
||||||
|
channelId ? `${defaultLink}${channelId}` : defaultLink,
|
||||||
|
isOpenInNewTab() ? '_blank' : '_self',
|
||||||
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
UpdateCheckItem,
|
UpdateCheckItem,
|
||||||
CheckSingleTypeUpdateConfig,
|
CheckSingleTypeUpdateConfig,
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
import { Options } from '.'
|
||||||
|
|
||||||
export const checkUpdate = async (config: CheckUpdateConfig) => {
|
export const checkUpdate = async (config: CheckUpdateConfig) => {
|
||||||
const {
|
const {
|
||||||
@ -140,7 +141,7 @@ export const checkAllUpdate = async (config: CheckSingleTypeUpdateConfig) => {
|
|||||||
console.groupEnd()
|
console.groupEnd()
|
||||||
}
|
}
|
||||||
export const silentCheckUpdate = () => checkAllUpdate({
|
export const silentCheckUpdate = () => checkAllUpdate({
|
||||||
maxCount: getComponentSettings(name).options.maxUpdateCount,
|
maxCount: getComponentSettings<Options>(name).options.maxUpdateCount,
|
||||||
})
|
})
|
||||||
export const silentCheckUpdateAndReload = reload(silentCheckUpdate)
|
export const silentCheckUpdateAndReload = reload(silentCheckUpdate)
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ const optionsMetadata = defineOptionsMetadata({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
type Options = OptionsOfMetadata<typeof optionsMetadata>
|
export type Options = OptionsOfMetadata<typeof optionsMetadata>
|
||||||
|
|
||||||
const entry: ComponentEntry<Options> = async ({
|
const entry: ComponentEntry<Options> = async ({
|
||||||
settings: { options: opt },
|
settings: { options: opt },
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { addData } from '@/plugins/data'
|
import { addData } from '@/plugins/data'
|
||||||
import { getComponentSettings } from '@/core/settings'
|
import { getComponentSettings } from '@/core/settings'
|
||||||
import { languageNameToCode } from '@/core/utils/i18n'
|
import { languageNameToCode } from '@/core/utils/i18n'
|
||||||
import { Translation, GeneralTranslation, RegexTranslation } from './types'
|
import { Translation, GeneralTranslation, RegexTranslation, Options as I18nOptions } from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在`plugin.setup`中可使用此帮助函数快速注入翻译数据
|
* 在`plugin.setup`中可使用此帮助函数快速注入翻译数据
|
||||||
@ -22,6 +22,6 @@ export const addI18nData = (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
export const getSelectedLanguage = () => {
|
export const getSelectedLanguage = () => {
|
||||||
const settings = getComponentSettings('i18n')
|
const settings = getComponentSettings<I18nOptions>('i18n')
|
||||||
return languageNameToCode(settings.options.language)
|
return languageNameToCode(settings.options.language)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { getComponentSettings } from '@/core/settings'
|
|||||||
import { languageNameToCode } from '@/core/utils/i18n'
|
import { languageNameToCode } from '@/core/utils/i18n'
|
||||||
import { registerAndGetData } from '@/plugins/data'
|
import { registerAndGetData } from '@/plugins/data'
|
||||||
import { formData } from '@/core/utils'
|
import { formData } from '@/core/utils'
|
||||||
|
import { Options as I18nOptions } from '../types'
|
||||||
|
|
||||||
export abstract class MachineTranslateProvider {
|
export abstract class MachineTranslateProvider {
|
||||||
abstract translate(text: string): Promise<string>
|
abstract translate(text: string): Promise<string>
|
||||||
@ -11,7 +12,7 @@ export abstract class MachineTranslateProvider {
|
|||||||
abstract link: string
|
abstract link: string
|
||||||
abstract defaultLanguage: string
|
abstract defaultLanguage: string
|
||||||
protected getTargetLanguage() {
|
protected getTargetLanguage() {
|
||||||
const i18n = getComponentSettings('i18n')
|
const i18n = getComponentSettings<I18nOptions>('i18n')
|
||||||
if (i18n.enabled) {
|
if (i18n.enabled) {
|
||||||
return languageNameToCode(i18n.options.language)
|
return languageNameToCode(i18n.options.language)
|
||||||
}
|
}
|
||||||
@ -109,7 +110,7 @@ export const [translateProviders] = registerAndGetData('i18n.machineTranslators'
|
|||||||
} as Record<string, MachineTranslateProvider>)
|
} as Record<string, MachineTranslateProvider>)
|
||||||
export const translateProviderNames = Object.keys(translateProviders)
|
export const translateProviderNames = Object.keys(translateProviders)
|
||||||
export const getTranslator = (): MachineTranslateProvider => {
|
export const getTranslator = (): MachineTranslateProvider => {
|
||||||
const { options: { translator } } = getComponentSettings('i18n')
|
const { options: { translator } } = getComponentSettings<I18nOptions>('i18n')
|
||||||
const provider = translateProviders[translator] || translateProviders.GoogleCN
|
const provider = translateProviders[translator] || translateProviders.GoogleCN
|
||||||
return provider
|
return provider
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,3 +10,7 @@ export type LanguagePack = {
|
|||||||
map?: [string, Translation][]
|
map?: [string, Translation][]
|
||||||
regex?: RegexTranslation
|
regex?: RegexTranslation
|
||||||
}
|
}
|
||||||
|
export type Options = {
|
||||||
|
language: string,
|
||||||
|
translator: string,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,33 +1,31 @@
|
|||||||
import { none } from '@/core/utils'
|
import { none } from '@/core/utils'
|
||||||
import { ComponentEntry, ComponentMetadata } from './component'
|
import { ComponentEntry, ComponentMetadata, UnknownOptions } from './component'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个自动添加指定样式的组件入口函数
|
* 创建一个自动添加指定样式的组件入口函数
|
||||||
* @param styleImport 动态导入样式的函数
|
* @param styleImport 动态导入样式的函数
|
||||||
* @param entry 组件入口函数
|
* @param entry 组件入口函数
|
||||||
*/
|
*/
|
||||||
export const styledComponentEntry = (
|
export const styledComponentEntry = <O extends UnknownOptions>(
|
||||||
styleImport: () => Promise<{ default: string }>,
|
styleImport: () => Promise<{ default: string }>,
|
||||||
entry: ComponentEntry,
|
entry: ComponentEntry<O>,
|
||||||
): ComponentEntry => (
|
): ComponentEntry<O> => async context => {
|
||||||
async context => {
|
|
||||||
const { default: style } = await styleImport()
|
const { default: style } = await styleImport()
|
||||||
const { addStyle } = await import('@/core/style')
|
const { addStyle } = await import('@/core/style')
|
||||||
addStyle(style, context.metadata.name)
|
addStyle(style, context.metadata.name)
|
||||||
return entry(context)
|
return entry(context)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建仅切换样式的组件`entry`, `reload`和`unload`, 展开至组件定义中即可, 也可以提供可选的组件入口函数
|
* 创建仅切换样式的组件`entry`, `reload`和`unload`, 展开至组件定义中即可, 也可以提供可选的组件入口函数
|
||||||
* @param styleImport 动态导入样式的函数
|
* @param styleImport 动态导入样式的函数
|
||||||
* @param entry 组件入口函数
|
* @param entry 组件入口函数
|
||||||
*/
|
*/
|
||||||
export const toggleStyle = (
|
export const toggleStyle = <O extends UnknownOptions>(
|
||||||
name: string,
|
name: string,
|
||||||
styleImport: () => Promise<{ default: string }>,
|
styleImport: () => Promise<{ default: string }>,
|
||||||
entry: ComponentEntry = none,
|
entry: ComponentEntry<O> = none,
|
||||||
): Pick<ComponentMetadata, 'name' | 'entry' | 'reload' | 'unload'> => {
|
): Pick<ComponentMetadata<O>, 'name' | 'entry' | 'reload' | 'unload'> => {
|
||||||
let styleElement: HTMLStyleElement = null
|
let styleElement: HTMLStyleElement = null
|
||||||
const styleEntry = async () => {
|
const styleEntry = async () => {
|
||||||
if (styleElement) {
|
if (styleElement) {
|
||||||
@ -39,9 +37,7 @@ export const toggleStyle = (
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
entry: context => (
|
entry: context => styleEntry().then(() => entry(context)),
|
||||||
styleEntry().then(() => entry(context))
|
|
||||||
),
|
|
||||||
reload: styleEntry,
|
reload: styleEntry,
|
||||||
unload: () => {
|
unload: () => {
|
||||||
styleElement?.remove()
|
styleElement?.remove()
|
||||||
|
|||||||
@ -1,71 +1,352 @@
|
|||||||
import { getComponentSettings, addComponentListener } from '@/core/settings'
|
/**
|
||||||
import { ComponentMetadata, ComponentOptions } from './component'
|
* Switch Options API
|
||||||
|
* @module src/components/switch-options
|
||||||
|
*
|
||||||
|
* 通过包装原始的 ComponentMetadata 为组件提供一系列开关选项。
|
||||||
|
* 如果组件未定义 Widget,还会提供一个默认的有相同效果的 Widget。
|
||||||
|
*
|
||||||
|
* API 主要函数是 {@link newSwitchComponentWrapper}。
|
||||||
|
*/
|
||||||
|
|
||||||
type Switches = {
|
import { getComponentSettings, addComponentListener } from '@/core/settings'
|
||||||
[key: string]: {
|
import {
|
||||||
displayName: string
|
ComponentEntry,
|
||||||
defaultValue: boolean
|
ComponentMetadata,
|
||||||
}
|
OptionsMetadata,
|
||||||
|
OptionsOfMetadata,
|
||||||
|
UnknownOptions,
|
||||||
|
} from './component'
|
||||||
|
import { Widget } from './widget'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个开关的设置
|
||||||
|
*/
|
||||||
|
export type SwitchItemMetadata = {
|
||||||
|
/** 开关的显示名称 */
|
||||||
|
displayName: string
|
||||||
|
/** 开关的默认开启状态 */
|
||||||
|
defaultValue: boolean
|
||||||
}
|
}
|
||||||
export interface SwitchOptions {
|
|
||||||
name: string
|
/**
|
||||||
switches: Switches
|
* 用于配置所有开关。
|
||||||
radio?: boolean
|
*
|
||||||
dimAt?: 'checked' | 'notChecked'
|
* 该定义中的每个属性都会被用于生成一个组件 option。
|
||||||
|
* 其中 option 的键为属性加上前缀 'switch-',值为一个 `boolean`,表示开关状态。
|
||||||
|
*/
|
||||||
|
export type SwitchItemsMetadata<S extends string> = {
|
||||||
|
[key in S]: SwitchItemMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可用于单独定义 SwitchItemsMetadata
|
||||||
|
*/
|
||||||
|
export const defineSwitchItemsMetadata = <S extends string>(
|
||||||
|
c: SwitchItemsMetadata<S>,
|
||||||
|
): SwitchItemsMetadata<S> => c
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于配置 API 的行为。使用 {@link defineSwitchMetadata} 定义。
|
||||||
|
*/
|
||||||
|
export interface SwitchMetadata<N extends string, S extends string> {
|
||||||
|
/**
|
||||||
|
* 作为键名注入到组件 `options` 中
|
||||||
|
*
|
||||||
|
* @see {@link SwitchOptions}
|
||||||
|
*/
|
||||||
|
name: N
|
||||||
|
/** 每个开关的单独配置。可用 {@link defineSwitchItemsMetadata} 单独定义 */
|
||||||
|
switches: SwitchItemsMetadata<S>
|
||||||
|
/**
|
||||||
|
* 是否单选。值为 `undefined` 时取默认值。
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
radio?: undefined | boolean
|
||||||
|
/**
|
||||||
|
* 控制开关变暗的时机
|
||||||
|
*
|
||||||
|
* `undefined`: 始终不变暗
|
||||||
|
* `'checked'`: 当开关关闭时变暗
|
||||||
|
* `'notChecked'`: 始终为变暗状态
|
||||||
|
*
|
||||||
|
* @default undefined
|
||||||
|
*/
|
||||||
|
dimAt?: undefined | 'checked' | 'notChecked'
|
||||||
|
/** 配置每个开关的图标 */
|
||||||
switchProps?: {
|
switchProps?: {
|
||||||
checkedIcon?: string
|
checkedIcon?: string
|
||||||
notCheckedIcon?: string
|
notCheckedIcon?: string
|
||||||
iconPosition?: 'left' | 'right'
|
iconPosition?: 'left' | 'right'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const createSwitchOptions = (options: SwitchOptions) => {
|
|
||||||
if (options.radio === undefined) {
|
/**
|
||||||
options.radio = false
|
* 定义一个 {@link SwitchMetadata}
|
||||||
|
*/
|
||||||
|
export const defineSwitchMetadata = <N extends string, S extends string>(
|
||||||
|
c: SwitchMetadata<N, S>,
|
||||||
|
): SwitchMetadata<N, S> => c
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入组件的 options 的一部分。
|
||||||
|
*
|
||||||
|
* 接口中的属性值大部分来自于 {@link SwitchMetadata} 中的定义。
|
||||||
|
* 用户在 `SwitchMetadata` 中未定义的,为其默认值。
|
||||||
|
*
|
||||||
|
* @see {@link SwitchOptions}
|
||||||
|
*/
|
||||||
|
export interface SwitchMetadataOption<N extends string, S extends string> {
|
||||||
|
name: N
|
||||||
|
switches: SwitchItemsMetadata<S>
|
||||||
|
radio: boolean
|
||||||
|
dimAt: undefined | 'checked' | 'notChecked'
|
||||||
|
switchProps?: {
|
||||||
|
checkedIcon?: string
|
||||||
|
notCheckedIcon?: string
|
||||||
|
iconPosition?: 'left' | 'right'
|
||||||
}
|
}
|
||||||
const { name: optionName, switches } = options
|
/** 组件名称 */
|
||||||
const extendComponentOptions: ComponentOptions = {}
|
componentName: string
|
||||||
Object.entries(switches).forEach(([key, { displayName, defaultValue }]) => {
|
/** 组件的 Widget 显示名称 */
|
||||||
extendComponentOptions[`switch-${key}`] = {
|
optionDisplayName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个 SwitchMetadataOption
|
||||||
|
*/
|
||||||
|
const newSwitchMetadataOption = <N extends string, S extends string>(
|
||||||
|
metadata: SwitchMetadata<N, S>,
|
||||||
|
componentName: string,
|
||||||
|
optionDisplayName,
|
||||||
|
) => ({
|
||||||
|
...metadata,
|
||||||
|
radio: metadata.radio === undefined ? false : metadata.radio,
|
||||||
|
dimAt: metadata.dimAt,
|
||||||
|
componentName,
|
||||||
|
optionDisplayName,
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为类型参数 `S` 加上 'switch-' 前缀。
|
||||||
|
*/
|
||||||
|
export type SwitchItemNames<S extends string> = `switch-${S}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API 注入的 options 中的一部分
|
||||||
|
*/
|
||||||
|
export type SwitchItemOptions<S extends string> = Record<SwitchItemNames<S>, boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装完成后的组件元数据中的 options 类型
|
||||||
|
*
|
||||||
|
* 由三部分组成:
|
||||||
|
* 1. 组件元数据被包装前的所有 options
|
||||||
|
* 2. 一个键值对:键为 {@link SwitchMetadata} 定义时的 `name` 属性,值为 {@link SwitchMetadataOption} 类型
|
||||||
|
* 3. 对应 {@link SwitchItemsMetadata} 的定义内容
|
||||||
|
* (来自于 `SwitchMetadata` 的 `switches`)。详情见该类型描述
|
||||||
|
*/
|
||||||
|
export type SwitchOptions<O extends UnknownOptions, N extends string, S extends string> = O & {
|
||||||
|
N: SwitchMetadataOption<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 注入的 options 后形成的类型
|
||||||
|
*/
|
||||||
|
export type SwitchOptionsMetadata<
|
||||||
|
O extends UnknownOptions,
|
||||||
|
N extends string,
|
||||||
|
S extends string,
|
||||||
|
> = OptionsMetadata<SwitchOptions<O, N, S>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向传入的 `options` 注入 API 所需的内容
|
||||||
|
*
|
||||||
|
* 该函数会直接修改传入的 `options` 自身,并将其返回。
|
||||||
|
*/
|
||||||
|
type SwitchOptionsMetadataExtender<S extends string> = <O extends UnknownOptions, N extends string>(
|
||||||
|
options: OptionsMetadata<O>,
|
||||||
|
switchMetadataOption: SwitchMetadataOption<N, S>,
|
||||||
|
) => SwitchOptionsMetadata<O, N, S>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个 {@link SwitchOptionsMetadataExtender}
|
||||||
|
*/
|
||||||
|
const newSwitchOptionsMetadataExtender = <S extends string>(
|
||||||
|
itemsMetadata: SwitchItemsMetadata<S>,
|
||||||
|
): SwitchOptionsMetadataExtender<S> => {
|
||||||
|
const optionsToExtend = {}
|
||||||
|
const entries = Object.entries<SwitchItemMetadata>(itemsMetadata)
|
||||||
|
for (const [key, { displayName, defaultValue }] of entries) {
|
||||||
|
optionsToExtend[`switch-${key}`] = {
|
||||||
defaultValue,
|
defaultValue,
|
||||||
displayName,
|
displayName,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
return (component: ComponentMetadata) => {
|
return <O extends UnknownOptions, N extends string>(
|
||||||
const optionDisplayName = `${component.displayName}选项`
|
options: OptionsMetadata<O>,
|
||||||
const selfOption = {
|
switchMetadataOption: SwitchMetadataOption<N, S>,
|
||||||
componentName: component.name,
|
) => {
|
||||||
optionDisplayName,
|
optionsToExtend[switchMetadataOption.name as string] = {
|
||||||
}
|
|
||||||
Object.assign(options, selfOption)
|
|
||||||
extendComponentOptions[optionName] = {
|
|
||||||
defaultValue: options,
|
defaultValue: options,
|
||||||
displayName: optionDisplayName,
|
displayName: switchMetadataOption.optionDisplayName,
|
||||||
}
|
}
|
||||||
component.options = { ...component.options, ...extendComponentOptions }
|
Object.assign(options, optionsToExtend)
|
||||||
if (!component.widget) {
|
return options as SwitchOptionsMetadata<O, N, S>
|
||||||
component.widget = {
|
}
|
||||||
component: () => import('./SwitchOptions.vue').then(m => m.default),
|
}
|
||||||
options,
|
|
||||||
}
|
/**
|
||||||
}
|
* 创建默认 Widget
|
||||||
const originalEntry = component.entry
|
*/
|
||||||
component.entry = async (...args) => {
|
const newWidget = <N extends string, S extends string>(
|
||||||
originalEntry?.(...args)
|
options: SwitchMetadataOption<N, S>,
|
||||||
const { name } = component
|
): Omit<Widget, 'name'> => ({
|
||||||
const componentOptions = getComponentSettings(name).options
|
component: () => import('./SwitchOptions.vue').then(m => m.default),
|
||||||
Object.keys(componentOptions).forEach(key => {
|
options,
|
||||||
if (key.startsWith('switch-')) {
|
})
|
||||||
addComponentListener(
|
|
||||||
`${name}.${key}`,
|
/**
|
||||||
(value: boolean) => {
|
* 携带 API 注入的 `options` 的 `entry` 函数类型
|
||||||
document.body.classList.toggle(`${name}-${key}`, value)
|
*/
|
||||||
},
|
export type SwitchEntry<
|
||||||
true,
|
O extends UnknownOptions,
|
||||||
)
|
N extends string,
|
||||||
}
|
S extends string,
|
||||||
})
|
T = unknown,
|
||||||
}
|
> = ComponentEntry<SwitchOptions<O, N, S>, T>
|
||||||
return component
|
|
||||||
|
/**
|
||||||
|
* 用于被 API 包装的组件原数据,类似于 `ComponentMetadata`。
|
||||||
|
*
|
||||||
|
* 定义该类型时,请使用 {@link defineIncompleteSwitchComponentMetadata}。
|
||||||
|
*
|
||||||
|
* 在通过该函数定义组件的 `entry` 时,可以获得被 API 注入的 options 的类型提示。
|
||||||
|
*/
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义一个用于被该 API 包装的组件元数据,
|
||||||
|
* 类似于 {@link import("./define").defineComponentMetadata}。
|
||||||
|
*
|
||||||
|
* 与直接定义的不同之处在于:定义 `entry` 时,可以获得被 API 注入的 options 的类型提示。
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被包装后的 {@link ComponentMetadata}
|
||||||
|
*/
|
||||||
|
export type SwitchComponentMetadata<
|
||||||
|
O extends UnknownOptions,
|
||||||
|
N extends string,
|
||||||
|
S extends string,
|
||||||
|
> = ComponentMetadata<SwitchOptions<O, N, S>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于包装组件元数据,生成开关选项。
|
||||||
|
*
|
||||||
|
* 传入参数可以是普通的组件元数据,也可以是 {@link IncompleteSwitchComponentMetadata}。
|
||||||
|
* 后者应使用 {@link defineIncompleteSwitchComponentMetadata} 定义;
|
||||||
|
* 在通过该函数定义组件的 `entry` 时,可以获得被 API 注入的 options 的类型提示。
|
||||||
|
*
|
||||||
|
* 为保障组件功能的正常运行,请勿使用以 'switch-' 开头的 option。
|
||||||
|
*
|
||||||
|
* 若传入组件未定义 Widget,则会注入一个用于控制各开关的 Widget。
|
||||||
|
* 此 Widget 的功能与注入组件的 options 功能相同。
|
||||||
|
*
|
||||||
|
* 被包装后的元数据,其 `entry` 函数是原函数的包装,用于实现相关功能。
|
||||||
|
*
|
||||||
|
* 该函数会直接修改传入参数自身,并将其返回。
|
||||||
|
*
|
||||||
|
* @param component - 被包装的组件元数据
|
||||||
|
* @returns 包装完成的组件元数据
|
||||||
|
*/
|
||||||
|
export type SwitchComponentWrapper<N extends string, S extends string> = <O extends UnknownOptions>(
|
||||||
|
component: ComponentMetadata<O> | IncompleteSwitchComponentMetadata<O, N, S>,
|
||||||
|
) => SwitchComponentMetadata<O, N, S>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个 {@link SwitchComponentWrapper}
|
||||||
|
*
|
||||||
|
* 传入参数请使用 {@link defineSwitchMetadata} 定义。
|
||||||
|
*
|
||||||
|
* @param metadata - 相关配置元数据
|
||||||
|
* @returns 组件包装器
|
||||||
|
*/
|
||||||
|
export const newSwitchComponentWrapper = <N extends string, S extends string>(
|
||||||
|
metadata: SwitchMetadata<N, S>,
|
||||||
|
): SwitchComponentWrapper<N, S> => {
|
||||||
|
const extendOptions = newSwitchOptionsMetadataExtender(metadata.switches)
|
||||||
|
// 返回的 wrapper
|
||||||
|
return <O extends UnknownOptions>(
|
||||||
|
component: ComponentMetadata<O> | IncompleteSwitchComponentMetadata<O, N, S>,
|
||||||
|
) => {
|
||||||
|
const switchMetadataOption = newSwitchMetadataOption(
|
||||||
|
metadata,
|
||||||
|
component.name,
|
||||||
|
`${component.displayName}选项`,
|
||||||
|
)
|
||||||
|
// 若没有 Widget,则注入一个
|
||||||
|
if (!component.widget) {
|
||||||
|
component.widget = newWidget(switchMetadataOption)
|
||||||
|
}
|
||||||
|
// 扩展组件 options
|
||||||
|
extendOptions(component.options, switchMetadataOption)
|
||||||
|
// 包装 entry 函数
|
||||||
|
component.entry = newSwitchEntry(component)
|
||||||
|
return component as SwitchComponentMetadata<O, N, S>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,8 +48,7 @@ export interface ComponentTag {
|
|||||||
|
|
||||||
type ComponentOptionValidator<T> = (value: T, oldValue: T) => T | undefined | null
|
type ComponentOptionValidator<T> = (value: T, oldValue: T) => T | undefined | null
|
||||||
|
|
||||||
// TODO: 参考 discussion #3041。当不兼容代码替换完成后将 any 改为 unknown
|
export type UnknownOptions = Record<string, unknown>
|
||||||
export type UnknownOptions = Record<string, any>
|
|
||||||
|
|
||||||
export type EmptyOptions = Record<string, never>
|
export type EmptyOptions = Record<string, never>
|
||||||
|
|
||||||
@ -81,10 +80,6 @@ export type OptionsMetadata<O extends UnknownOptions = UnknownOptions> = {
|
|||||||
[OptionName in keyof O]: OptionMetadata<O[OptionName]>
|
[OptionName in keyof O]: OptionMetadata<O[OptionName]>
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 参考 discussion #3041。当不兼容代码替换完成后删除
|
|
||||||
export type ComponentOptions = OptionsMetadata
|
|
||||||
export type ComponentOption = OptionMetadata
|
|
||||||
|
|
||||||
/** 组件标签 */
|
/** 组件标签 */
|
||||||
export const componentsTags = {
|
export const componentsTags = {
|
||||||
/** 视频 */
|
/** 视频 */
|
||||||
|
|||||||
@ -58,12 +58,11 @@ const emptySettings: ComponentSettings = {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 参考 discussion #3041。
|
|
||||||
// 当不兼容代码替换完成后将 R 的默认类型替换为 UnknownOptions
|
|
||||||
/**
|
/**
|
||||||
* 获取已加载组件的设置
|
* 获取已加载组件的设置
|
||||||
*
|
*
|
||||||
* 若组件未安装,则返回一个默认的 ComponentSettings 对象:
|
* 使用此函数,应当确保该组件已被加载。
|
||||||
|
* 否则返回值是一个默认的 ComponentSettings 对象:
|
||||||
* ```js
|
* ```js
|
||||||
* {
|
* {
|
||||||
* enabled: false,
|
* enabled: false,
|
||||||
@ -73,9 +72,9 @@ const emptySettings: ComponentSettings = {
|
|||||||
*
|
*
|
||||||
* @param component 组件或组件名称
|
* @param component 组件或组件名称
|
||||||
*/
|
*/
|
||||||
export const getComponentSettings = <R extends UnknownOptions = UnknownOptions>(
|
export const getComponentSettings = <O extends UnknownOptions>(
|
||||||
component: ComponentMetadata | string,
|
component: ComponentMetadata<O> | string,
|
||||||
): ComponentSettings<R> => {
|
): ComponentSettings<O> => {
|
||||||
let componentMetadata: ComponentMetadata
|
let componentMetadata: ComponentMetadata
|
||||||
if (typeof component === 'string') {
|
if (typeof component === 'string') {
|
||||||
if (componentsMap[component] === undefined) {
|
if (componentsMap[component] === undefined) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user