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

This commit is contained in:
tangjinzhou 2023-08-06 11:18:07 +08:00
commit 12074a0dca
12 changed files with 534 additions and 2 deletions

View File

@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/app/demo/basic.vue correctly 1`] = `
<div class="ant-app">
<!--teleport start-->
<!--teleport end-->
<!--teleport start-->
<!--teleport end-->
<div class="ant-space ant-space-horizontal ant-space-align-center">
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open message</span>
</button></div>
<!---->
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open modal</span>
</button></div>
<!---->
<div class="ant-space-item"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open notification</span>
</button></div>
<!---->
</div>
</div>
`;
exports[`renders ./components/app/demo/myPage.vue correctly 1`] = `
<div class="ant-space ant-space-horizontal ant-space-align-center">
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open message</span>
</button></div>
<!---->
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open modal</span>
</button></div>
<!---->
<div class="ant-space-item"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open notification</span>
</button></div>
<!---->
</div>
`;

View File

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('app');

44
components/app/context.ts Normal file
View File

@ -0,0 +1,44 @@
import { provide, inject, reactive } from 'vue';
import type { InjectionKey } from 'vue';
import type { MessageInstance, ConfigOptions as MessageConfig } from '../message/interface';
import type { NotificationInstance, NotificationConfig } from '../notification/interface';
import type { ModalStaticFunctions } from '../modal/confirm';
export type AppConfig = {
message?: MessageConfig;
notification?: NotificationConfig;
};
export const AppConfigContextKey: InjectionKey<AppConfig> = Symbol('appConfigContext');
export const useProvideAppConfigContext = (appConfigContext: AppConfig) => {
return provide(AppConfigContextKey, appConfigContext);
};
export const useInjectAppConfigContext = () => {
return inject(AppConfigContextKey, {});
};
type ModalType = Omit<ModalStaticFunctions, 'warn'>;
export interface useAppProps {
message: MessageInstance;
notification: NotificationInstance;
modal: ModalType;
}
export const AppContextKey: InjectionKey<useAppProps> = Symbol('appContext');
export const useProvideAppContext = (appContext: useAppProps) => {
return provide(AppContextKey, appContext);
};
const defaultAppContext: useAppProps = reactive({
message: {},
notification: {},
modal: {},
} as useAppProps);
export const useInjectAppContext = () => {
return inject(AppContextKey, defaultAppContext);
};

View File

@ -0,0 +1,26 @@
<docs>
---
order: 0
title:
zh-CN: 基本使用
en-US: Basic Usage
---
## zh-CN
获取 `message``notification``modal` 静态方法
## en-US
Static method for `message`, `notification`, `modal`.
</docs>
<template>
<a-app>
<my-page />
</a-app>
</template>
<script lang="ts" setup>
import myPage from './myPage.vue';
</script>

View File

@ -0,0 +1,19 @@
<template>
<demo-sort :cols="1">
<basic />
</demo-sort>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import Basic from './basic.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
export default defineComponent({
CN,
US,
components: {
Basic,
},
});
</script>

View File

@ -0,0 +1,32 @@
<template>
<a-space>
<a-button type="primary" @click="showMessage">Open message</a-button>
<a-button type="primary" @click="showModal">Open modal</a-button>
<a-button type="primary" @click="showNotification">Open notification</a-button>
</a-space>
</template>
<script setup lang="ts">
import { App } from 'ant-design-vue';
const { message, modal, notification } = App.useApp();
const showMessage = () => {
message.success('Success!');
};
const showModal = () => {
modal.warning({
title: 'This is a warning message',
content: 'some messages...some messages...',
});
};
const showNotification = () => {
notification.info({
message: `Notification topLeft`,
description: 'Hello, Ant Design Vue!!',
placement: 'topLeft',
});
};
</script>

View File

@ -0,0 +1,129 @@
---
category: Components
cols: 1
type: Other
title: App
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
---
Application wrapper for some global usages.
## When To Use
- Provide reset styles based on `.ant-app` element.
- You could use static methods of `message/notification/Modal` form `useApp` without writing `contextHolder` manually.
## API
### App
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| message | Global config for Message | [MessageConfig](/components/message/#messageconfig) | - | 4.x |
| notification | Global config for Notification | [NotificationConfig](/components/notification/#notificationconfig) | - | 4.x |
## How to use
### Basic usage
App provides upstream and downstream method calls through `provide/inject`, because useApp needs to be used as a subcomponent, we recommend encapsulating App at the top level in the application.
```html
/*myPage.vue*/
<template>
<a-space>
<a-button type="primary" @click="showMessage">Open message</a-button>
<a-button type="primary" @click="showModal">Open modal</a-button>
<a-button type="primary" @click="showNotification">Open notification</a-button>
</a-space>
</template>
<script setup lang="ts">
import { App } from 'ant-design-vue';
const { message, modal, notification } = App.useApp();
const showMessage = () => {
message.success('Success!');
};
const showModal = () => {
modal.warning({
title: 'This is a warning message',
content: 'some messages...some messages...',
});
};
const showNotification = () => {
notification.info({
message: `Notification topLeft`,
description: 'Hello, Ant Design Vue!!',
placement: 'topLeft',
});
};
</script>
```
Note: App.useApp must be available under App.
#### Embedded usage scenarios (if not necessary, try not to do nesting)
```html
<a-app>
<a-space>
...
<a-app>...</a-app>
</a-space>
</a-app>
```
#### Sequence with ConfigProvider
The App component can only use the token in the `ConfigProvider`, if you need to use the Token, the ConfigProvider and the App component must appear in pairs.
```html
<a-config-provider theme="{{ ... }}">
<a-app>...</a-app>
</a-config-provider>
```
#### Global scene (pinia scene)
```ts
import { App } from 'ant-design-vue';
import type { MessageInstance } from 'ant-design-vue/es/message/interface';
import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm';
import type { NotificationInstance } from 'ant-design-vue/es/notification/interface';
export const useGloablStore = defineStore('global', () => {
const message: MessageInstance = ref();
const notification: NotificationInstance = ref();
const modal: Omit<ModalStaticFunctions, 'warn'> = ref();
(() => {
const staticFunction = App.useApp();
message.value = staticFunction.message;
modal.value = staticFunction.modal;
notification.value = staticFunction.notification;
})();
return { message, notification, modal };
});
```
```html
// sub page
<template>
<a-space>
<a-button type="primary" @click="showMessage">Open message</a-button>
</a-space>
</template>
<script setup>
import { useGlobalStore } from '@/stores/global';
const global = useGlobalStore();
const showMessage = () => {
global.message.success('Success!');
};
</script>
```

83
components/app/index.tsx Normal file
View File

@ -0,0 +1,83 @@
import { defineComponent, computed } from 'vue';
import type { App as TypeApp, Plugin } from 'vue';
import { initDefaultProps } from '../_util/props-util';
import classNames from '../_util/classNames';
import { objectType } from '../_util/type';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import useMessage from '../message/useMessage';
import useModal from '../modal/useModal';
import useNotification from '../notification/useNotification';
import type { AppConfig } from './context';
import {
useProvideAppConfigContext,
useInjectAppConfigContext,
useProvideAppContext,
useInjectAppContext,
} from './context';
import useStyle from './style';
export const AppProps = () => {
return {
rootClassName: String,
message: objectType<AppConfig['message']>(),
notification: objectType<AppConfig['notification']>(),
};
};
const useApp = () => {
return useInjectAppContext();
};
const App = defineComponent({
name: 'AApp',
props: initDefaultProps(AppProps(), {}),
setup(props, { slots }) {
const { prefixCls } = useConfigInject('app', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const customClassName = computed(() => {
return classNames(hashId.value, prefixCls.value, props.rootClassName);
});
const appConfig = useInjectAppConfigContext();
const mergedAppConfig = computed(() => ({
message: { ...appConfig.message, ...props.message },
notification: { ...appConfig.notification, ...props.notification },
}));
useProvideAppConfigContext(mergedAppConfig.value);
const [messageApi, messageContextHolder] = useMessage(mergedAppConfig.value.message);
const [notificationApi, notificationContextHolder] = useNotification(
mergedAppConfig.value.notification,
);
const [ModalApi, ModalContextHolder] = useModal();
const memoizedContextValue = computed(() => ({
message: messageApi,
notification: notificationApi,
modal: ModalApi,
}));
useProvideAppContext(memoizedContextValue.value);
return () => {
return wrapSSR(
<div class={customClassName.value}>
{ModalContextHolder()}
{messageContextHolder()}
{notificationContextHolder()}
{slots.default?.()}
</div>,
);
};
},
});
App.useApp = useApp;
App.install = function (app: TypeApp) {
app.component(App.name, App);
};
export default App as typeof App &
Plugin & {
readonly useApp: typeof useApp;
};

View File

@ -0,0 +1,130 @@
---
category: Components
subtitle: 包裹组件
cols: 1
type: 其它
title: App
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
---
新的包裹组件,提供重置样式和提供消费上下文的默认环境。
## 何时使用
- 提供可消费 provide/inject 的 `message.xxx`、`Modal.xxx`、`notification.xxx` 的静态方法,可以简化 useMessage 等方法需要手动植入 `contextHolder` 的问题。
- 提供基于 `.ant-app` 的默认重置样式,解决原生元素没有 antd 规范样式的问题。
## API
### App
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| message | App 内 Message 的全局配置 | [MessageConfig](/components/message-cn/#messageconfig) | - | 4.x |
| notification | App 内 Notification 的全局配置 | [NotificationConfig](/components/notification-cn/#notificationconfig) | - | 4.x |
## 如何使用
### 基础用法
App 组件通过 `provide/inject` 提供上下文方法调用,因而 useApp 需要作为子组件才能使用,我们推荐在应用中顶层包裹 App。
```html
/*myPage.vue*/
<template>
<a-space>
<a-button type="primary" @click="showMessage">Open message</a-button>
<a-button type="primary" @click="showModal">Open modal</a-button>
<a-button type="primary" @click="showNotification">Open notification</a-button>
</a-space>
</template>
<script setup lang="ts">
import { App } from 'ant-design-vue';
const { message, modal, notification } = App.useApp();
const showMessage = () => {
message.success('Success!');
};
const showModal = () => {
modal.warning({
title: 'This is a warning message',
content: 'some messages...some messages...',
});
};
const showNotification = () => {
notification.info({
message: `Notification topLeft`,
description: 'Hello, Ant Design Vue!!',
placement: 'topLeft',
});
};
</script>
```
注意App.useApp 必须在 App 之下方可使用。
#### 内嵌使用场景(如无必要,尽量不做嵌套)
```html
<a-app>
<a-space>
...
<a-app>...</a-app>
</a-space>
</a-app>
```
#### 与 ConfigProvider 先后顺序
App 组件只能在 `ConfigProvider` 之下才能使用 Design Token 如果需要使用其样式重置能力,则 ConfigProvider 与 App 组件必须成对出现。
```html
<a-config-provider theme="{{ ... }}">
<a-app>...</a-app>
</a-config-provider>
```
#### 全局场景 (pinia 场景)
```ts
import { App } from 'ant-design-vue';
import type { MessageInstance } from 'ant-design-vue/es/message/interface';
import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm';
import type { NotificationInstance } from 'ant-design-vue/es/notification/interface';
export const useGloablStore = defineStore('global', () => {
const message: MessageInstance = ref();
const notification: NotificationInstance = ref();
const modal: Omit<ModalStaticFunctions, 'warn'> = ref();
(() => {
const staticFunction = App.useApp();
message.value = staticFunction.message;
modal.value = staticFunction.modal;
notification.value = staticFunction.notification;
})();
return { message, notification, modal };
});
```
```html
// sub page
<template>
<a-space>
<a-button type="primary" @click="showMessage">Open message</a-button>
</a-space>
</template>
<script setup>
import { useGlobalStore } from '@/stores/global';
const global = useGlobalStore();
const showMessage = () => {
global.message.success('Success!');
};
</script>
```

View File

@ -0,0 +1,22 @@
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook } from '../../theme/internal';
export type ComponentToken = {};
interface AppToken extends FullToken<'App'> {}
// =============================== Base ===============================
const genBaseStyle: GenerateStyle<AppToken> = token => {
const { componentCls, colorText, fontSize, lineHeight, fontFamily } = token;
return {
[componentCls]: {
color: colorText,
fontSize,
lineHeight,
fontFamily,
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook('App', token => [genBaseStyle(token)]);

View File

@ -261,3 +261,6 @@ export { default as QRCode } from './qrcode';
export type { TourProps, TourStepProps } from './tour';
export { default as Tour } from './tour';
export type { AppProps } from './app';
export { default as App } from './app';

View File

@ -46,7 +46,7 @@ import type { ComponentToken as TypographyComponentToken } from '../../typograph
import type { ComponentToken as UploadComponentToken } from '../../upload/style';
import type { ComponentToken as TourComponentToken } from '../../tour/style';
import type { ComponentToken as QRCodeComponentToken } from '../../qrcode/style';
// import type { ComponentToken as AppComponentToken } from '../../app/style';
import type { ComponentToken as AppComponentToken } from '../../app/style';
import type { ComponentToken as WaveToken } from '../../_util/wave/style';
export interface ComponentTokenMap {
@ -112,7 +112,7 @@ export interface ComponentTokenMap {
Progress?: ProgressComponentToken;
Tour?: TourComponentToken;
QRCode?: QRCodeComponentToken;
// App?: AppComponentToken;
App?: AppComponentToken;
// /** @private Internal TS definition. Do not use. */
Wave?: WaveToken;