Merge remote-tracking branch 'origin/main' into feat-v4

pull/6514/head
tangjinzhou 2023-04-23 11:02:19 +08:00
commit b0990b7323
22 changed files with 128 additions and 63 deletions

View File

@ -5,10 +5,12 @@ import { nextTick } from 'vue';
import { asyncExpect, sleep } from '../../../tests/utils';
import mountTest from '../../../tests/shared/mountTest';
import { resetWarned } from '../../_util/warning';
import focusTest from '../../../tests/shared/focusTest';
describe('Button', () => {
mountTest(Button);
mountTest(Button.Group);
focusTest(Button);
it('renders correctly', () => {
const wrapper = mount({
render() {

View File

@ -38,7 +38,7 @@ export default defineComponent({
props: initDefaultProps(buttonProps(), { type: 'default' }),
slots: ['icon'],
// emits: ['click', 'mousedown'],
setup(props, { slots, attrs, emit }) {
setup(props, { slots, attrs, emit, expose }) {
const { prefixCls, autoInsertSpaceInButton, direction, size } = useConfigInject('btn', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const groupSizeContext = GroupSizeContext.useInject();
@ -157,6 +157,17 @@ export default defineComponent({
delayTimeoutRef.value && clearTimeout(delayTimeoutRef.value);
});
const focus = () => {
buttonNodeRef.value?.focus();
};
const blur = () => {
buttonNodeRef.value?.blur();
};
expose({
focus,
blur,
});
return () => {
const { icon = slots.icon?.() } = props;
const children = flattenChildren(slots.default?.());

View File

@ -56,6 +56,15 @@ It accepts all props which native buttons support.
## FAQ
### Methods
#### Checkbox
| Name | Description | Version |
| ------- | ------------ | ------- |
| blur() | remove focus | |
| focus() | get focus | |
### How to remove space between 2 chinese characters
Following the Ant Design specification, we will add one space between if Button (exclude Text button and Link button) contains two Chinese characters only. If you don't need that, you can use [ConfigProvider](/components/config-provider/#api) to set `autoInsertSpaceInButton` as `false`.

View File

@ -57,6 +57,15 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Lp1kTYmSsgoAAA
支持原生 button 的其他所有属性。
### 方法
#### Button
| 名称 | 描述 | 版本 |
| ------- | -------- | ---- |
| blur() | 移除焦点 | |
| focus() | 获取焦点 | |
## FAQ
### 如何移除 2 个汉字之间的空格

View File

@ -211,3 +211,20 @@ Please refer [replace date](/docs/vue/replace-date)
### Why config dayjs.locale globally not work?
DatePicker default set `locale` as `en` in v4. You can config DatePicker `locale` prop or [ConfigProvider `locale`](/components/config-provider) prop instead.
### How to modify start day of week?
Please use correct [language](/docs/vue/i18n) ([#5605](https://github.com/ant-design/ant-design/issues/5605)), or update dayjs `locale` config:
- Example: <https://codesandbox.io/s/dayjs-day-of-week-x9tuj2?file=/demo.tsx>
```js
import dayjs from 'dayjs';
import updateLocale from 'dayjs/plugin/updateLocale';
import 'dayjs/locale/zh-cn';
dayjs.extend(updateLocale);
dayjs.updateLocale('zh-cn', {
weekStart: 0,
});
```

View File

@ -212,3 +212,18 @@ export type FormatType = Generic | GenericFn | Array<Generic | GenericFn>;
### 为何全局修改 dayjs.locale 不生效?
DatePicker 默认 `locale``en`。你可以通过 DatePicker 的 `locale` 属性来单独设置,也可以通过 [ConfigProvider `locale`](/components/config-provider-cn) 属性来配置。
### 如何修改周的起始日?
请使用正确的[语言包](/docs/vue/i18n-cn)[#5605](https://github.com/ant-design/ant-design/issues/5605)),或者修改 dayjs 的 `locale` 配置:<https://codesandbox.io/s/dayjs-day-of-week-x9tuj2?file=/demo.tsx>
```js
import dayjs from 'dayjs';
import updateLocale from 'dayjs/plugin/updateLocale';
import 'dayjs/locale/zh-cn';
dayjs.extend(updateLocale);
dayjs.updateLocale('zh-cn', {
weekStart: 0,
});
```

View File

@ -34,12 +34,12 @@ import { warning } from '../vc-util/warning';
import find from 'lodash-es/find';
import { tuple } from '../_util/type';
import type {
FormLabelAlign,
InternalNamePath,
Rule,
RuleError,
RuleObject,
ValidateOptions,
FormLabelAlign,
} from './interface';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import { useInjectForm } from './context';
@ -113,10 +113,7 @@ export const formItemProps = () => ({
wrapperCol: { type: Object as PropType<ColProps & HTMLAttributes> },
hasFeedback: { type: Boolean, default: false },
colon: { type: Boolean, default: undefined },
labelAlign: {
...PropTypes.oneOf(tuple('left', 'right')),
type: String as PropType<FormLabelAlign>,
},
labelAlign: String as PropType<FormLabelAlign>,
prop: { type: [String, Number, Array] as PropType<string | number | Array<string | number>> },
name: { type: [String, Number, Array] as PropType<string | number | Array<string | number>> },
rules: [Array, Object] as PropType<Rule[] | Rule>,
@ -237,7 +234,7 @@ export default defineComponent({
if (typeof props.label === 'string') {
variables.label = props.label;
} else if (props.name) {
variables.label = String(name);
variables.label = String(props.name);
}
if (props.messageVariables) {
variables = { ...variables, ...props.messageVariables };

View File

@ -2,13 +2,13 @@ import type { App, PropType, ExtractPropTypes } from 'vue';
import { computed, watch, shallowRef, defineComponent } from 'vue';
import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types';
import VcMentions, { Option } from '../vc-mentions';
import VcMentions from '../vc-mentions';
import { mentionsProps as baseMentionsProps } from '../vc-mentions/src/mentionsProps';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import { flattenChildren, getOptionProps } from '../_util/props-util';
import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext';
import omit from '../_util/omit';
import { optionProps } from '../vc-mentions/src/Option';
import { optionProps, optionOptions } from '../vc-mentions/src/Option';
import type { KeyboardEventHandler } from '../_util/EventInterface';
import type { InputStatus } from '../_util/statusUtils';
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
@ -279,7 +279,7 @@ const Mentions = defineComponent({
/* istanbul ignore next */
export const MentionsOption = defineComponent({
compatConfig: { MODE: 3 },
...(Option as any),
...optionOptions,
name: 'AMentionsOption',
props: optionProps,
});

View File

@ -2,7 +2,6 @@ import Table, { tableProps } from './Table';
import Column from './Column';
import ColumnGroup from './ColumnGroup';
import type { TableProps, TablePaginationConfig } from './Table';
import { defineComponent } from 'vue';
import type { App } from 'vue';
import { EXPAND_COLUMN, Summary, SummaryCell, SummaryRow } from '../vc-table';
import {
@ -16,8 +15,8 @@ export type { ColumnProps } from './Column';
export type { ColumnsType, ColumnType, ColumnGroupType } from './interface';
export type { TableProps, TablePaginationConfig };
const TableSummaryRow = defineComponent({ ...(SummaryRow as any), name: 'ATableSummaryRow' });
const TableSummaryCell = defineComponent({ ...(SummaryCell as any), name: 'ATableSummaryCell' });
const TableSummaryRow = SummaryRow;
const TableSummaryCell = SummaryCell;
const TableSummary = Object.assign(Summary, {
Cell: TableSummaryCell,

View File

@ -78,14 +78,12 @@ function isChecked(selectedKeys: (string | number)[], eventKey: string | number)
return selectedKeys.indexOf(eventKey) !== -1;
}
function handleTreeData(data: TransferProps['dataSource'], targetKeys: string[] = []) {
data.forEach(item => {
item['disabled'] = targetKeys.includes(item.key as any);
if (item.children) {
handleTreeData(item.children, targetKeys);
}
});
return data as TreeProps['treeData'];
function handleTreeData(treeNodes: TransferProps['dataSource'], targetKeys: string[] = []) {
return treeNodes.map(({ children, ...props }) => ({
...props,
disabled: targetKeys.includes(props.key as string),
children: handleTreeData(children ?? [], targetKeys),
}));
}
export default defineComponent({

View File

@ -1,9 +1,7 @@
import type { App } from 'vue';
import { defineComponent } from 'vue';
import Tree from './Tree';
import { TreeNode as VcTreeNode } from '../vc-tree';
import DirectoryTree from './DirectoryTree';
import { treeNodeProps } from '../vc-tree/props';
export type { EventDataNode, DataNode } from '../vc-tree/interface';
@ -26,11 +24,7 @@ export type {
/* istanbul ignore next */
const TreeNode = defineComponent({
...(VcTreeNode as any),
name: 'ATreeNode',
props: treeNodeProps,
});
const TreeNode = VcTreeNode;
export { DirectoryTree, TreeNode };

View File

@ -17,11 +17,14 @@ export type BaseOptionsProps = Partial<ExtractPropTypes<typeof baseOptionsProps>
export type OptionProps = Partial<ExtractPropTypes<typeof optionProps>> & Partial<HTMLAttributes>;
export default defineComponent({
compatConfig: { MODE: 3 },
export const optionOptions = {
name: 'Option',
props: optionProps,
render(_props: any, { slots }: any) {
return slots.default?.();
},
};
export default defineComponent({
compatConfig: { MODE: 3 },
...optionOptions,
});

View File

@ -331,7 +331,11 @@ function RangerPicker<DateType>() {
// Fill disabled unit
for (let i = 0; i < 2; i += 1) {
if (mergedDisabled[i] && !getValue(postValues, i) && !getValue(props.allowEmpty, i)) {
if (
mergedDisabled.value[i] &&
!getValue(postValues, i) &&
!getValue(props.allowEmpty, i)
) {
postValues = updateValues(postValues, props.generateConfig.getNow(), i);
}
}

View File

@ -873,7 +873,7 @@ export default defineComponent({
style={{
width: 0,
height: 0,
display: 'flex',
position: 'absolute',
overflow: 'hidden',
opacity: 0,
}}

View File

@ -13,7 +13,7 @@ export interface SummaryCellProps {
}
export default defineComponent<SummaryCellProps>({
name: 'SummaryCell',
name: 'ATableSummaryCell',
props: ['index', 'colSpan', 'rowSpan', 'align'] as any,
setup(props, { attrs, slots }) {
const tableContext = useInjectTable();

View File

@ -2,7 +2,7 @@ import { defineComponent } from 'vue';
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'FooterRow',
name: 'ATableSummaryRow',
setup(_props, { slots }) {
return () => <tr>{slots.default?.()}</tr>;
},

View File

@ -25,7 +25,7 @@ const defaultTitle = '---';
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'TreeNode',
name: 'ATreeNode',
inheritAttrs: false,
props: treeNodeProps,
isTreeNode: 1,

View File

@ -225,8 +225,13 @@ const List = defineComponent({
offset: undefined,
});
}
if (componentRef.value) {
state.scrollTop = componentRef.value.scrollTop;
}
},
{
immediate: true,
},
{ immediate: true },
);
watch(
[
@ -276,11 +281,11 @@ const List = defineComponent({
itemTop = currentItemBottom;
}
// Fallback to normal if not match. This code should never reach
/* istanbul ignore next */
// When scrollTop at the end but data cut to small count will reach this
if (startIndex === undefined) {
startIndex = 0;
startOffset = 0;
endIndex = Math.ceil(height / itemHeight);
}
if (endIndex === undefined) {
endIndex = dataLen - 1;
@ -324,7 +329,7 @@ const List = defineComponent({
// When data size reduce. It may trigger native scroll event back to fit scroll position
function onFallbackScroll(e: UIEvent) {
const { scrollTop: newScrollTop } = e.currentTarget as Element;
if (Math.abs(newScrollTop - state.scrollTop) >= 1) {
if (newScrollTop !== state.scrollTop) {
syncScrollTop(newScrollTop);
}

View File

@ -119,18 +119,19 @@ export default defineComponent({
this.onScrollbarTouchStart,
supportsPassive ? ({ passive: false } as EventListenerOptions) : false,
);
this.thumbRef.current.removeEventListener(
'touchstart',
this.onMouseDown,
supportsPassive ? ({ passive: false } as EventListenerOptions) : false,
);
this.thumbRef.current.removeEventListener(
'touchmove',
this.onMouseMove,
supportsPassive ? ({ passive: false } as EventListenerOptions) : false,
);
this.thumbRef.current.removeEventListener('touchend', this.onMouseUp);
if (this.thumbRef.current) {
this.thumbRef.current.removeEventListener(
'touchstart',
this.onMouseDown,
supportsPassive ? ({ passive: false } as EventListenerOptions) : false,
);
this.thumbRef.current.removeEventListener(
'touchmove',
this.onMouseMove,
supportsPassive ? ({ passive: false } as EventListenerOptions) : false,
);
this.thumbRef.current.removeEventListener('touchend', this.onMouseUp);
}
raf.cancel(this.moveRaf);
},

View File

@ -1,6 +1,7 @@
import type { VNodeProps, Ref, ShallowRef } from 'vue';
import { watch, ref } from 'vue';
import { onUnmounted, watch, ref } from 'vue';
import type { GetKey } from '../interface';
import wrapperRaf from '../../_util/raf';
export type CacheMap = Map<any, number>;
@ -16,14 +17,14 @@ export default function useHeights<T>(
watch(mergedData, () => {
updatedMark.value = Symbol('update');
});
let heightUpdateId = 0;
let collectRaf: number = undefined;
function cancelRaf() {
wrapperRaf.cancel(collectRaf);
}
function collectHeight() {
heightUpdateId += 1;
const currentId = heightUpdateId;
Promise.resolve().then(() => {
// Only collect when it's latest call
if (currentId !== heightUpdateId) return;
// let changed = false;
cancelRaf();
collectRaf = wrapperRaf(() => {
instance.forEach((element, key) => {
if (element && element.offsetParent) {
const { offsetHeight } = element;
@ -57,6 +58,9 @@ export default function useHeights<T>(
}
}
}
onUnmounted(() => {
cancelRaf();
});
return [setInstance, collectHeight, heights, updatedMark];
}

View File

@ -103,7 +103,7 @@ export default function useScrollTo(
collectHeight();
}
syncScroll(times - 1, newTargetAlign);
});
}, 2);
};
syncScroll(5);

View File

@ -1,7 +1,6 @@
export function isZhCN(name) {
return /-cn\/?$/.test(name);
}
export function isLocalStorageNameSupported() {
const testKey = 'test';
const storage = window.localStorage;
@ -13,7 +12,6 @@ export function isLocalStorageNameSupported() {
return false;
}
}
export function getLocalizedPathname(path, zhCN, query = {}, hash) {
const pathname = path.startsWith('/') ? path : `/${path}`;
let fullPath;
@ -27,7 +25,6 @@ export function getLocalizedPathname(path, zhCN, query = {}, hash) {
} else {
fullPath = `${pathname}-cn`;
}
if (hash) {
const localHash = hash[zhCN ? 'zhCN' : 'enUS'];
fullPath += `#${localHash}`;