Skip to content

Commit c30f3de

Browse files
committed
feat: add app components
1 parent 698c0ff commit c30f3de

File tree

12 files changed

+489
-2
lines changed

12 files changed

+489
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`renders ./components/app/demo/basic.vue correctly 1`] = `
4+
<div class="ant-app">
5+
<!--teleport start-->
6+
<!--teleport end-->
7+
<!--teleport start-->
8+
<!--teleport end-->
9+
<!---->
10+
<div class="ant-space ant-space-horizontal ant-space-align-center">
11+
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
12+
<!----><span>Open message</span>
13+
</button></div>
14+
<!---->
15+
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
16+
<!----><span>Open modal</span>
17+
</button></div>
18+
<!---->
19+
<div class="ant-space-item"><button class="ant-btn ant-btn-primary" type="button">
20+
<!----><span>Open notification</span>
21+
</button></div>
22+
<!---->
23+
</div>
24+
</div>
25+
`;
26+
27+
exports[`renders ./components/app/demo/myPage.vue correctly 1`] = `
28+
<div class="ant-space ant-space-horizontal ant-space-align-center">
29+
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
30+
<!----><span>Open message</span>
31+
</button></div>
32+
<!---->
33+
<div class="ant-space-item" style="margin-right: 8px;"><button class="ant-btn ant-btn-primary" type="button">
34+
<!----><span>Open modal</span>
35+
</button></div>
36+
<!---->
37+
<div class="ant-space-item"><button class="ant-btn ant-btn-primary" type="button">
38+
<!----><span>Open notification</span>
39+
</button></div>
40+
<!---->
41+
</div>
42+
`;

components/app/__tests__/demo.test.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import demoTest from '../../../tests/shared/demoTest';
2+
3+
demoTest('app');

components/app/context.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { reactive, provide, inject } from 'vue';
2+
import type { InjectionKey } from 'vue';
3+
import type { MessageInstance, ConfigOptions as MessageConfig } from '../message/interface';
4+
import type { NotificationInstance, NotificationConfig } from '../notification/interface';
5+
import type { ModalStaticFunctions } from '../modal/confirm';
6+
7+
export type AppConfig = {
8+
message?: MessageConfig;
9+
notification?: NotificationConfig;
10+
};
11+
12+
export const AppConfigContextKey: InjectionKey<AppConfig> = Symbol('appConfigContext');
13+
14+
export const useProvideAppConfigContext = (appConfigContext: AppConfig) => {
15+
return provide(AppConfigContextKey, appConfigContext);
16+
};
17+
18+
export const useInjectAppConfigContext = () => {
19+
return inject(AppConfigContextKey, {});
20+
};
21+
22+
type ModalType = Omit<ModalStaticFunctions, 'warn'>;
23+
export interface useAppProps {
24+
message: MessageInstance;
25+
notification: NotificationInstance;
26+
modal: ModalType;
27+
}
28+
29+
export const AppContextKey: InjectionKey<useAppProps> = Symbol('appContext');
30+
31+
export const useProvideAppContext = (appContext: useAppProps) => {
32+
return provide(AppContextKey, appContext);
33+
};
34+
35+
const defaultAppContext: useAppProps = reactive({
36+
message: {},
37+
notification: {},
38+
modal: {},
39+
} as useAppProps);
40+
41+
export const useInjectAppContext = () => {
42+
return inject(AppContextKey, defaultAppContext);
43+
};

components/app/demo/basic.vue

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<docs>
2+
---
3+
order: 0
4+
title:
5+
zh-CN: 基本使用
6+
en-US: Basic Usage
7+
---
8+
9+
## zh-CN
10+
11+
获取 `message`, `notification`, `modal` 静态方法。
12+
13+
## en-US
14+
15+
Static method for `message`, `notification`, `modal`.
16+
</docs>
17+
18+
<template>
19+
<a-app>
20+
<my-page></my-page>
21+
</a-app>
22+
</template>
23+
24+
<script setup>
25+
import myPage from './myPage.vue';
26+
</script>

components/app/demo/index.vue

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<demo-sort :cols="1">
3+
<basic />
4+
</demo-sort>
5+
</template>
6+
7+
<script lang="ts">
8+
import Basic from './basic.vue';
9+
import CN from '../index.zh-CN.md';
10+
import US from '../index.en-US.md';
11+
import { defineComponent } from 'vue';
12+
13+
export default defineComponent({
14+
CN,
15+
US,
16+
components: {
17+
Basic,
18+
},
19+
setup() {
20+
return {};
21+
},
22+
});
23+
</script>

components/app/demo/myPage.vue

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<template>
2+
<a-space>
3+
<a-button type="primary" @click="showMessage">Open message</a-button>
4+
<a-button type="primary" @click="showModal">Open modal</a-button>
5+
<a-button type="primary" @click="showNotification">Open notification</a-button>
6+
</a-space>
7+
</template>
8+
9+
<script setup>
10+
import { App } from 'ant-design-vue';
11+
12+
const { message, modal, notification } = App.useApp();
13+
14+
const showMessage = () => {
15+
message.success('Success!');
16+
};
17+
18+
const showModal = () => {
19+
modal.warning({
20+
title: 'This is a warning message',
21+
content: 'some messages...some messages...',
22+
});
23+
};
24+
25+
const showNotification = () => {
26+
notification.info({
27+
message: `Notification topLeft`,
28+
description: 'Hello, Ant Design!!',
29+
placement: 'topLeft',
30+
});
31+
};
32+
</script>

components/app/index.en-US.md

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
category: Components
3+
group: Other
4+
title: App
5+
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HJz8SZos2wgAAAAAAAAAAAAADrJ8AQ/original
6+
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*oC92TK44Ex8AAAAAAAAAAAAADrJ8AQ/original
7+
demo:
8+
cols: 2
9+
---
10+
11+
New App Component which provide global style & static function replacement.
12+
13+
## When To Use
14+
15+
- Provide reset styles based on `.ant-app` element.
16+
- You could use static methods of `message/notification/Modal` form `useApp` without put `contextHolder` mannully.
17+
18+
## How to use
19+
20+
### Basic usage
21+
22+
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.
23+
24+
```html
25+
<template>
26+
<a-app>
27+
<my-page></my-page>
28+
</a-app>
29+
</template>
30+
<script>
31+
import myPage from './myPage.vue';
32+
</script>
33+
34+
// myPage
35+
<template>
36+
<a-space>
37+
<a-button type="primary" @click="showMessage">Open message</a-button>
38+
<a-button type="primary" @click="showModal">Open modal</a-button>
39+
<a-button type="primary" @click="showNotification">Open notification</a-button>
40+
</a-space>
41+
</template>
42+
43+
<script setup>
44+
import { App } from 'ant-design-vue';
45+
46+
const { message, modal, notification } = App.useApp();
47+
48+
const showMessage = () => {
49+
message.success('Success!');
50+
};
51+
52+
const showModal = () => {
53+
modal.warning({
54+
title: 'This is a warning message',
55+
content: 'some messages...some messages...',
56+
});
57+
};
58+
59+
const showNotification = () => {
60+
notification.info({
61+
message: `Notification topLeft`,
62+
description: 'Hello, Ant Design Vue!!',
63+
placement: 'topLeft',
64+
});
65+
};
66+
</script>
67+
```
68+
69+
Note: App.useApp must be available under App.
70+
71+
### Sequence with ConfigProvider
72+
73+
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.
74+
75+
```html
76+
<a-config-provider theme="{{" ... }}>
77+
<a-app>...</a-app>
78+
</a-config-provider>
79+
```
80+
81+
### Embedded usage scenarios (if not necessary, try not to do nesting)
82+
83+
```html
84+
<a-app>
85+
<a-space>
86+
...
87+
<a-app>...</a-app>
88+
</a-space>
89+
</a-app>
90+
```
91+
92+
## API
93+
94+
### App
95+
96+
| Property | Description | Type | Default |
97+
| --- | --- | --- | --- |
98+
| message | Global config for Message | [MessageConfig](/components/message/#messageconfig) | - |
99+
| notification | Global config for Notification | [NotificationConfig](/components/notification/#notificationconfig) | - |

components/app/index.tsx

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { defineComponent, computed } from 'vue';
2+
import type { PropType, App as TypeApp, Plugin } from 'vue';
3+
import { initDefaultProps } from '../_util/props-util';
4+
import classNames from '../_util/classNames';
5+
import type { VueNode } from '../_util/type';
6+
import { objectType } from '../_util/type';
7+
import useConfigInject from '../config-provider/hooks/useConfigInject';
8+
import useMessage from '../message/useMessage';
9+
import useModal from '../modal/useModal';
10+
import useNotification from '../notification/useNotification';
11+
import type { AppConfig } from './context';
12+
import {
13+
useProvideAppConfigContext,
14+
useInjectAppConfigContext,
15+
useProvideAppContext,
16+
useInjectAppContext,
17+
} from './context';
18+
import useStyle from './style';
19+
20+
export const AppProps = () => {
21+
return {
22+
style: String,
23+
className: String,
24+
rootClassName: String,
25+
children: {
26+
type: Function as PropType<() => VueNode>,
27+
},
28+
message: objectType<AppConfig['message']>(),
29+
notification: objectType<AppConfig['notification']>(),
30+
};
31+
};
32+
33+
const useApp = () => {
34+
return useInjectAppContext();
35+
};
36+
37+
const App = defineComponent({
38+
name: 'AApp',
39+
props: initDefaultProps(AppProps(), {}),
40+
setup(props, { slots }) {
41+
const { prefixCls } = useConfigInject('app', props);
42+
const [wrapSSR, hashId] = useStyle(prefixCls);
43+
const customClassName = classNames(
44+
hashId.value,
45+
prefixCls.value,
46+
props.className,
47+
props.rootClassName,
48+
);
49+
50+
const appConfig = useInjectAppConfigContext();
51+
const mergedAppConfig = computed(() => ({
52+
message: { ...appConfig.message, ...props.message },
53+
notification: { ...appConfig.notification, ...props.notification },
54+
}));
55+
useProvideAppConfigContext(mergedAppConfig.value);
56+
57+
const [messageApi, messageContextHolder] = useMessage(mergedAppConfig.value.message);
58+
const [notificationApi, notificationContextHolder] = useNotification(
59+
mergedAppConfig.value.notification,
60+
);
61+
const [ModalApi, ModalContextHolder] = useModal();
62+
63+
const memoizedContextValue = computed(() => ({
64+
message: messageApi,
65+
notification: notificationApi,
66+
modal: ModalApi,
67+
}));
68+
useProvideAppContext(memoizedContextValue.value);
69+
70+
const childNode = slots.default?.();
71+
return () => {
72+
return wrapSSR(
73+
<div class={customClassName} style={props.style}>
74+
{ModalContextHolder()}
75+
{messageContextHolder()}
76+
{notificationContextHolder()}
77+
{props.children}
78+
{childNode}
79+
</div>,
80+
);
81+
};
82+
},
83+
});
84+
85+
App.useApp = useApp;
86+
87+
App.install = function (app: TypeApp) {
88+
app.component(App.name, App);
89+
};
90+
91+
export default App as typeof App &
92+
Plugin & {
93+
readonly useApp: typeof useApp;
94+
};

0 commit comments

Comments
 (0)