refactor: carousel by ts

pull/2992/head
tangjinzhou 4 years ago
parent 93b64061db
commit 90059e6b45

@ -1,17 +1,17 @@
import { inject } from 'vue'; import { App, defineComponent, inject } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import debounce from 'lodash-es/debounce'; import debounce from 'lodash-es/debounce';
import hasProp, { initDefaultProps, getComponent } from '../_util/props-util'; import hasProp, { getComponent } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import warning from '../_util/warning'; import warning from '../_util/warning';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import SlickCarousel from '../vc-slick/src'; import SlickCarousel from '../vc-slick/src';
import { tuple } from '../_util/type';
export const CarouselEffect = PropTypes.oneOf(['scrollx', 'fade']);
// Carousel // Carousel
export const CarouselProps = { export const CarouselProps = {
effect: CarouselEffect, effect: PropTypes.oneOf(tuple('scrollx', 'fade')),
dots: PropTypes.looseBool, dots: PropTypes.looseBool.def(true),
vertical: PropTypes.looseBool, vertical: PropTypes.looseBool,
autoplay: PropTypes.looseBool, autoplay: PropTypes.looseBool,
easing: PropTypes.string, easing: PropTypes.string,
@ -20,18 +20,18 @@ export const CarouselProps = {
// style: PropTypes.React.CSSProperties, // style: PropTypes.React.CSSProperties,
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
accessibility: PropTypes.looseBool, accessibility: PropTypes.looseBool,
nextArrow: PropTypes.any, nextArrow: PropTypes.VNodeChild,
prevArrow: PropTypes.any, prevArrow: PropTypes.VNodeChild,
pauseOnHover: PropTypes.looseBool, pauseOnHover: PropTypes.looseBool,
// className: PropTypes.string, // className: PropTypes.string,
adaptiveHeight: PropTypes.looseBool, adaptiveHeight: PropTypes.looseBool,
arrows: PropTypes.looseBool, arrows: PropTypes.looseBool.def(false),
autoplaySpeed: PropTypes.number, autoplaySpeed: PropTypes.number,
centerMode: PropTypes.looseBool, centerMode: PropTypes.looseBool,
centerPadding: PropTypes.string, centerPadding: PropTypes.string,
cssEase: PropTypes.string, cssEase: PropTypes.string,
dotsClass: PropTypes.string, dotsClass: PropTypes.string,
draggable: PropTypes.looseBool, draggable: PropTypes.looseBool.def(false),
fade: PropTypes.looseBool, fade: PropTypes.looseBool,
focusOnSelect: PropTypes.looseBool, focusOnSelect: PropTypes.looseBool,
infinite: PropTypes.looseBool, infinite: PropTypes.looseBool,
@ -50,20 +50,18 @@ export const CarouselProps = {
useCSS: PropTypes.looseBool, useCSS: PropTypes.looseBool,
slickGoTo: PropTypes.number, slickGoTo: PropTypes.number,
responsive: PropTypes.array, responsive: PropTypes.array,
dotPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right']), dotPosition: PropTypes.oneOf(tuple('top', 'bottom', 'left', 'right')),
}; };
const Carousel = { const Carousel = defineComponent({
name: 'ACarousel', name: 'ACarousel',
inheritAttrs: false, inheritAttrs: false,
props: initDefaultProps(CarouselProps, { props: CarouselProps,
dots: true,
arrows: false,
draggable: false,
}),
setup() { setup() {
return { return {
configProvider: inject('configProvider', defaultConfigProvider), configProvider: inject('configProvider', defaultConfigProvider),
slick: undefined,
innerSlider: undefined,
}; };
}, },
beforeMount() { beforeMount() {
@ -90,7 +88,7 @@ const Carousel = {
const { autoplay } = this; const { autoplay } = this;
if (autoplay) { if (autoplay) {
window.removeEventListener('resize', this.onWindowResized); window.removeEventListener('resize', this.onWindowResized);
this.onWindowResized.cancel(); (this.onWindowResized as any).cancel();
} }
}, },
methods: { methods: {
@ -103,7 +101,7 @@ const Carousel = {
} }
return 'bottom'; return 'bottom';
}, },
saveSlick(node) { saveSlick(node: HTMLElement) {
this.slick = node; this.slick = node;
}, },
onWindowResized() { onWindowResized() {
@ -122,7 +120,7 @@ const Carousel = {
this.slick.slickPrev(); this.slick.slickPrev();
}, },
goTo(slide, dontAnimate = false) { goTo(slide: number, dontAnimate = false) {
this.slick.slickGoTo(slide, dontAnimate); this.slick.slickGoTo(slide, dontAnimate);
}, },
}, },
@ -134,7 +132,7 @@ const Carousel = {
if (props.effect === 'fade') { if (props.effect === 'fade') {
props.fade = true; props.fade = true;
} }
const { class: cls, style, ...restAttrs } = this.$attrs; const { class: cls, style, ...restAttrs } = this.$attrs as any;
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
let className = getPrefixCls('carousel', props.prefixCls); let className = getPrefixCls('carousel', props.prefixCls);
const dotsClass = 'slick-dots'; const dotsClass = 'slick-dots';
@ -156,14 +154,18 @@ const Carousel = {
}; };
return ( return (
<div class={className} style={style}> <div class={className} style={style}>
<SlickCarousel ref={this.saveSlick} {...SlickCarouselProps} vSlots={$slots}></SlickCarousel> <SlickCarousel
ref={this.saveSlick}
{...SlickCarouselProps}
v-slots={$slots}
></SlickCarousel>
</div> </div>
); );
}, },
}; });
/* istanbul ignore next */ /* istanbul ignore next */
Carousel.install = function(app) { Carousel.install = function(app: App) {
app.component(Carousel.name, Carousel); app.component(Carousel.name, Carousel);
return app; return app;
}; };

@ -1,4 +1,4 @@
import { inject, provide } from 'vue'; import { App, defineComponent, inject, provide, PropType } from 'vue';
import PropTypes, { withUndefined } from '../_util/vue-types'; import PropTypes, { withUndefined } from '../_util/vue-types';
import VcCascader from '../vc-cascader'; import VcCascader from '../vc-cascader';
import arrayTreeFilter from 'array-tree-filter'; import arrayTreeFilter from 'array-tree-filter';
@ -23,35 +23,74 @@ import BaseMixin from '../_util/BaseMixin';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import warning from '../_util/warning'; import warning from '../_util/warning';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import { tuple, VueNode } from 'components/_util/type';
export interface CascaderOptionType {
value?: string | number;
label?: VueNode;
disabled?: boolean;
isLeaf?: boolean;
loading?: boolean;
children?: Array<CascaderOptionType>;
[key: string]: any;
}
export interface FieldNamesType {
value?: string;
label?: string;
children?: string;
}
export interface FilledFieldNamesType {
value: string;
label: string;
children: string;
}
const CascaderOptionType = PropTypes.shape({ // const CascaderOptionType = PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.any, // label: PropTypes.any,
disabled: PropTypes.looseBool, // disabled: PropTypes.looseBool,
children: PropTypes.array, // children: PropTypes.array,
key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}).loose; // }).loose;
const FieldNamesType = PropTypes.shape({ // const FieldNamesType = PropTypes.shape({
value: PropTypes.string.isRequired, // value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired, // label: PropTypes.string.isRequired,
children: PropTypes.string, // children: PropTypes.string,
}).loose; // }).loose;
const CascaderExpandTrigger = PropTypes.oneOf(['click', 'hover']); export interface ShowSearchType {
filter?: (inputValue: string, path: CascaderOptionType[], names: FilledFieldNamesType) => boolean;
const ShowSearchType = PropTypes.shape({ render?: (
filter: PropTypes.func, inputValue: string,
render: PropTypes.func, path: CascaderOptionType[],
sort: PropTypes.func, prefixCls: string | undefined,
matchInputWidth: PropTypes.looseBool, names: FilledFieldNamesType,
limit: withUndefined(PropTypes.oneOfType([Boolean, Number])), ) => VueNode;
}).loose; sort?: (
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) => number;
matchInputWidth?: boolean;
limit?: number | false;
}
// const ShowSearchType = PropTypes.shape({
// filter: PropTypes.func,
// render: PropTypes.func,
// sort: PropTypes.func,
// matchInputWidth: PropTypes.looseBool,
// limit: withUndefined(PropTypes.oneOfType([Boolean, Number])),
// }).loose;
function noop() {} function noop() {}
const CascaderProps = { const CascaderProps = {
/** 可选项数据源 */ /** 可选项数据源 */
options: PropTypes.arrayOf(CascaderOptionType).def([]), options: { type: Array as PropType<CascaderOptionType>, default: [] },
/** 默认的选中项 */ /** 默认的选中项 */
defaultValue: PropTypes.array, defaultValue: PropTypes.array,
/** 指定选中项 */ /** 指定选中项 */
@ -65,22 +104,25 @@ const CascaderProps = {
/** 自定义浮层类名 */ /** 自定义浮层类名 */
popupClassName: PropTypes.string, popupClassName: PropTypes.string,
/** 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` */ /** 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` */
popupPlacement: PropTypes.oneOf(['bottomLeft', 'bottomRight', 'topLeft', 'topRight']).def( popupPlacement: PropTypes.oneOf(tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight')).def(
'bottomLeft', 'bottomLeft',
), ),
/** 输入框占位文本*/ /** 输入框占位文本*/
placeholder: PropTypes.string.def('Please select'), placeholder: PropTypes.string.def('Please select'),
/** 输入框大小,可选 `large` `default` `small` */ /** 输入框大小,可选 `large` `default` `small` */
size: PropTypes.oneOf(['large', 'default', 'small']), size: PropTypes.oneOf(tuple('large', 'default', 'small')),
/** 禁用*/ /** 禁用*/
disabled: PropTypes.looseBool.def(false), disabled: PropTypes.looseBool.def(false),
/** 是否支持清除*/ /** 是否支持清除*/
allowClear: PropTypes.looseBool.def(true), allowClear: PropTypes.looseBool.def(true),
showSearch: withUndefined(PropTypes.oneOfType([Boolean, ShowSearchType])), showSearch: {
notFoundContent: PropTypes.any, type: [Boolean, Object] as PropType<ShowSearchType>,
default: undefined,
},
notFoundContent: PropTypes.VNodeChild,
loadData: PropTypes.func, loadData: PropTypes.func,
/** 次级菜单的展开方式,可选 'click' 和 'hover' */ /** 次级菜单的展开方式,可选 'click' 和 'hover' */
expandTrigger: CascaderExpandTrigger, expandTrigger: PropTypes.oneOf(tuple('click', 'hover')),
/** 当此项为 true 时,点选每级菜单选项值都会发生变化 */ /** 当此项为 true 时,点选每级菜单选项值都会发生变化 */
changeOnSelect: PropTypes.looseBool, changeOnSelect: PropTypes.looseBool,
/** 浮层可见变化时回调 */ /** 浮层可见变化时回调 */
@ -89,9 +131,9 @@ const CascaderProps = {
inputPrefixCls: PropTypes.string, inputPrefixCls: PropTypes.string,
getPopupContainer: PropTypes.func, getPopupContainer: PropTypes.func,
popupVisible: PropTypes.looseBool, popupVisible: PropTypes.looseBool,
fieldNames: FieldNamesType, fieldNames: { type: Object as PropType<FieldNamesType> },
autofocus: PropTypes.looseBool, autofocus: PropTypes.looseBool,
suffixIcon: PropTypes.any, suffixIcon: PropTypes.VNodeChild,
showSearchRender: PropTypes.any, showSearchRender: PropTypes.any,
onChange: PropTypes.func, onChange: PropTypes.func,
onPopupVisibleChange: PropTypes.func, onPopupVisibleChange: PropTypes.func,
@ -101,14 +143,25 @@ const CascaderProps = {
'onUpdate:value': PropTypes.func, 'onUpdate:value': PropTypes.func,
}; };
type CascaderPropsTypes = typeof CascaderProps;
// We limit the filtered item count by default // We limit the filtered item count by default
const defaultLimit = 50; const defaultLimit = 50;
function defaultFilterOption(inputValue, path, names) { function defaultFilterOption(
inputValue: string,
path: CascaderOptionType[],
names: FilledFieldNamesType,
) {
return path.some(option => option[names.label].indexOf(inputValue) > -1); return path.some(option => option[names.label].indexOf(inputValue) > -1);
} }
function defaultSortFilteredOption(a, b, inputValue, names) { function defaultSortFilteredOption(
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) {
function callback(elem) { function callback(elem) {
return elem[names.label].indexOf(inputValue) > -1; return elem[names.label].indexOf(inputValue) > -1;
} }
@ -116,8 +169,9 @@ function defaultSortFilteredOption(a, b, inputValue, names) {
return a.findIndex(callback) - b.findIndex(callback); return a.findIndex(callback) - b.findIndex(callback);
} }
function getFilledFieldNames({ fieldNames = {} }) { function getFilledFieldNames(props: CascaderPropsTypes) {
const names = { const fieldNames = (props.fieldNames || {}) as FieldNamesType;
const names: FilledFieldNamesType = {
children: fieldNames.children || 'children', children: fieldNames.children || 'children',
label: fieldNames.label || 'label', label: fieldNames.label || 'label',
value: fieldNames.value || 'value', value: fieldNames.value || 'value',
@ -125,8 +179,12 @@ function getFilledFieldNames({ fieldNames = {} }) {
return names; return names;
} }
function flattenTree(options = [], props, ancestor = []) { function flattenTree(
const names = getFilledFieldNames(props); options: CascaderOptionType[],
props: CascaderPropsTypes,
ancestor: CascaderOptionType[] = [],
) {
const names: FilledFieldNamesType = getFilledFieldNames(props);
let flattenOptions = []; let flattenOptions = [];
const childrenName = names.children; const childrenName = names.children;
options.forEach(option => { options.forEach(option => {
@ -163,7 +221,7 @@ const Cascader = {
}, },
data() { data() {
this.cachedOptions = []; this.cachedOptions = [];
const { value, defaultValue, popupVisible, showSearch, options } = this; const { value, defaultValue, popupVisible, showSearch, options } = this.$props;
return { return {
sValue: value || defaultValue || [], sValue: value || defaultValue || [],
inputValue: '', inputValue: '',
@ -553,7 +611,7 @@ const Cascader = {
}, },
}; };
Cascader.install = function(app) { Cascader.install = function(app: App) {
app.component(Cascader.name, Cascader); app.component(Cascader.name, Cascader);
return app; return app;
}; };
Loading…
Cancel
Save