Merge branch 'next' of github.com:vueComponent/ant-design-vue into next

feat-dayjs
tangjinzhou 2020-10-17 22:22:44 +08:00
commit dbb67a7b81
30 changed files with 301 additions and 235 deletions

View File

@ -2,7 +2,7 @@ export interface RefObject extends Function {
current?: any; current?: any;
} }
function createRef(): RefObject { function createRef(): any {
const func: RefObject = (node: any) => { const func: RefObject = (node: any) => {
func.current = node; func.current = node;
}; };

View File

@ -42,7 +42,7 @@ const responsiveObserve = {
if (!subscribers.size) this.unregister(); if (!subscribers.size) this.unregister();
}, },
unregister() { unregister() {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => { Object.keys(responsiveMap).forEach((screen: string) => {
const matchMediaQuery = responsiveMap[screen]!; const matchMediaQuery = responsiveMap[screen]!;
const handler = this.matchHandlers[matchMediaQuery]; const handler = this.matchHandlers[matchMediaQuery];
handler?.mql.removeListener(handler?.listener); handler?.mql.removeListener(handler?.listener);
@ -50,7 +50,7 @@ const responsiveObserve = {
subscribers.clear(); subscribers.clear();
}, },
register() { register() {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => { Object.keys(responsiveMap).forEach((screen: string) => {
const matchMediaQuery = responsiveMap[screen]!; const matchMediaQuery = responsiveMap[screen]!;
const listener = ({ matches }: { matches: boolean }) => { const listener = ({ matches }: { matches: boolean }) => {
this.dispatch({ this.dispatch({

View File

@ -19,17 +19,10 @@ const AffixMounter = {
render() { render() {
return ( return (
<div> <div ref="container" class="container">
<div ref="container" class="container"> <Affix class="fixed" target={() => this.$refs.container} ref="affix">
<Affix <Button type="primary">Fixed at the top of container</Button>
class="fixed" </Affix>
target={() => this.$refs.container}
ref="affix"
{...{ props: this.$props }}
>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
</div> </div>
); );
}, },

View File

@ -150,6 +150,6 @@ AutoComplete.install = function(app: App) {
}; };
export default AutoComplete as typeof AutoComplete & { export default AutoComplete as typeof AutoComplete & {
readonly Option: typeof AutoComplete.Option; readonly Option: typeof Option;
readonly OptGroup: typeof AutoComplete.OptGroup; readonly OptGroup: typeof OptGroup;
}; };

View File

@ -59,7 +59,7 @@ exports[`Button should not render as link button when href is undefined 1`] = `
`; `;
exports[`Button should support link button 1`] = ` exports[`Button should support link button 1`] = `
<a target="_blank" href="http://ant.design" class="ant-btn"> <a target="_blank" class="ant-btn" href="http://ant.design">
<!----><span>link button</span> <!----><span>link button</span>
</a> </a>
`; `;

View File

@ -96,7 +96,7 @@ const Card = defineComponent({
} = this.$props; } = this.$props;
const { $slots } = this; const { $slots } = this;
const children = getSlot(this); const children = getSlot(this);
const getPrefixCls = this.configProvider.getPrefixCls; const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('card', customizePrefixCls); const prefixCls = getPrefixCls('card', customizePrefixCls);
const tabBarExtraContent = getComponent(this, 'tabBarExtraContent'); const tabBarExtraContent = getComponent(this, 'tabBarExtraContent');

View File

@ -19,9 +19,9 @@ export default defineComponent({
props: { props: {
name: PropTypes.string, name: PropTypes.string,
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
defaultValue: { type: Array as PropType<Array<CheckboxValueType>>}, defaultValue: { type: Array as PropType<Array<CheckboxValueType>> },
value: { type: Array as PropType<Array<CheckboxValueType>>}, value: { type: Array as PropType<Array<CheckboxValueType>> },
options: {type: Array as PropType<Array<CheckboxOptionType | string>>}, options: { type: Array as PropType<Array<CheckboxOptionType | string>> },
disabled: PropTypes.looseBool, disabled: PropTypes.looseBool,
onChange: PropTypes.func, onChange: PropTypes.func,
}, },
@ -48,7 +48,7 @@ export default defineComponent({
}, },
methods: { methods: {
getOptions() { getOptions() {
const { options, $slots } = this; const { options = [], $slots } = this;
return options.map(option => { return options.map(option => {
if (typeof option === 'string') { if (typeof option === 'string') {
return { return {

View File

@ -8,11 +8,11 @@ describe('ConfigProvider', () => {
mountTest({ mountTest({
render() { render() {
return ( return (
<div> <>
<ConfigProvider> <ConfigProvider>
<div /> <div />
</ConfigProvider> </ConfigProvider>
</div> </>
); );
}, },
}); });

View File

@ -89,16 +89,16 @@ const ACol = defineComponent<ColProps>({
let mergedStyle: CSSProperties = {}; let mergedStyle: CSSProperties = {};
if (gutter) { if (gutter) {
mergedStyle = { mergedStyle = {
...(gutter[0]! > 0 ...(gutter[0] > 0
? { ? {
paddingLeft: gutter[0]! / 2, paddingLeft: `${gutter[0] / 2}px`,
paddingRight: gutter[0]! / 2, paddingRight: `${gutter[0] / 2}px`,
} }
: {}), : {}),
...(gutter[1]! > 0 ...(gutter[1] > 0
? { ? {
paddingTop: gutter[1]! / 2, paddingTop: `${gutter[1] / 2}px`,
paddingBottom: gutter[1]! / 2, paddingBottom: `${gutter[1] / 2}px`,
} }
: {}), : {}),
...mergedStyle, ...mergedStyle,
@ -109,7 +109,7 @@ const ACol = defineComponent<ColProps>({
} }
return ( return (
<div style={mergedStyle} class={classes}> <div class={classes} style={mergedStyle}>
{slots.default?.()} {slots.default?.()}
</div> </div>
); );

View File

@ -104,16 +104,16 @@ const ARow = defineComponent<RowProps>({
[`${prefixCls}-${align}`]: align, [`${prefixCls}-${align}`]: align,
}); });
const rowStyle = { const rowStyle = {
...(gutter[0]! > 0 ...(gutter[0] > 0
? { ? {
marginLeft: gutter[0]! / -2, marginLeft: `${gutter[0] / -2}px`,
marginRight: gutter[0]! / -2, marginRight: `${gutter[0] / -2}px`,
} }
: {}), : {}),
...(gutter[1]! > 0 ...(gutter[1] > 0
? { ? {
marginTop: gutter[1]! / -2, marginTop: `${gutter[1] / -2}px`,
marginBottom: gutter[1]! / 2, marginBottom: `${gutter[1] / -2}px`,
} }
: {}), : {}),
}; };

View File

@ -3,12 +3,24 @@
exports[`Grid renders wrapped Col correctly 1`] = ` exports[`Grid renders wrapped Col correctly 1`] = `
<div class="ant-row" style="margin-left: -10px; margin-right: -10px;"> <div class="ant-row" style="margin-left: -10px; margin-right: -10px;">
<div> <div>
<div class="ant-col ant-col-12" style="padding-left: 10px; padding-right: 10px;"></div> <div class="ant-col ant-col-12" style="padding-left: 10px; padding-right: 10px;">
<!---->
</div>
</div>
<div class="ant-col ant-col-12" style="padding-left: 10px; padding-right: 10px;">
<!---->
</div> </div>
<div class="ant-col ant-col-12" style="padding-left: 10px; padding-right: 10px;"></div>
</div> </div>
`; `;
exports[`Grid should render Col 1`] = `<div class="ant-col ant-col-2"></div>`; exports[`Grid should render Col 1`] = `
<div class="ant-col ant-col-2">
<!---->
</div>
`;
exports[`Grid should render Row 1`] = `<div class="ant-row"></div>`; exports[`Grid should render Row 1`] = `
<div class="ant-row">
<!---->
</div>
`;

View File

@ -121,7 +121,7 @@ describe('message', () => {
}, 0); }, 0);
}); });
it('should allow custom icon', async () => { it('should allow custom icon', async () => {
message.open({ content: 'Message', icon: () => <SmileOutlined /> }); // eslint-disable-line message.open({ content: 'Message', icon: <SmileOutlined /> });
await asyncExpect(() => { await asyncExpect(() => {
expect(document.querySelectorAll('.anticon-smile').length).toBe(1); expect(document.querySelectorAll('.anticon-smile').length).toBe(1);
}, 0); }, 0);

View File

@ -1,70 +0,0 @@
import { Circle as VCCircle } from '../vc-progress';
import { validProgress } from './utils';
const statusColorMap = {
normal: '#108ee9',
exception: '#ff5500',
success: '#87d068',
};
function getPercentage({ percent, successPercent }) {
const ptg = validProgress(percent);
if (!successPercent) return ptg;
const successPtg = validProgress(successPercent);
return [successPercent, validProgress(ptg - successPtg)];
}
function getStrokeColor({ progressStatus, successPercent, strokeColor }) {
const color = strokeColor || statusColorMap[progressStatus];
if (!successPercent) return color;
return [statusColorMap.success, color];
}
const Circle = (_, { attrs, slots }) => {
const {
prefixCls,
width,
strokeWidth,
trailColor,
strokeLinecap,
gapPosition,
gapDegree,
type,
} = attrs;
const circleSize = width || 120;
const circleStyle = {
width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize,
height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize,
fontSize: `${circleSize * 0.15 + 6}px`,
};
const circleWidth = strokeWidth || 6;
const gapPos = gapPosition || (type === 'dashboard' && 'bottom') || 'top';
const gapDeg = gapDegree || (type === 'dashboard' && 75);
const strokeColor = getStrokeColor(attrs);
const isGradient = Object.prototype.toString.call(strokeColor) === '[object Object]';
const wrapperClassName = {
[`${prefixCls}-inner`]: true,
[`${prefixCls}-circle-gradient`]: isGradient,
};
return (
<div class={wrapperClassName} style={circleStyle}>
<VCCircle
percent={getPercentage(attrs)}
strokeWidth={circleWidth}
trailWidth={circleWidth}
strokeColor={strokeColor}
strokeLinecap={strokeLinecap}
trailColor={trailColor}
prefixCls={prefixCls}
gapDegree={gapDeg}
gapPosition={gapPos}
/>
{slots?.default()}
</div>
);
};
export default Circle;

View File

@ -0,0 +1,85 @@
import { defineComponent, ExtractPropTypes } from 'vue';
import { Circle as VCCircle } from '../vc-progress';
import PropTypes from '../_util/vue-types';
import { validProgress } from './utils';
import { ProgressProps } from './props';
const CircleProps = {
...ProgressProps,
progressStatus: PropTypes.string,
};
const statusColorMap: Record<string, string> = {
normal: '#108ee9',
exception: '#ff5500',
success: '#87d068',
};
export type ICircleProps = ExtractPropTypes<typeof CircleProps>;
function getPercentage({ percent, successPercent }: ICircleProps) {
const ptg = validProgress(percent);
if (!successPercent) return ptg;
const successPtg = validProgress(successPercent);
return [successPercent, validProgress(ptg - successPtg)];
}
function getStrokeColor({ progressStatus, successPercent, strokeColor }: ICircleProps) {
const color = strokeColor || statusColorMap[progressStatus];
if (!successPercent) return color;
return [statusColorMap.success, color];
}
const Circle = defineComponent({
props: CircleProps,
setup(props, { slots }) {
return () => {
const {
prefixCls,
width,
strokeWidth,
trailColor,
strokeLinecap,
gapPosition,
gapDegree,
type,
} = props;
const circleSize = width || 120;
const circleStyle = {
width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize,
height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize,
fontSize: `${circleSize * 0.15 + 6}px`,
};
const circleWidth = strokeWidth || 6;
const gapPos = gapPosition || (type === 'dashboard' && 'bottom') || 'top';
const gapDeg = gapDegree || (type === 'dashboard' && 75);
const strokeColor = getStrokeColor(props);
const isGradient = Object.prototype.toString.call(strokeColor) === '[object Object]';
const wrapperClassName = {
[`${prefixCls}-inner`]: true,
[`${prefixCls}-circle-gradient`]: isGradient,
};
return (
<div class={wrapperClassName} style={circleStyle}>
<VCCircle
percent={getPercentage(props)}
strokeWidth={circleWidth}
trailWidth={circleWidth}
strokeColor={strokeColor}
strokeLinecap={strokeLinecap}
trailColor={trailColor}
prefixCls={prefixCls}
gapDegree={gapDeg}
gapPosition={gapPos}
/>
{slots?.default()}
</div>
);
};
},
});
export default Circle;

View File

@ -1,9 +1,10 @@
import { App } from 'vue';
import Progress from './progress'; import Progress from './progress';
export { ProgressProps } from './progress'; export { ProgressProps } from './props';
/* istanbul ignore next */ /* istanbul ignore next */
Progress.install = function(app) { Progress.install = function(app: App) {
app.component(Progress.name, Progress); app.component(Progress.name, Progress);
return app; return app;
}; };

View File

@ -1,7 +1,7 @@
import { inject } from 'vue'; import { defineComponent, inject } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types'; import { getOptionProps } from '../_util/props-util';
import { getOptionProps, initDefaultProps } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import CheckOutlined from '@ant-design/icons-vue/CheckOutlined'; import CheckOutlined from '@ant-design/icons-vue/CheckOutlined';
@ -10,30 +10,9 @@ import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
import Line from './line'; import Line from './line';
import Circle from './circle'; import Circle from './circle';
import { validProgress } from './utils'; import { validProgress } from './utils';
import { ProgressProps, ProgressStatuses } from './props';
const ProgressStatuses = ['normal', 'exception', 'active', 'success']; export default defineComponent({
export const ProgressType = PropTypes.oneOf(['line', 'circle', 'dashboard']);
export const ProgressSize = PropTypes.oneOf(['default', 'small']);
export const ProgressProps = {
prefixCls: PropTypes.string,
type: ProgressType,
percent: PropTypes.number,
successPercent: PropTypes.number,
format: PropTypes.func,
status: PropTypes.oneOf(ProgressStatuses),
showInfo: PropTypes.looseBool,
strokeWidth: PropTypes.number,
strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square']),
strokeColor: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
trailColor: PropTypes.string,
width: PropTypes.number,
gapDegree: PropTypes.number,
gapPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right']),
size: ProgressSize,
};
export default {
name: 'AProgress', name: 'AProgress',
props: initDefaultProps(ProgressProps, { props: initDefaultProps(ProgressProps, {
type: 'line', type: 'line',
@ -65,7 +44,7 @@ export default {
} }
return status || 'normal'; return status || 'normal';
}, },
renderProcessInfo(prefixCls, progressStatus) { renderProcessInfo(prefixCls: string, progressStatus: typeof ProgressStatuses[number]) {
const { showInfo, format, type, percent, successPercent } = this.$props; const { showInfo, format, type, percent, successPercent } = this.$props;
if (!showInfo) return null; if (!showInfo) return null;
@ -93,7 +72,7 @@ export default {
render() { render() {
const props = getOptionProps(this); const props = getOptionProps(this);
const { prefixCls: customizePrefixCls, size, type, showInfo } = props; const { prefixCls: customizePrefixCls, size, type, showInfo } = props;
const getPrefixCls = this.configProvider.getPrefixCls; const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('progress', customizePrefixCls); const prefixCls = getPrefixCls('progress', customizePrefixCls);
const progressStatus = this.getProgressStatus(); const progressStatus = this.getProgressStatus();
const progressInfo = this.renderProcessInfo(prefixCls, progressStatus); const progressInfo = this.renderProcessInfo(prefixCls, progressStatus);
@ -128,4 +107,4 @@ export default {
}; };
return <div {...progressProps}>{progress}</div>; return <div {...progressProps}>{progress}</div>;
}, },
}; });

View File

@ -0,0 +1,24 @@
import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type';
export const ProgressStatuses = tuple('normal', 'exception', 'active', 'success');
export const ProgressType = PropTypes.oneOf(tuple('line', 'circle', 'dashboard'));
export const ProgressSize = PropTypes.oneOf(tuple('default', 'small'));
export const ProgressProps = {
prefixCls: PropTypes.string,
type: ProgressType,
percent: PropTypes.number,
successPercent: PropTypes.number,
format: PropTypes.func,
status: PropTypes.oneOf(ProgressStatuses),
showInfo: PropTypes.looseBool,
strokeWidth: PropTypes.number,
strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square']),
strokeColor: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
trailColor: PropTypes.string,
width: PropTypes.number,
gapDegree: PropTypes.number,
gapPosition: PropTypes.oneOf(tuple('top', 'bottom', 'left', 'right')),
size: ProgressSize,
};

View File

@ -1,4 +1,4 @@
export function validProgress(progress) { export function validProgress(progress?: number) {
if (!progress || progress < 0) { if (!progress || progress < 0) {
return 0; return 0;
} }

View File

@ -1,6 +1,8 @@
import { inject, App, CSSProperties, SetupContext } from 'vue'; import { inject, App, defineComponent, PropType } from 'vue';
import PropTypes from '../_util/vue-types';
import { filterEmpty } from '../_util/props-util'; import { filterEmpty } from '../_util/props-util';
import { defaultConfigProvider, SizeType } from '../config-provider'; import { defaultConfigProvider, SizeType } from '../config-provider';
import { tuple } from '../_util/type';
const spaceSize = { const spaceSize = {
small: 8, small: 8,
@ -8,67 +10,74 @@ const spaceSize = {
large: 24, large: 24,
}; };
export interface SpaceProps { const Space = defineComponent({
prefixCls?: string; name: 'ASpace',
className?: string; props: {
style?: CSSProperties; prefixCls: PropTypes.string,
size?: SizeType | number; size: {
direction?: 'horizontal' | 'vertical'; type: [String, Number] as PropType<number | SizeType>,
// No `stretch` since many components do not support that. },
align?: 'start' | 'end' | 'center' | 'baseline'; direction: PropTypes.oneOf(tuple('horizontal', 'vertical')),
} align: PropTypes.oneOf(tuple('start', 'end', 'center', 'baseline')),
},
setup(props, { slots }) {
const configProvider = inject('configProvider', defaultConfigProvider);
const {
align,
size = 'small',
direction = 'horizontal',
prefixCls: customizePrefixCls,
} = props;
const Space = (props: SpaceProps, { slots }: SetupContext) => { const { getPrefixCls } = configProvider;
const configProvider = inject('configProvider', defaultConfigProvider);
const { align, size = 'small', direction = 'horizontal', prefixCls: customizePrefixCls } = props;
const { getPrefixCls } = configProvider; return () => {
const prefixCls = getPrefixCls('space', customizePrefixCls); const prefixCls = getPrefixCls('space', customizePrefixCls);
const items = filterEmpty(slots.default?.()); const items = filterEmpty(slots.default?.());
const len = items.length; const len = items.length;
if (len === 0) { if (len === 0) {
return null; return null;
} }
const mergedAlign = align === undefined && direction === 'horizontal' ? 'center' : align; const mergedAlign = align === undefined && direction === 'horizontal' ? 'center' : align;
const someSpaceClass = { const someSpaceClass = {
[prefixCls]: true, [prefixCls]: true,
[`${prefixCls}-${direction}`]: true, [`${prefixCls}-${direction}`]: true,
[`${prefixCls}-align-${mergedAlign}`]: mergedAlign, [`${prefixCls}-align-${mergedAlign}`]: mergedAlign,
}; };
const itemClassName = `${prefixCls}-item`; const itemClassName = `${prefixCls}-item`;
const marginDirection = 'marginRight'; // directionConfig === 'rtl' ? 'marginLeft' : 'marginRight'; const marginDirection = 'marginRight'; // directionConfig === 'rtl' ? 'marginLeft' : 'marginRight';
return ( return (
<div class={someSpaceClass}> <div class={someSpaceClass}>
{items.map((child, i) => ( {items.map((child, i) => (
<div <div
class={itemClassName} class={itemClassName}
key={`${itemClassName}-${i}`} key={`${itemClassName}-${i}`}
style={ style={
i === len - 1 i === len - 1
? {} ? {}
: { : {
[direction === 'vertical' ? 'marginBottom' : marginDirection]: [direction === 'vertical' ? 'marginBottom' : marginDirection]:
typeof size === 'string' ? `${spaceSize[size]}px` : `${size}px`, typeof size === 'string' ? `${spaceSize[size]}px` : `${size}px`,
} }
} }
> >
{child} {child}
</div>
))}
</div> </div>
))} );
</div> };
); },
}; });
Space.displayName = 'ASpace';
/* istanbul ignore next */ /* istanbul ignore next */
Space.install = function(app: App) { Space.install = function(app: App) {
app.component(Space.displayName, Space); app.component(Space.name, Space);
return app; return app;
}; };

View File

@ -1,25 +1,26 @@
import { defineComponent } from 'vue';
import moment from 'moment'; import moment from 'moment';
import interopDefault from '../_util/interopDefault'; import interopDefault from '../_util/interopDefault';
import { initDefaultProps } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps';
import Statistic, { StatisticProps } from './Statistic'; import Statistic, { StatisticProps } from './Statistic';
import { formatCountdown } from './utils'; import { formatCountdown, countdownValueType, FormatConfig } from './utils';
const REFRESH_INTERVAL = 1000 / 30; const REFRESH_INTERVAL = 1000 / 30;
function getTime(value) { function getTime(value?: countdownValueType) {
return interopDefault(moment)(value).valueOf(); return interopDefault(moment)(value).valueOf();
} }
export default { export default defineComponent({
name: 'AStatisticCountdown', name: 'AStatisticCountdown',
props: initDefaultProps(StatisticProps, { props: initDefaultProps(StatisticProps, {
format: 'HH:mm:ss', format: 'HH:mm:ss',
}), }),
setup() {
created() { return {
this.countdownId = undefined; countdownId: undefined,
} as { countdownId: number };
}, },
mounted() { mounted() {
this.syncTimer(); this.syncTimer();
}, },
@ -46,7 +47,7 @@ export default {
startTimer() { startTimer() {
if (this.countdownId) return; if (this.countdownId) return;
this.countdownId = window.setInterval(() => { this.countdownId = window.setInterval(() => {
this.$refs.statistic.$forceUpdate(); (this.$refs.statistic as any).$forceUpdate();
this.syncTimer(); this.syncTimer();
}, REFRESH_INTERVAL); }, REFRESH_INTERVAL);
}, },
@ -64,7 +65,7 @@ export default {
} }
}, },
formatCountdown({ value, config }) { formatCountdown({ value, config }: { value: countdownValueType; config: FormatConfig }) {
const { format } = this.$props; const { format } = this.$props;
return formatCountdown(value, { ...config, format }); return formatCountdown(value, { ...config, format });
}, },
@ -84,4 +85,4 @@ export default {
/> />
); );
}, },
}; });

View File

@ -1,13 +1,18 @@
import padEnd from 'lodash-es/padEnd'; import padEnd from 'lodash-es/padEnd';
import { createVNode } from 'vue'; import { FunctionalComponent, VNodeTypes } from 'vue';
import { FormatConfig, valueType } from './utils';
const Number = (_, { attrs }) => { interface NumberProps extends FormatConfig {
const { value, formatter, precision, decimalSeparator, groupSeparator = '', prefixCls } = attrs; value: valueType;
let valueNode; }
const Number: FunctionalComponent<NumberProps> = props => {
const { value, formatter, precision, decimalSeparator, groupSeparator = '', prefixCls } = props;
let valueNode: VNodeTypes;
if (typeof formatter === 'function') { if (typeof formatter === 'function') {
// Customize formatter // Customize formatter
valueNode = formatter({ value, h: createVNode }); valueNode = formatter({ value });
} else { } else {
// Internal formatter // Internal formatter
const val = String(value); const val = String(value);

View File

@ -1,26 +1,30 @@
import { inject } from 'vue'; import { defineComponent, inject, PropType } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { getComponent, initDefaultProps } from '../_util/props-util'; import { getComponent } from '../_util/props-util';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import StatisticNumber from './Number'; import StatisticNumber from './Number';
import { countdownValueType } from './utils';
export const StatisticProps = { export const StatisticProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
decimalSeparator: PropTypes.string, decimalSeparator: PropTypes.string,
groupSeparator: PropTypes.string, groupSeparator: PropTypes.string,
format: PropTypes.string, format: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]), value: {
valueStyle: PropTypes.any, type: [String, Number, Object] as PropType<countdownValueType>,
},
valueStyle: PropTypes.style,
valueRender: PropTypes.any, valueRender: PropTypes.any,
formatter: PropTypes.any, formatter: PropTypes.any,
precision: PropTypes.number, precision: PropTypes.number,
prefix: PropTypes.any, prefix: PropTypes.VNodeChild,
suffix: PropTypes.any, suffix: PropTypes.VNodeChild,
title: PropTypes.any, title: PropTypes.VNodeChild,
onFinish: PropTypes.func, onFinish: PropTypes.func,
}; };
export default { export default defineComponent({
name: 'AStatistic', name: 'AStatistic',
props: initDefaultProps(StatisticProps, { props: initDefaultProps(StatisticProps, {
decimalSeparator: '.', decimalSeparator: '.',
@ -35,7 +39,7 @@ export default {
render() { render() {
const { prefixCls: customizePrefixCls, value = 0, valueStyle, valueRender } = this.$props; const { prefixCls: customizePrefixCls, value = 0, valueStyle, valueRender } = this.$props;
const getPrefixCls = this.configProvider.getPrefixCls; const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('statistic', customizePrefixCls); const prefixCls = getPrefixCls('statistic', customizePrefixCls);
const title = getComponent(this, 'title'); const title = getComponent(this, 'title');
@ -64,4 +68,4 @@ export default {
</div> </div>
); );
}, },
}; });

View File

@ -1,9 +1,10 @@
import Statistic from './Statistic'; import Statistic from './Statistic';
import Countdown from './Countdown'; import Countdown from './Countdown';
import { App } from 'vue';
Statistic.Countdown = Countdown; Statistic.Countdown = Countdown;
/* istanbul ignore next */ /* istanbul ignore next */
Statistic.install = function(app) { Statistic.install = function(app: App) {
app.component(Statistic.name, Statistic); app.component(Statistic.name, Statistic);
app.component(Statistic.Countdown.name, Statistic.Countdown); app.component(Statistic.Countdown.name, Statistic.Countdown);
return app; return app;

View File

@ -1,10 +1,32 @@
import { VNodeTypes } from 'vue';
import moment from 'moment'; import moment from 'moment';
import padStart from 'lodash-es/padStart'; import padStart from 'lodash-es/padStart';
import interopDefault from '../_util/interopDefault'; import interopDefault from '../_util/interopDefault';
export type valueType = number | string;
export type countdownValueType = valueType | string;
export type Formatter =
| false
| 'number'
| 'countdown'
| (({ value, config }: { value: valueType; config?: FormatConfig }) => VNodeTypes);
export interface FormatConfig {
formatter?: Formatter;
decimalSeparator?: string;
groupSeparator?: string;
precision?: number;
prefixCls?: string;
}
export interface CountdownFormatConfig extends FormatConfig {
format?: string;
}
// Countdown // Countdown
const timeUnits = [ const timeUnits: [string, number][] = [
['Y', 1000 * 60 * 60 * 24 * 365], // years ['Y', 1000 * 60 * 60 * 24 * 365], // years
['M', 1000 * 60 * 60 * 24 * 30], // months ['M', 1000 * 60 * 60 * 24 * 30], // months
['D', 1000 * 60 * 60 * 24], // days ['D', 1000 * 60 * 60 * 24], // days
@ -14,8 +36,8 @@ const timeUnits = [
['S', 1], // million seconds ['S', 1], // million seconds
]; ];
export function formatTimeStr(duration, format) { export function formatTimeStr(duration: number, format: string) {
let leftDuration = duration; let leftDuration: number = duration;
const escapeRegex = /\[[^\]]*\]/g; const escapeRegex = /\[[^\]]*\]/g;
const keepList = (format.match(escapeRegex) || []).map(str => str.slice(1, -1)); const keepList = (format.match(escapeRegex) || []).map(str => str.slice(1, -1));
@ -41,7 +63,7 @@ export function formatTimeStr(duration, format) {
}); });
} }
export function formatCountdown(value, config) { export function formatCountdown(value: countdownValueType, config: CountdownFormatConfig) {
const { format = '' } = config; const { format = '' } = config;
const target = interopDefault(moment)(value).valueOf(); const target = interopDefault(moment)(value).valueOf();
const current = interopDefault(moment)().valueOf(); const current = interopDefault(moment)().valueOf();

View File

@ -75,7 +75,6 @@ export const BaseProps = () => ({
// Options // Options
options: PropTypes.array, options: PropTypes.array,
children: PropTypes.array.def([]),
mode: PropTypes.string, mode: PropTypes.string,
// Value // Value

View File

@ -9,7 +9,7 @@ const Item: FunctionalComponent<ItemProps> = ({ setRef }, { slots }) => {
return children && children.length return children && children.length
? cloneVNode(children[0], { ? cloneVNode(children[0], {
ref: setRef, ref: setRef as any,
}) })
: children; : children;
}; };
@ -19,4 +19,5 @@ Item.props = {
default: () => {}, default: () => {},
}, },
}; };
export default Item; export default Item;

View File

@ -45,7 +45,7 @@ function renderChildren<T>(
}); });
const key = getKey(item); const key = getKey(item);
return ( return (
<Item key={key} setRef={ele => setNodeRef(item, ele)}> <Item key={key} setRef={ele => setNodeRef(item, ele as HTMLElement)}>
{node} {node}
</Item> </Item>
); );
@ -102,7 +102,7 @@ const List = defineComponent({
mergedData: computed(() => props.data || EMPTY_DATA) as any, mergedData: computed(() => props.data || EMPTY_DATA) as any,
}); });
const componentRef = ref<Element>(); const componentRef = ref<HTMLDivElement>();
// =============================== Item Key =============================== // =============================== Item Key ===============================
const getKey = (item: Record<string, any>) => { const getKey = (item: Record<string, any>) => {
@ -257,8 +257,8 @@ const List = defineComponent({
const removeEventListener = () => { const removeEventListener = () => {
if (componentRef.value) { if (componentRef.value) {
componentRef.value.removeEventListener('wheel', onRawWheel); componentRef.value.removeEventListener('wheel', onRawWheel);
componentRef.value.removeEventListener('DOMMouseScroll' as any, onFireFoxScroll); componentRef.value.removeEventListener('DOMMouseScroll', onFireFoxScroll as any);
componentRef.value.removeEventListener('MozMousePixelScroll', onMozMousePixelScroll); componentRef.value.removeEventListener('MozMousePixelScroll', onMozMousePixelScroll as any);
} }
}; };
watchEffect(() => { watchEffect(() => {
@ -266,8 +266,8 @@ const List = defineComponent({
if (componentRef.value) { if (componentRef.value) {
removeEventListener(); removeEventListener();
componentRef.value.addEventListener('wheel', onRawWheel); componentRef.value.addEventListener('wheel', onRawWheel);
componentRef.value.addEventListener('DOMMouseScroll' as any, onFireFoxScroll); componentRef.value.addEventListener('DOMMouseScroll', onFireFoxScroll as any);
componentRef.value.addEventListener('MozMousePixelScroll', onMozMousePixelScroll); componentRef.value.addEventListener('MozMousePixelScroll', onMozMousePixelScroll as any);
} }
}); });
}); });

View File

@ -4,7 +4,7 @@ const SMOOTH_PTG = 14 / 15;
export default function useMobileTouchMove( export default function useMobileTouchMove(
inVirtual: Ref<boolean>, inVirtual: Ref<boolean>,
listRef: Ref<Element | undefined>, listRef: Ref<HTMLDivElement | undefined>,
callback: (offsetY: number, smoothOffset?: boolean) => boolean, callback: (offsetY: number, smoothOffset?: boolean) => boolean,
) { ) {
let touched = false; let touched = false;