style: vc-select2 to ts

This commit is contained in:
tangjinzhou 2020-10-03 15:54:52 +08:00
parent 47e5df9103
commit 4b4fc9266c
14 changed files with 266 additions and 99 deletions

View File

@ -31,7 +31,8 @@
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/ban-types": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-empty-function": 0
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-non-null-assertion": 0
}
}
],

View File

@ -1,8 +0,0 @@
function createRef() {
const func = function setRef(node) {
func.current = node;
};
return func;
}
export default createRef;

View File

@ -0,0 +1,12 @@
interface RefObject extends Function {
current?: any;
}
function createRef(): RefObject {
const func: RefObject = (node: any) => {
func.current = node;
};
return func;
}
export default createRef;

View File

@ -1,6 +1,10 @@
export const responsiveArray = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
export const responsiveMap = {
export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
export const responsiveMap: BreakpointMap = {
xs: '(max-width: 575px)',
sm: '(min-width: 576px)',
md: '(min-width: 768px)',
@ -9,40 +13,46 @@ export const responsiveMap = {
xxl: '(min-width: 1600px)',
};
const subscribers = new Map();
type SubscribeFunc = (screens: ScreenMap) => void;
const subscribers = new Map<Number, SubscribeFunc>();
let subUid = -1;
let screens = {};
const responsiveObserve = {
matchHandlers: {},
dispatch(pointMap) {
matchHandlers: {} as {
[prop: string]: {
mql: MediaQueryList;
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
};
},
dispatch(pointMap: ScreenMap) {
screens = pointMap;
subscribers.forEach(func => func(screens));
return subscribers.size >= 1;
},
subscribe(func) {
subscribe(func: SubscribeFunc): number {
if (!subscribers.size) this.register();
subUid += 1;
subscribers.set(subUid, func);
func(screens);
return subUid;
},
unsubscribe(token) {
unsubscribe(token: number) {
subscribers.delete(token);
if (!subscribers.size) this.unregister();
},
unregister() {
Object.keys(responsiveMap).forEach(screen => {
const matchMediaQuery = responsiveMap[screen];
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
const matchMediaQuery = responsiveMap[screen]!;
const handler = this.matchHandlers[matchMediaQuery];
handler?.mql.removeListener(handler?.listener);
});
subscribers.clear();
},
register() {
Object.keys(responsiveMap).forEach(screen => {
const matchMediaQuery = responsiveMap[screen];
const listener = ({ matches }) => {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
const matchMediaQuery = responsiveMap[screen]!;
const listener = ({ matches }: { matches: boolean }) => {
this.dispatch({
...screens,
[screen]: matches,

View File

@ -1,10 +1,11 @@
// based on rc-resize-observer 0.1.3
import ResizeObserver from 'resize-observer-polyfill';
import { defineComponent } from 'vue';
import BaseMixin from '../_util/BaseMixin';
import { findDOMNode } from '../_util/props-util';
// Still need to be compatible with React 15, we use class component here
const VueResizeObserver = {
const VueResizeObserver = defineComponent({
name: 'ResizeObserver',
mixins: [BaseMixin],
props: {
@ -84,6 +85,6 @@ const VueResizeObserver = {
render() {
return this.$slots.default && this.$slots.default()[0];
},
};
});
export default VueResizeObserver;

View File

@ -5,7 +5,7 @@ import classNames from '../_util/classNames';
import pickAttrs from '../_util/pickAttrs';
import { isValidElement } from '../_util/props-util';
import createRef from '../_util/createRef';
import { computed, reactive, watch } from 'vue';
import { computed, defineComponent, reactive, watch } from 'vue';
import List from '../vc-virtual-list/List';
const OptionListProps = {
@ -39,7 +39,7 @@ const OptionListProps = {
* Using virtual list of option display.
* Will fallback to dom if use customize render.
*/
const OptionList = {
const OptionList = defineComponent({
props: OptionListProps,
name: 'OptionList',
inheritAttrs: false,
@ -138,7 +138,7 @@ const OptionList = {
const mergedLabel = props.childrenAsData ? children : label;
return item ? (
<div
aria-label={typeof mergedLabel === 'string' ? mergedLabel : null}
aria-label={typeof mergedLabel === 'string' ? mergedLabel : undefined}
{...attrs}
key={index}
role="option"
@ -343,6 +343,6 @@ const OptionList = {
</>
);
},
};
});
export default OptionList;

View File

@ -3,8 +3,10 @@ import PropTypes from '../_util/vue-types';
import { getSlot } from '../_util/props-util';
import classNames from '../_util/classNames';
import createRef from '../_util/createRef';
import { CSSProperties, defineComponent, VNodeChild } from 'vue';
import { RenderDOMFunc } from './interface';
const getBuiltInPlacements = dropdownMatchSelectWidth => {
const getBuiltInPlacements = (dropdownMatchSelectWidth: number | boolean) => {
// Enable horizontal overflow auto-adjustment when a custom dropdown width is provided
const adjustX = typeof dropdownMatchSelectWidth !== 'number' ? 0 : 1;
@ -43,33 +45,27 @@ const getBuiltInPlacements = dropdownMatchSelectWidth => {
},
};
};
export default {
export interface SelectTriggerProps {
prefixCls: string;
disabled: boolean;
visible: boolean;
popupElement: VNodeChild;
animation?: string;
transitionName?: string;
containerWidth: number;
dropdownStyle: CSSProperties;
dropdownClassName: string;
direction: string;
dropdownMatchSelectWidth?: boolean | number;
dropdownRender?: (menu: VNodeChild) => VNodeChild;
getPopupContainer?: RenderDOMFunc;
dropdownAlign: object;
empty: boolean;
getTriggerDOMNode: () => HTMLElement;
}
const SelectTrigger = defineComponent<SelectTriggerProps>({
name: 'SelectTrigger',
inheritAttrs: false,
props: {
// onPopupFocus: PropTypes.func,
// onPopupScroll: PropTypes.func,
dropdownAlign: PropTypes.object,
visible: PropTypes.bool,
disabled: PropTypes.bool,
dropdownClassName: PropTypes.string,
dropdownStyle: PropTypes.object,
empty: PropTypes.bool,
prefixCls: PropTypes.string,
popupClassName: PropTypes.string,
// children: PropTypes.any,
animation: PropTypes.string,
transitionName: PropTypes.string,
getPopupContainer: PropTypes.func,
dropdownRender: PropTypes.func,
containerWidth: PropTypes.number,
dropdownMatchSelectWidth: PropTypes.oneOfType([Number, Boolean]).def(true),
popupElement: PropTypes.any,
direction: PropTypes.string,
getTriggerDOMNode: PropTypes.func,
},
created() {
this.popupRef = createRef();
},
@ -89,7 +85,7 @@ export default {
},
render() {
const { empty, ...props } = { ...this.$props, ...this.$attrs };
const { empty = false, ...props } = { ...this.$props, ...this.$attrs };
const {
visible,
dropdownAlign,
@ -122,7 +118,6 @@ export default {
showAction={[]}
hideAction={[]}
popupPlacement={this.direction === 'rtl' ? 'bottomRight' : 'bottomLeft'}
popupPlacement="bottomLeft"
builtinPlacements={builtInPlacements}
prefixCls={dropdownPrefixCls}
popupTransitionName={this.getDropdownTransitionName()}
@ -141,4 +136,24 @@ export default {
</Trigger>
);
},
});
SelectTrigger.props = {
dropdownAlign: PropTypes.object,
visible: PropTypes.bool,
disabled: PropTypes.bool,
dropdownClassName: PropTypes.string,
dropdownStyle: PropTypes.object,
empty: PropTypes.bool,
prefixCls: PropTypes.string,
popupClassName: PropTypes.string,
animation: PropTypes.string,
transitionName: PropTypes.string,
getPopupContainer: PropTypes.func,
dropdownRender: PropTypes.func,
containerWidth: PropTypes.number,
dropdownMatchSelectWidth: PropTypes.oneOfType([Number, Boolean]).def(true),
popupElement: PropTypes.any,
direction: PropTypes.string,
getTriggerDOMNode: PropTypes.func,
};
export default SelectTrigger;

View File

@ -1,41 +0,0 @@
const TransBtn = (
_,
{ attrs: { class: className, customizeIcon, customizeIconProps, onMousedown, onClick }, slots },
) => {
let icon;
if (typeof customizeIcon === 'function') {
icon = customizeIcon(customizeIconProps);
} else {
icon = customizeIcon;
}
return (
<span
class={className}
onMousedown={event => {
event.preventDefault();
if (onMousedown) {
onMousedown(event);
}
}}
style={{
userSelect: 'none',
WebkitUserSelect: 'none',
}}
unselectable="on"
onClick={onClick}
aria-hidden
>
{icon !== undefined ? (
icon
) : (
<span class={className.split(/\s+/).map(cls => `${cls}-icon`)}>{slots?.default()}</span>
)}
</span>
);
};
TransBtn.inheritAttrs = false;
export default TransBtn;

View File

@ -0,0 +1,59 @@
import { SetupContext, VNodeChild } from 'vue';
import PropTypes from '../_util/vue-types';
export interface TransBtnProps {
class: string;
customizeIcon: VNodeChild | ((props?: any) => VNodeChild);
customizeIconProps?: any;
onMousedown?: (payload: MouseEvent) => void;
onClick?: (payload: MouseEvent) => void;
}
const TransBtn = (props: TransBtnProps, { slots }: SetupContext) => {
const { class: className, customizeIcon, customizeIconProps, onMousedown, onClick } = props;
let icon: VNodeChild;
if (typeof customizeIcon === 'function') {
icon = customizeIcon(customizeIconProps);
} else {
icon = customizeIcon;
}
return (
<span
class={className}
onMousedown={event => {
event.preventDefault();
if (onMousedown) {
onMousedown(event);
}
}}
style={{
userSelect: 'none',
WebkitUserSelect: 'none',
}}
unselectable="on"
onClick={onClick}
aria-hidden
>
{icon !== undefined ? (
icon
) : (
<span class={className.split(/\s+/).map((cls: any) => `${cls}-icon`)}>
{slots.default && slots.default()}
</span>
)}
</span>
);
};
TransBtn.inheritAttrs = false;
TransBtn.props = {
class: PropTypes.string,
customizeIcon: PropTypes.any,
customizeIconProps: PropTypes.any,
onMousedown: PropTypes.func,
onClick: PropTypes.func,
};
export default TransBtn;

View File

@ -0,0 +1,64 @@
import * as Vue from 'vue';
import { SelectProps, RefSelectProps } from '../generate';
export type SelectSource = 'option' | 'selection' | 'input';
export const INTERNAL_PROPS_MARK = 'RC_SELECT_INTERNAL_PROPS_MARK';
// =================================== Shared Type ===================================
export type Key = string | number;
export type RawValueType = string | number;
export interface LabelValueType {
key?: Key;
value?: RawValueType;
label?: Vue.VNodeChild;
}
export type DefaultValueType = RawValueType | RawValueType[] | LabelValueType | LabelValueType[];
export interface DisplayLabelValueType extends LabelValueType {
disabled?: boolean;
}
export type SingleType<MixType> = MixType extends (infer Single)[] ? Single : MixType;
export type OnClear = () => void;
export type CustomTagProps = {
label: DefaultValueType;
value: DefaultValueType;
disabled: boolean;
onClose: (event?: MouseEvent) => void;
closable: boolean;
};
// ==================================== Generator ====================================
export type GetLabeledValue<FOT extends FlattenOptionsType> = (
value: RawValueType,
config: {
options: FOT;
prevValue: DefaultValueType;
labelInValue: boolean;
optionLabelProp: string;
},
) => LabelValueType;
export type FilterOptions<OptionsType extends object[]> = (
searchValue: string,
options: OptionsType,
/** Component props, since Select & TreeSelect use different prop name, use any here */
config: {
optionFilterProp: string;
filterOption: boolean | FilterFunc<OptionsType[number]>;
},
) => OptionsType;
export type FilterFunc<OptionType> = (inputValue: string, option?: OptionType) => boolean;
export type FlattenOptionsType<OptionsType extends object[] = object[]> = {
key: Key;
data: OptionsType[number];
/** Used for customize data */
[name: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}[];

View File

@ -0,0 +1,54 @@
import * as Vue from 'vue';
import { Key, RawValueType } from './generator';
export type RenderDOMFunc = (props: any) => HTMLElement;
export type RenderNode = Vue.VNodeChild | ((props: any) => Vue.VNodeChild);
export type Mode = 'multiple' | 'tags' | 'combobox';
// ======================== Option ========================
export type OnActiveValue = (
active: RawValueType,
index: number,
info?: { source?: 'keyboard' | 'mouse' },
) => void;
export interface OptionCoreData {
key?: Key;
disabled?: boolean;
value: Key;
title?: string;
className?: string;
class?: string;
style?: Vue.CSSProperties;
label?: Vue.VNodeChild;
/** @deprecated Only works when use `children` as option data */
children?: Vue.VNodeChild;
}
export interface OptionData extends OptionCoreData {
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface OptionGroupData {
key?: Key;
label?: Vue.VNodeChild;
options: OptionData[];
className?: string;
class?: string;
style?: Vue.CSSProperties;
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export type OptionsType = (OptionData | OptionGroupData)[];
export interface FlattenOptionData {
group?: boolean;
groupOption?: boolean;
key: string | number;
data: OptionData | OptionGroupData;
}

View File

@ -1,4 +1,4 @@
import { inject, provide } from 'vue';
import { defineComponent, inject, provide } from 'vue';
import PropTypes from '../_util/vue-types';
import contains from '../vc-util/Dom/contains';
import {
@ -37,7 +37,7 @@ const ALL_HANDLERS = [
'onContextmenu',
];
export default {
export default defineComponent({
name: 'Trigger',
mixins: [BaseMixin],
inheritAttrs: false,
@ -653,4 +653,4 @@ export default {
}
return [portal, trigger];
},
};
});