21 changed files with 1139 additions and 835 deletions
File diff suppressed because it is too large
@ -1,5 +1,6 @@ |
|||
import BasicTree from './src/Tree.vue'; |
|||
import './style'; |
|||
|
|||
export { BasicTree }; |
|||
export type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; |
|||
export * from './src/typing'; |
|||
export * from './src/tree'; |
|||
|
|||
@ -1,108 +0,0 @@ |
|||
import type { PropType } from 'vue'; |
|||
import type { |
|||
ReplaceFields, |
|||
ActionItem, |
|||
Keys, |
|||
CheckKeys, |
|||
ContextMenuOptions, |
|||
TreeItem, |
|||
} from './typing'; |
|||
import type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; |
|||
import type { TreeDataItem } from 'ant-design-vue/es/tree'; |
|||
import { propTypes } from '/@/utils/propTypes'; |
|||
|
|||
export const basicProps = { |
|||
value: { |
|||
type: [Object, Array] as PropType<Keys | CheckKeys>, |
|||
}, |
|||
renderIcon: { |
|||
type: Function as PropType<(params: Recordable) => string>, |
|||
}, |
|||
|
|||
helpMessage: { |
|||
type: [String, Array] as PropType<string | string[]>, |
|||
default: '', |
|||
}, |
|||
|
|||
title: propTypes.string, |
|||
toolbar: propTypes.bool, |
|||
search: propTypes.bool, |
|||
searchValue: propTypes.string, |
|||
checkStrictly: propTypes.bool, |
|||
clickRowToExpand: propTypes.bool.def(true), |
|||
checkable: propTypes.bool.def(false), |
|||
defaultExpandLevel: { |
|||
type: [String, Number] as PropType<string | number>, |
|||
default: '', |
|||
}, |
|||
defaultExpandAll: propTypes.bool.def(false), |
|||
|
|||
replaceFields: { |
|||
type: Object as PropType<ReplaceFields>, |
|||
}, |
|||
|
|||
treeData: { |
|||
type: Array as PropType<TreeDataItem[]>, |
|||
}, |
|||
|
|||
actionList: { |
|||
type: Array as PropType<ActionItem[]>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
expandedKeys: { |
|||
type: Array as PropType<Keys>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
selectedKeys: { |
|||
type: Array as PropType<Keys>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
checkedKeys: { |
|||
type: Array as PropType<CheckKeys>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
beforeRightClick: { |
|||
type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>, |
|||
default: null, |
|||
}, |
|||
|
|||
rightMenuList: { |
|||
type: Array as PropType<ContextMenuItem[]>, |
|||
}, |
|||
// 自定义数据过滤判断方法(注: 不是整个过滤方法,而是内置过滤的判断方法,用于增强原本仅能通过title进行过滤的方式)
|
|||
filterFn: { |
|||
type: Function as PropType< |
|||
(searchValue: any, node: TreeItem, replaceFields: ReplaceFields) => boolean |
|||
>, |
|||
default: null, |
|||
}, |
|||
// 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启
|
|||
highlight: { |
|||
type: [Boolean, String] as PropType<Boolean | String>, |
|||
default: false, |
|||
}, |
|||
// 搜索完成时自动展开结果
|
|||
expandOnSearch: propTypes.bool.def(false), |
|||
// 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效
|
|||
checkOnSearch: propTypes.bool.def(false), |
|||
// 搜索完成自动select所有结果
|
|||
selectedOnSearch: propTypes.bool.def(false), |
|||
}; |
|||
|
|||
export const treeNodeProps = { |
|||
actionList: { |
|||
type: Array as PropType<ActionItem[]>, |
|||
default: () => [], |
|||
}, |
|||
replaceFields: { |
|||
type: Object as PropType<ReplaceFields>, |
|||
}, |
|||
treeData: { |
|||
type: Array as PropType<TreeDataItem[]>, |
|||
default: () => [], |
|||
}, |
|||
}; |
|||
@ -0,0 +1,184 @@ |
|||
import type { ExtractPropTypes } from 'vue'; |
|||
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; |
|||
|
|||
import { buildProps } from '/@/utils/props'; |
|||
|
|||
export enum ToolbarEnum { |
|||
SELECT_ALL, |
|||
UN_SELECT_ALL, |
|||
EXPAND_ALL, |
|||
UN_EXPAND_ALL, |
|||
CHECK_STRICTLY, |
|||
CHECK_UN_STRICTLY, |
|||
} |
|||
|
|||
export const treeEmits = [ |
|||
'update:expandedKeys', |
|||
'update:selectedKeys', |
|||
'update:value', |
|||
'change', |
|||
'check', |
|||
'update:searchValue', |
|||
]; |
|||
|
|||
export interface TreeState { |
|||
expandedKeys: KeyType[]; |
|||
selectedKeys: KeyType[]; |
|||
checkedKeys: CheckKeys; |
|||
checkStrictly: boolean; |
|||
} |
|||
|
|||
export interface FieldNames { |
|||
children?: string; |
|||
title?: string; |
|||
key?: string; |
|||
} |
|||
|
|||
export type KeyType = string | number; |
|||
|
|||
export type CheckKeys = |
|||
| KeyType[] |
|||
| { checked: string[] | number[]; halfChecked: string[] | number[] }; |
|||
|
|||
export const treeProps = buildProps({ |
|||
value: { |
|||
type: [Object, Array] as PropType<KeyType[] | CheckKeys>, |
|||
}, |
|||
|
|||
renderIcon: { |
|||
type: Function as PropType<(params: Recordable) => string>, |
|||
}, |
|||
|
|||
helpMessage: { |
|||
type: [String, Array] as PropType<string | string[]>, |
|||
default: '', |
|||
}, |
|||
|
|||
title: { |
|||
type: String, |
|||
default: '', |
|||
}, |
|||
toolbar: Boolean, |
|||
search: Boolean, |
|||
searchValue: { |
|||
type: String, |
|||
default: '', |
|||
}, |
|||
checkStrictly: Boolean, |
|||
clickRowToExpand: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
checkable: Boolean, |
|||
defaultExpandLevel: { |
|||
type: [String, Number] as PropType<string | number>, |
|||
default: '', |
|||
}, |
|||
defaultExpandAll: Boolean, |
|||
|
|||
fieldNames: { |
|||
type: Object as PropType<FieldNames>, |
|||
}, |
|||
|
|||
treeData: { |
|||
type: Array as PropType<TreeDataItem[]>, |
|||
}, |
|||
|
|||
actionList: { |
|||
type: Array as PropType<TreeActionItem[]>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
expandedKeys: { |
|||
type: Array as PropType<KeyType[]>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
selectedKeys: { |
|||
type: Array as PropType<KeyType[]>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
checkedKeys: { |
|||
type: Array as PropType<CheckKeys>, |
|||
default: () => [], |
|||
}, |
|||
|
|||
beforeRightClick: { |
|||
type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>, |
|||
default: undefined, |
|||
}, |
|||
|
|||
rightMenuList: { |
|||
type: Array as PropType<ContextMenuItem[]>, |
|||
}, |
|||
// 自定义数据过滤判断方法(注: 不是整个过滤方法,而是内置过滤的判断方法,用于增强原本仅能通过title进行过滤的方式)
|
|||
filterFn: { |
|||
type: Function as PropType< |
|||
(searchValue: any, node: TreeItem, replaceFields: FieldNames) => boolean |
|||
>, |
|||
default: undefined, |
|||
}, |
|||
// 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启
|
|||
highlight: { |
|||
type: [Boolean, String] as PropType<Boolean | String>, |
|||
default: false, |
|||
}, |
|||
// 搜索完成时自动展开结果
|
|||
expandOnSearch: Boolean, |
|||
// 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效
|
|||
checkOnSearch: Boolean, |
|||
// 搜索完成自动select所有结果
|
|||
selectedOnSearch: Boolean, |
|||
}); |
|||
|
|||
export type TreeProps = ExtractPropTypes<typeof treeProps>; |
|||
|
|||
export interface ContextMenuItem { |
|||
label: string; |
|||
icon?: string; |
|||
disabled?: boolean; |
|||
handler?: Fn; |
|||
divider?: boolean; |
|||
children?: ContextMenuItem[]; |
|||
} |
|||
|
|||
export interface ContextMenuOptions { |
|||
icon?: string; |
|||
styles?: any; |
|||
items?: ContextMenuItem[]; |
|||
} |
|||
|
|||
export interface TreeItem extends TreeDataItem { |
|||
icon?: any; |
|||
} |
|||
|
|||
export interface TreeActionItem { |
|||
render: (record: Recordable) => any; |
|||
show?: boolean | ((record: Recordable) => boolean); |
|||
} |
|||
|
|||
export interface InsertNodeParams { |
|||
parentKey: string | null; |
|||
node: TreeDataItem; |
|||
list?: TreeDataItem[]; |
|||
push?: 'push' | 'unshift'; |
|||
} |
|||
|
|||
export interface TreeActionType { |
|||
checkAll: (checkAll: boolean) => void; |
|||
expandAll: (expandAll: boolean) => void; |
|||
setExpandedKeys: (keys: KeyType[]) => void; |
|||
getExpandedKeys: () => KeyType[]; |
|||
setSelectedKeys: (keys: KeyType[]) => void; |
|||
getSelectedKeys: () => KeyType[]; |
|||
setCheckedKeys: (keys: CheckKeys) => void; |
|||
getCheckedKeys: () => CheckKeys; |
|||
filterByLevel: (level: number) => void; |
|||
insertNodeByKey: (opt: InsertNodeParams) => void; |
|||
insertNodesByKey: (opt: InsertNodeParams) => void; |
|||
deleteNodeByKey: (key: string) => void; |
|||
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void; |
|||
setSearchValue: (value: string) => void; |
|||
getSearchValue: () => string; |
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
import type { TreeDataItem, CheckEvent as CheckEventOrigin } from 'ant-design-vue/es/tree/Tree'; |
|||
|
|||
import { ContextMenuItem } from '/@/hooks/web/useContextMenu'; |
|||
|
|||
export interface ActionItem { |
|||
render: (record: Recordable) => any; |
|||
show?: boolean | ((record: Recordable) => boolean); |
|||
} |
|||
|
|||
export interface TreeItem extends TreeDataItem { |
|||
icon?: any; |
|||
} |
|||
|
|||
export interface ReplaceFields { |
|||
children?: string; |
|||
title?: string; |
|||
key?: string; |
|||
} |
|||
|
|||
export type Keys = (string | number)[]; |
|||
export type CheckKeys = |
|||
| (string | number)[] |
|||
| { checked: (string | number)[]; halfChecked: (string | number)[] }; |
|||
|
|||
export interface TreeActionType { |
|||
checkAll: (checkAll: boolean) => void; |
|||
expandAll: (expandAll: boolean) => void; |
|||
setExpandedKeys: (keys: Keys) => void; |
|||
getExpandedKeys: () => Keys; |
|||
setSelectedKeys: (keys: Keys) => void; |
|||
getSelectedKeys: () => Keys; |
|||
setCheckedKeys: (keys: CheckKeys) => void; |
|||
getCheckedKeys: () => CheckKeys; |
|||
filterByLevel: (level: number) => void; |
|||
insertNodeByKey: (opt: InsertNodeParams) => void; |
|||
insertNodesByKey: (opt: InsertNodeParams) => void; |
|||
deleteNodeByKey: (key: string) => void; |
|||
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void; |
|||
setSearchValue: (value: string) => void; |
|||
getSearchValue: () => string; |
|||
} |
|||
|
|||
export interface InsertNodeParams { |
|||
parentKey: string | null; |
|||
node: TreeDataItem; |
|||
list?: TreeDataItem[]; |
|||
push?: 'push' | 'unshift'; |
|||
} |
|||
|
|||
export interface ContextMenuOptions { |
|||
icon?: string; |
|||
styles?: any; |
|||
items?: ContextMenuItem[]; |
|||
} |
|||
|
|||
export type CheckEvent = CheckEventOrigin; |
|||
@ -0,0 +1,49 @@ |
|||
@tree-prefix-cls: ~'@{namespace}-tree'; |
|||
|
|||
.@{tree-prefix-cls} { |
|||
background-color: @component-background; |
|||
|
|||
.ant-tree-node-content-wrapper { |
|||
position: relative; |
|||
|
|||
.ant-tree-title { |
|||
position: absolute; |
|||
left: 0; |
|||
width: 100%; |
|||
} |
|||
} |
|||
|
|||
&__title { |
|||
position: relative; |
|||
display: flex; |
|||
align-items: center; |
|||
width: 100%; |
|||
padding-right: 10px; |
|||
|
|||
&:hover { |
|||
.@{tree-prefix-cls}__action { |
|||
visibility: visible; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&__content { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
&__actions { |
|||
position: absolute; |
|||
top: 2px; |
|||
right: 3px; |
|||
display: flex; |
|||
} |
|||
|
|||
&__action { |
|||
margin-left: 4px; |
|||
visibility: hidden; |
|||
} |
|||
|
|||
&-header { |
|||
border-bottom: 1px solid @border-color-base; |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
import './index.less'; |
|||
@ -0,0 +1,52 @@ |
|||
import { prefixCls } from '/@/settings/designSetting'; |
|||
|
|||
type Mod = string | { [key: string]: any }; |
|||
type Mods = Mod | Mod[]; |
|||
|
|||
export type BEM = ReturnType<typeof createBEM>; |
|||
|
|||
function genBem(name: string, mods?: Mods): string { |
|||
if (!mods) { |
|||
return ''; |
|||
} |
|||
|
|||
if (typeof mods === 'string') { |
|||
return ` ${name}--${mods}`; |
|||
} |
|||
|
|||
if (Array.isArray(mods)) { |
|||
return mods.reduce<string>((ret, item) => ret + genBem(name, item), ''); |
|||
} |
|||
|
|||
return Object.keys(mods).reduce((ret, key) => ret + (mods[key] ? genBem(name, key) : ''), ''); |
|||
} |
|||
|
|||
/** |
|||
* bem helper |
|||
* b() // 'button'
|
|||
* b('text') // 'button__text'
|
|||
* b({ disabled }) // 'button button--disabled'
|
|||
* b('text', { disabled }) // 'button__text button__text--disabled'
|
|||
* b(['disabled', 'primary']) // 'button button--disabled button--primary'
|
|||
*/ |
|||
export function buildBEM(name: string) { |
|||
return (el?: Mods, mods?: Mods): Mods => { |
|||
if (el && typeof el !== 'string') { |
|||
mods = el; |
|||
el = ''; |
|||
} |
|||
|
|||
el = el ? `${name}__${el}` : name; |
|||
|
|||
return `${el}${genBem(el, mods)}`; |
|||
}; |
|||
} |
|||
|
|||
export function createBEM(name: string) { |
|||
return [buildBEM(`${prefixCls}-${name}`)]; |
|||
} |
|||
|
|||
export function createNamespace(name: string) { |
|||
const prefixedName = `${prefixCls}-${name}`; |
|||
return [prefixedName, buildBEM(prefixedName)] as const; |
|||
} |
|||
@ -0,0 +1,185 @@ |
|||
// copy from element-plus
|
|||
|
|||
import { warn } from 'vue'; |
|||
import { isObject } from '@vue/shared'; |
|||
import { fromPairs } from 'lodash-es'; |
|||
import type { ExtractPropTypes, PropType } from '@vue/runtime-core'; |
|||
import type { Mutable } from './types'; |
|||
|
|||
const wrapperKey = Symbol(); |
|||
export type PropWrapper<T> = { [wrapperKey]: T }; |
|||
|
|||
export const propKey = Symbol(); |
|||
|
|||
type ResolveProp<T> = ExtractPropTypes<{ |
|||
key: { type: T; required: true }; |
|||
}>['key']; |
|||
type ResolvePropType<T> = ResolveProp<T> extends { type: infer V } ? V : ResolveProp<T>; |
|||
type ResolvePropTypeWithReadonly<T> = Readonly<T> extends Readonly<Array<infer A>> |
|||
? ResolvePropType<A[]> |
|||
: ResolvePropType<T>; |
|||
|
|||
type IfUnknown<T, V> = [unknown] extends [T] ? V : T; |
|||
|
|||
export type BuildPropOption<T, D extends BuildPropType<T, V, C>, R, V, C> = { |
|||
type?: T; |
|||
values?: readonly V[]; |
|||
required?: R; |
|||
default?: R extends true |
|||
? never |
|||
: D extends Record<string, unknown> | Array<any> |
|||
? () => D |
|||
: (() => D) | D; |
|||
validator?: ((val: any) => val is C) | ((val: any) => boolean); |
|||
}; |
|||
|
|||
type _BuildPropType<T, V, C> = |
|||
| (T extends PropWrapper<unknown> |
|||
? T[typeof wrapperKey] |
|||
: [V] extends [never] |
|||
? ResolvePropTypeWithReadonly<T> |
|||
: never) |
|||
| V |
|||
| C; |
|||
export type BuildPropType<T, V, C> = _BuildPropType< |
|||
IfUnknown<T, never>, |
|||
IfUnknown<V, never>, |
|||
IfUnknown<C, never> |
|||
>; |
|||
|
|||
type _BuildPropDefault<T, D> = [T] extends [ |
|||
// eslint-disable-next-line @typescript-eslint/ban-types
|
|||
Record<string, unknown> | Array<any> | Function, |
|||
] |
|||
? D |
|||
: D extends () => T |
|||
? ReturnType<D> |
|||
: D; |
|||
|
|||
export type BuildPropDefault<T, D, R> = R extends true |
|||
? { readonly default?: undefined } |
|||
: { |
|||
readonly default: Exclude<D, undefined> extends never |
|||
? undefined |
|||
: Exclude<_BuildPropDefault<T, D>, undefined>; |
|||
}; |
|||
export type BuildPropReturn<T, D, R, V, C> = { |
|||
readonly type: PropType<BuildPropType<T, V, C>>; |
|||
readonly required: IfUnknown<R, false>; |
|||
readonly validator: ((val: unknown) => boolean) | undefined; |
|||
[propKey]: true; |
|||
} & BuildPropDefault<BuildPropType<T, V, C>, IfUnknown<D, never>, IfUnknown<R, false>>; |
|||
|
|||
/** |
|||
* @description Build prop. It can better optimize prop types |
|||
* @description 生成 prop,能更好地优化类型 |
|||
* @example |
|||
// limited options
|
|||
// the type will be PropType<'light' | 'dark'>
|
|||
buildProp({ |
|||
type: String, |
|||
values: ['light', 'dark'], |
|||
} as const) |
|||
* @example |
|||
// limited options and other types
|
|||
// the type will be PropType<'small' | 'medium' | number>
|
|||
buildProp({ |
|||
type: [String, Number], |
|||
values: ['small', 'medium'], |
|||
validator: (val: unknown): val is number => typeof val === 'number', |
|||
} as const) |
|||
@link see more: https://github.com/element-plus/element-plus/pull/3341
|
|||
*/ |
|||
export function buildProp< |
|||
T = never, |
|||
D extends BuildPropType<T, V, C> = never, |
|||
R extends boolean = false, |
|||
V = never, |
|||
C = never, |
|||
>(option: BuildPropOption<T, D, R, V, C>, key?: string): BuildPropReturn<T, D, R, V, C> { |
|||
// filter native prop type and nested prop, e.g `null`, `undefined` (from `buildProps`)
|
|||
if (!isObject(option) || !!option[propKey]) return option as any; |
|||
|
|||
const { values, required, default: defaultValue, type, validator } = option; |
|||
|
|||
const _validator = |
|||
values || validator |
|||
? (val: unknown) => { |
|||
let valid = false; |
|||
let allowedValues: unknown[] = []; |
|||
|
|||
if (values) { |
|||
allowedValues = [...values, defaultValue]; |
|||
valid ||= allowedValues.includes(val); |
|||
} |
|||
if (validator) valid ||= validator(val); |
|||
|
|||
if (!valid && allowedValues.length > 0) { |
|||
const allowValuesText = [...new Set(allowedValues)] |
|||
.map((value) => JSON.stringify(value)) |
|||
.join(', '); |
|||
warn( |
|||
`Invalid prop: validation failed${ |
|||
key ? ` for prop "${key}"` : '' |
|||
}. Expected one of [${allowValuesText}], got value ${JSON.stringify(val)}.`,
|
|||
); |
|||
} |
|||
return valid; |
|||
} |
|||
: undefined; |
|||
|
|||
return { |
|||
type: |
|||
typeof type === 'object' && Object.getOwnPropertySymbols(type).includes(wrapperKey) |
|||
? type[wrapperKey] |
|||
: type, |
|||
required: !!required, |
|||
default: defaultValue, |
|||
validator: _validator, |
|||
[propKey]: true, |
|||
} as unknown as BuildPropReturn<T, D, R, V, C>; |
|||
} |
|||
|
|||
type NativePropType = [((...args: any) => any) | { new (...args: any): any } | undefined | null]; |
|||
|
|||
export const buildProps = < |
|||
O extends { |
|||
[K in keyof O]: O[K] extends BuildPropReturn<any, any, any, any, any> |
|||
? O[K] |
|||
: [O[K]] extends NativePropType |
|||
? O[K] |
|||
: O[K] extends BuildPropOption<infer T, infer D, infer R, infer V, infer C> |
|||
? D extends BuildPropType<T, V, C> |
|||
? BuildPropOption<T, D, R, V, C> |
|||
: never |
|||
: never; |
|||
}, |
|||
>( |
|||
props: O, |
|||
) => |
|||
fromPairs( |
|||
Object.entries(props).map(([key, option]) => [key, buildProp(option as any, key)]), |
|||
) as unknown as { |
|||
[K in keyof O]: O[K] extends { [propKey]: boolean } |
|||
? O[K] |
|||
: [O[K]] extends NativePropType |
|||
? O[K] |
|||
: O[K] extends BuildPropOption< |
|||
infer T, |
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|||
infer _D, |
|||
infer R, |
|||
infer V, |
|||
infer C |
|||
> |
|||
? BuildPropReturn<T, O[K]['default'], R, V, C> |
|||
: never; |
|||
}; |
|||
|
|||
export const definePropType = <T>(val: any) => ({ [wrapperKey]: val } as PropWrapper<T>); |
|||
|
|||
export const keyOf = <T>(arr: T) => Object.keys(arr) as Array<keyof T>; |
|||
export const mutable = <T extends readonly any[] | Record<string, unknown>>(val: T) => |
|||
val as Mutable<typeof val>; |
|||
|
|||
export const componentSize = ['large', 'medium', 'small', 'mini'] as const; |
|||
@ -0,0 +1,42 @@ |
|||
// copy from element-plus
|
|||
|
|||
import type { CSSProperties, Plugin } from 'vue'; |
|||
|
|||
type OptionalKeys<T extends Record<string, unknown>> = { |
|||
[K in keyof T]: T extends Record<K, T[K]> ? never : K; |
|||
}[keyof T]; |
|||
|
|||
type RequiredKeys<T extends Record<string, unknown>> = Exclude<keyof T, OptionalKeys<T>>; |
|||
|
|||
type MonoArgEmitter<T, Keys extends keyof T> = <K extends Keys>(evt: K, arg?: T[K]) => void; |
|||
|
|||
type BiArgEmitter<T, Keys extends keyof T> = <K extends Keys>(evt: K, arg: T[K]) => void; |
|||
|
|||
export type EventEmitter<T extends Record<string, unknown>> = MonoArgEmitter<T, OptionalKeys<T>> & |
|||
BiArgEmitter<T, RequiredKeys<T>>; |
|||
|
|||
export type AnyFunction<T> = (...args: any[]) => T; |
|||
|
|||
export type PartialReturnType<T extends (...args: unknown[]) => unknown> = Partial<ReturnType<T>>; |
|||
|
|||
export type SFCWithInstall<T> = T & Plugin; |
|||
|
|||
export type Nullable<T> = T | null; |
|||
|
|||
export type RefElement = Nullable<HTMLElement>; |
|||
|
|||
export type CustomizedHTMLElement<T> = HTMLElement & T; |
|||
|
|||
export type Indexable<T> = { |
|||
[key: string]: T; |
|||
}; |
|||
|
|||
export type Hash<T> = Indexable<T>; |
|||
|
|||
export type TimeoutHandle = ReturnType<typeof global.setTimeout>; |
|||
|
|||
export type ComponentSize = 'large' | 'medium' | 'small' | 'mini'; |
|||
|
|||
export type StyleValue = string | CSSProperties | Array<StyleValue>; |
|||
|
|||
export type Mutable<T> = { -readonly [P in keyof T]: T[P] }; |
|||
Loading…
Reference in new issue