feat(message-provider): add placement prop (#851)

* feat:n-input Support hidden password

* feat(form): support require-mark-placement(#171)

* Revert "feat(form): support require-mark-placement(#171)"

This reverts commit 0627777693.

* Revert "feat:n-input Support hidden password"

This reverts commit ea6491783d.

* feat(message provider): add placement prop(wip)

* feat(message provider): add placement prop

* feat: add demo

* feat: fix demo code

* feat: fix code

* feat: fix code

* Update CHANGELOG.en-US.md

* feat: fix style code
This commit is contained in:
doom-9 2021-08-16 01:15:06 +08:00 committed by GitHub
parent 95d0efa4d3
commit 3d7f96dd00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 316 additions and 16 deletions

View File

@ -9,7 +9,8 @@
### Feats ### Feats
- `n-message-provider` add `container-style` prop - `n-message-provider` add `container-style` prop.
- `n-message-provider` add `placement` prop.
### Fixes ### Fixes

View File

@ -9,6 +9,7 @@
### Feats ### Feats
- `n-message-provider` 新增 `container-style` 属性 - `n-message-provider` 新增 `container-style` 属性
- `n-message-provider` 新增 `placement` 属性
### Fixes ### Fixes

View File

@ -21,10 +21,10 @@ import { defineComponent } from 'vue'
// content // content
export default defineComponent({ export default defineComponent({
setup () { setup() {
const message = useMessage() const message = useMessage()
return { return {
warning () { warning() {
message.warning('...') message.warning('...')
} }
} }
@ -45,6 +45,7 @@ modify-content
manually-close manually-close
about-theme about-theme
multiple-line multiple-line
placement
``` ```
## API ## API
@ -56,6 +57,7 @@ multiple-line
| closable | `boolean` | All messages whether to show close icon. | | closable | `boolean` | All messages whether to show close icon. |
| duration | `number` | `3000` | All messages's default duration. | | duration | `number` | `3000` | All messages's default duration. |
| max | `number` | `undefined` | Limit the number of message to display. | | max | `number` | `undefined` | Limit the number of message to display. |
| placement | `top \| top-left \| top-right \| bottom \| bottom-left \| bottom-right ` | `top` | All message's placement. |
| to | `string \| HTMLElement` | `'body'` | Container node of message container. | | to | `string \| HTMLElement` | `'body'` | Container node of message container. |
### MessageProvider Injection API ### MessageProvider Injection API

View File

@ -0,0 +1,119 @@
# Placement
```html
<n-message-provider :placement="placement">
<Buttons @changePlacement="changePlacement" />
</n-message-provider>
```
```js
import { useMessage, NButton } from 'naive-ui'
import { h, ref } from 'vue'
const Buttons = {
emits: ['changePlacement'],
setup() {
const message = useMessage()
return {
message
}
},
render() {
return [
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'top')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => 'Top' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'bottom')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => 'Bottom' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'top-left')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => 'TopLeft' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'top-right')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => 'TopRight' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'bottom-left')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => 'BottomLeft' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'bottom-right')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => 'BottomRight' }
)
]
}
}
export default {
components: {
Buttons
},
setup() {
const placement = ref('top')
return {
placement,
changePlacement: (val) => {
placement.value = val
}
}
}
}
```

View File

@ -21,10 +21,10 @@ import { defineComponent } from 'vue'
// content // content
export default defineComponent({ export default defineComponent({
setup () { setup() {
const message = useMessage() const message = useMessage()
return { return {
warning () { warning() {
message.warning('...') message.warning('...')
} }
} }
@ -45,6 +45,7 @@ modify-content
manually-close manually-close
about-theme about-theme
multiple-line multiple-line
placement
``` ```
## API ## API
@ -56,6 +57,7 @@ multiple-line
| closable | `boolean` | 所有 Message 是否显示 close 图标 | | closable | `boolean` | 所有 Message 是否显示 close 图标 |
| duration | `number` | `3000` | 所有 Message 默认的持续时长 | | duration | `number` | `3000` | 所有 Message 默认的持续时长 |
| max | `number` | `undefined` | 限制提示信息显示的个数 | | max | `number` | `undefined` | 限制提示信息显示的个数 |
| placement | `top \| top-left \| top-right \| bottom \| bottom-left \| bottom-right ` | `top` | 所有 Message 显示的位置 |
| to | `string \| HTMLElement` | `'body'` | Message 容器节点的位置 | | to | `string \| HTMLElement` | `'body'` | Message 容器节点的位置 |
### MessageProvider Injection API ### MessageProvider Injection API

View File

@ -0,0 +1,119 @@
# 位置
```html
<n-message-provider :placement="placement">
<Buttons @changePlacement="changePlacement" />
</n-message-provider>
```
```js
import { useMessage, NButton } from 'naive-ui'
import { h, ref } from 'vue'
const Buttons = {
emits: ['changePlacement'],
setup() {
const message = useMessage()
return {
message
}
},
render() {
return [
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'top')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => '顶部' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'bottom')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => '底部' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'top-left')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => '左上' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'top-right')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => '右上' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'bottom-left')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => '左下' }
),
h(
NButton,
{
onClick: () => {
this.$emit('changePlacement', 'bottom-right')
this.message.info('How many roads must a man walk down')
},
style: {
marginRight: '10px'
}
},
{ default: () => '右下' }
)
]
}
}
export default {
components: {
Buttons
},
setup() {
const placement = ref('top')
return {
placement,
changePlacement: (val) => {
placement.value = val
}
}
}
}
```

View File

@ -35,7 +35,7 @@ const iconMap = {
export default defineComponent({ export default defineComponent({
name: 'Message', name: 'Message',
props: messageProps, props: messageProps,
setup (props) { setup(props) {
const { const {
props: messageProviderProps, props: messageProviderProps,
mergedClsPrefixRef mergedClsPrefixRef
@ -51,7 +51,7 @@ export default defineComponent({
) )
return { return {
mergedClsPrefix: mergedClsPrefixRef, mergedClsPrefix: mergedClsPrefixRef,
handleClose () { handleClose() {
props.onClose?.() props.onClose?.()
}, },
cssVars: computed(() => { cssVars: computed(() => {
@ -106,10 +106,11 @@ export default defineComponent({
'--line-height': lineHeight, '--line-height': lineHeight,
'--border-radius': borderRadius '--border-radius': borderRadius
} }
}) }),
placement: messageProviderProps.placement
} }
}, },
render () { render() {
const { const {
icon, icon,
type, type,
@ -122,7 +123,12 @@ export default defineComponent({
return ( return (
<div <div
class={`${mergedClsPrefix}-message-wrapper`} class={`${mergedClsPrefix}-message-wrapper`}
style={cssVars as CSSProperties} style={{
...(cssVars as CSSProperties),
alignItems: this.placement.startsWith('top')
? 'flex-start'
: 'flex-end'
}}
> >
<div class={`${mergedClsPrefix}-message`}> <div class={`${mergedClsPrefix}-message`}>
<div <div
@ -150,7 +156,7 @@ export default defineComponent({
} }
}) })
function createIconVNode ( function createIconVNode(
icon: undefined | (() => VNodeChild), icon: undefined | (() => VNodeChild),
type: MessageType, type: MessageType,
clsPrefix: string clsPrefix: string

View File

@ -72,6 +72,17 @@ const messageProviderProps = {
default: 3000 default: 3000
}, },
max: Number, max: Number,
placement: {
type: String as PropType<
| 'top'
| 'top-left'
| 'top-right'
| 'bottom'
| 'bottom-left'
| 'bottom-right'
>,
default: 'top'
},
closable: Boolean, closable: Boolean,
containerStyle: [String, Object] as PropType<string | CSSProperties> containerStyle: [String, Object] as PropType<string | CSSProperties>
} }
@ -164,7 +175,10 @@ export default defineComponent({
{this.messageList.length ? ( {this.messageList.length ? (
<Teleport to={this.to ?? 'body'}> <Teleport to={this.to ?? 'body'}>
<div <div
class={`${this.mergedClsPrefix}-message-container`} class={[
`${this.mergedClsPrefix}-message-container`,
`${this.mergedClsPrefix}-message-container--${this.placement}`
]}
key="message-container" key="message-container"
style={this.containerStyle} style={this.containerStyle}
> >

View File

@ -29,6 +29,7 @@ export default c([
margin: var(--margin); margin: var(--margin);
z-index: 0; z-index: 0;
transform-origin: top center; transform-origin: top center;
display: flex;
`, [ `, [
fadeInHeightExpand({ fadeInHeightExpand({
overflow: 'visible', overflow: 'visible',
@ -108,13 +109,48 @@ export default c([
cB('message-container', ` cB('message-container', `
z-index: 6000; z-index: 6000;
position: fixed; position: fixed;
top: 12px;
left: 0;
right: 0;
height: 0; height: 0;
overflow: visible; overflow: visible;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
`) `, [
cM('top', `
top: 12px;
left: 0;
right: 0;
`),
cM('top-left', `
top: 12px;
left: 12px;
right: 0;
align-items: flex-start;
`),
cM('top-right', `
top: 12px;
left: 0;
right: 12px;
align-items: flex-end;
`),
cM('bottom', `
bottom: 4px;
left: 0;
right: 0;
justify-content: flex-end;
`),
cM('bottom-left', `
bottom: 4px;
left: 12px;
right: 0;
justify-content: flex-end;
align-items: flex-start;
`),
cM('bottom-right', `
bottom: 4px;
left: 0;
right: 12px;
justify-content: flex-end;
align-items: flex-end;
`)
])
]) ])