Skip to content

Commit

Permalink
feat: add app components
Browse files Browse the repository at this point in the history
  • Loading branch information
selicens committed Jul 9, 2023
1 parent 698c0ff commit c30f3de
Show file tree
Hide file tree
Showing 12 changed files with 489 additions and 2 deletions.
42 changes: 42 additions & 0 deletions components/app/__tests__/__snapshots__/demo.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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>
`;
3 changes: 3 additions & 0 deletions components/app/__tests__/demo.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';

demoTest('app');
43 changes: 43 additions & 0 deletions components/app/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { reactive, provide, inject } 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);
};
26 changes: 26 additions & 0 deletions components/app/demo/basic.vue
Original file line number Diff line number Diff line change
@@ -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></my-page>
</a-app>
</template>

<script setup>
import myPage from './myPage.vue';
</script>
23 changes: 23 additions & 0 deletions components/app/demo/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<demo-sort :cols="1">
<basic />
</demo-sort>
</template>

<script lang="ts">
import Basic from './basic.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
import { defineComponent } from 'vue';
export default defineComponent({
CN,
US,
components: {
Basic,
},
setup() {
return {};
},
});
</script>
32 changes: 32 additions & 0 deletions components/app/demo/myPage.vue
Original file line number Diff line number Diff line change
@@ -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>
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!!',
placement: 'topLeft',
});
};
</script>
99 changes: 99 additions & 0 deletions components/app/index.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
category: Components
group: Other
title: App
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HJz8SZos2wgAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*oC92TK44Ex8AAAAAAAAAAAAADrJ8AQ/original
demo:
cols: 2
---

New App Component which provide global style & static function replacement.

## When To Use

- Provide reset styles based on `.ant-app` element.
- You could use static methods of `message/notification/Modal` form `useApp` without put `contextHolder` mannully.

## 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
<template>
<a-app>
<my-page></my-page>
</a-app>
</template>
<script>
import myPage from './myPage.vue';
</script>

// myPage
<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>
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.

### 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>
```

### Embedded usage scenarios (if not necessary, try not to do nesting)

```html
<a-app>
<a-space>
...
<a-app>...</a-app>
</a-space>
</a-app>
```

## API

### App

| Property | Description | Type | Default |
| --- | --- | --- | --- |
| message | Global config for Message | [MessageConfig](/components/message/#messageconfig) | - |
| notification | Global config for Notification | [NotificationConfig](/components/notification/#notificationconfig) | - |
94 changes: 94 additions & 0 deletions components/app/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { defineComponent, computed } from 'vue';
import type { PropType, App as TypeApp, Plugin } from 'vue';
import { initDefaultProps } from '../_util/props-util';
import classNames from '../_util/classNames';
import type { VueNode } from '../_util/type';
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 {
style: String,
className: String,
rootClassName: String,
children: {
type: Function as PropType<() => VueNode>,
},
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 = classNames(
hashId.value,
prefixCls.value,
props.className,
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);

const childNode = slots.default?.();
return () => {
return wrapSSR(
<div class={customClassName} style={props.style}>
{ModalContextHolder()}
{messageContextHolder()}
{notificationContextHolder()}
{props.children}
{childNode}
</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;
};
Loading

0 comments on commit c30f3de

Please sign in to comment.