feat(notification): clsPrefix

This commit is contained in:
07akioni 2021-04-18 22:35:39 +08:00
parent 86c2c6742e
commit 8f3fb3737c
10 changed files with 171 additions and 136 deletions

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { h, defineComponent, PropType, inject, computed, renderSlot } from 'vue'
import { createId } from 'seemly'
import { useMemo } from 'vooks'

View File

@ -75,7 +75,6 @@ export default defineComponent({
<div
class={`${clsPrefix}-color-picker-pallete__layer`}
style={{
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
backgroundImage: `linear-gradient(90deg, white, hsl(${this.displayedHue}, 100%, 50%))`
}}
/>

View File

@ -58,7 +58,7 @@ interface PrivateMessageRef extends MessageReactive {
export type MessageProviderInst = MessageApiInjection
const messageProps = {
const messageProviderProps = {
...(useTheme.props as ThemeProps<MessageTheme>),
to: {
type: [String, Object],
@ -66,9 +66,11 @@ const messageProps = {
}
}
export type MessageProviderProps = ExtractPublicPropTypes<typeof messageProps>
export type MessageProviderProps = ExtractPublicPropTypes<
typeof messageProviderProps
>
type MessageProviderSetupProps = ExtractPropTypes<typeof messageProps>
type MessageProviderSetupProps = ExtractPropTypes<typeof messageProviderProps>
export const messageProviderInjectionKey: InjectionKey<{
props: MessageProviderSetupProps
@ -77,7 +79,7 @@ export const messageProviderInjectionKey: InjectionKey<{
export default defineComponent({
name: 'MessageProvider',
props: messageProps,
props: messageProviderProps,
setup (props) {
const { mergedClsPrefix } = useConfig(props)
const messageListRef = ref<PrivateMessageReactive[]>([])
@ -120,13 +122,15 @@ export default defineComponent({
1
)
}
return {
cPrefix: mergedClsPrefix,
messageRefs,
messageList: messageListRef,
handleAfterLeave,
...api
}
return Object.assign(
{
cPrefix: mergedClsPrefix,
messageRefs,
messageList: messageListRef,
handleAfterLeave
},
api
)
},
render () {
return (

View File

@ -1,2 +1,6 @@
export { default as NNotificationProvider } from './src/NotificationProvider'
export type {
NotificationProviderProps,
NotificationProviderInst
} from './src/NotificationProvider'
export { useNotification } from './src/use-notification'

View File

@ -16,7 +16,7 @@ import {
WarningIcon,
ErrorIcon
} from '../../_internal/icons'
import { NotificationProviderInjection } from './NotificationProvider'
import { notificationProviderInjectionKey } from './NotificationProvider'
const iconMap = {
info: <InfoIcon />,
@ -57,10 +57,12 @@ export default defineComponent({
name: 'Notification',
props: notificationProps,
setup (props) {
const NNotificationProvider = inject<NotificationProviderInjection>(
'NNotificationProvider'
) as NotificationProviderInjection
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { cPrefixRef, mergedThemeRef } = inject(
notificationProviderInjectionKey
)!
return {
cPrefix: cPrefixRef,
showAvatar: computed(() => {
return props.avatar || props.type !== 'default'
}),
@ -95,7 +97,7 @@ export default defineComponent({
cubicBezierEaseIn,
cubicBezierEaseInOut
}
} = NNotificationProvider.mergedTheme
} = mergedThemeRef.value
const { left, right, top, bottom } = getPadding(padding)
return {
'--color': color,
@ -127,58 +129,61 @@ export default defineComponent({
}
},
render () {
const { cPrefix } = this
return (
<div
class={[
'n-notification',
`${cPrefix}-notification`,
{
'n-notification--closable': this.closable,
'n-notification--show-avatar': this.showAvatar
[`${cPrefix}-notification--closable`]: this.closable,
[`${cPrefix}-notification--show-avatar`]: this.showAvatar
}
]}
style={this.cssVars as CSSProperties}
>
{this.showAvatar ? (
<div class="n-notification__avatar">
<div class={`${cPrefix}-notification__avatar`}>
{this.avatar ? (
<Render render={this.avatar} />
) : this.type !== 'default' ? (
<NBaseIcon>{{ default: () => iconMap[this.type] }}</NBaseIcon>
<NBaseIcon clsPrefix={cPrefix}>
{{ default: () => iconMap[this.type] }}
</NBaseIcon>
) : null}
</div>
) : null}
{this.closable ? (
<NBaseClose
class="n-notification__close"
clsPrefix={cPrefix}
class={`${cPrefix}-notification__close`}
onClick={this.handleCloseClick}
/>
) : null}
<div ref="bodyRef" class="n-notification-main">
<div ref="bodyRef" class={`${cPrefix}-notification-main`}>
{this.title ? (
<div class="n-notification-main__header">
<div class={`${cPrefix}-notification-main__header`}>
<Render render={this.title} />
</div>
) : null}
{this.description ? (
<div class="n-notification-main__description">
<div class={`${cPrefix}-notification-main__description`}>
<Render render={this.description} />
</div>
) : null}
{this.content ? (
<pre class="n-notification-main__content">
<pre class={`${cPrefix}-notification-main__content`}>
<Render render={this.content} />
</pre>
) : null}
{this.meta || this.action ? (
<div class="n-notification-main-footer">
<div class={`${cPrefix}-notification-main-footer`}>
{this.meta ? (
<div class="n-notification-main-footer__meta">
<div class={`${cPrefix}-notification-main-footer__meta`}>
<Render render={this.meta} />
</div>
) : null}
{this.action ? (
<div class="n-notification-main-footer__action">
<div class={`${cPrefix}-notification-main-footer__action`}>
<Render render={this.action} />
</div>
) : null}

View File

@ -1,6 +1,6 @@
import { h, defineComponent, inject } from 'vue'
import { NScrollbar } from '../../scrollbar'
import { NotificationProviderInjection } from './NotificationProvider'
import { notificationProviderInjectionKey } from './NotificationProvider'
export default defineComponent({
name: 'NotificationContainer',
@ -11,29 +11,28 @@ export default defineComponent({
}
},
setup () {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { mergedThemeRef, cPrefixRef } = inject(
notificationProviderInjectionKey
)!
return {
NNotificationProvider: inject<NotificationProviderInjection>(
'NNotificationProvider'
) as NotificationProviderInjection
mergedTheme: mergedThemeRef,
cPrefix: cPrefixRef
}
},
render () {
const { $slots, scrollable, NNotificationProvider } = this
const { $slots, scrollable, cPrefix, mergedTheme } = this
return (
<div
class={[
'n-notification-container',
{
'n-notification-container--scrollable': scrollable
}
`${cPrefix}-notification-container`,
scrollable && `${cPrefix}-notification-container--scrollable`
]}
>
{scrollable ? (
<NScrollbar
theme={NNotificationProvider.mergedTheme.peers.Scrollbar}
themeOverrides={
NNotificationProvider.mergedTheme.peerOverrides.Scrollbar
}
theme={mergedTheme.peers.Scrollbar}
themeOverrides={mergedTheme.peerOverrides.Scrollbar}
>
{$slots}
</NScrollbar>

View File

@ -118,29 +118,28 @@ export default defineComponent({
}
},
render () {
return h(
Transition,
{
name: 'n-notification-transition',
appear: true,
return (
<Transition
name="notification-transition"
appear={true}
// convert to any since Element is not compitable with HTMLElement
onBeforeEnter: this.handleBeforeEnter as any,
onAfterEnter: this.handleAfterEnter as any,
onBeforeLeave: this.handleBeforeLeave as any,
onLeave: this.handleLeave as any,
onAfterLeave: this.handleAfterLeave as any
},
{
default: () => {
const props = keep(this.$props, notificationPropKeys)
return this.show
? h(NNotification, {
...props,
onClose: this.handleClose
})
: null
}
}
onBeforeEnter={this.handleBeforeEnter as any}
onAfterEnter={this.handleAfterEnter as any}
onBeforeLeave={this.handleBeforeLeave as any}
onLeave={this.handleLeave as any}
onAfterLeave={this.handleAfterLeave as any}
>
{{
default: () => {
return this.show ? (
<NNotification
{...keep(this.$props, notificationPropKeys)}
onClose={this.handleClose}
/>
) : null
}
}}
</Transition>
)
}
})

View File

@ -8,12 +8,15 @@ import {
defineComponent,
PropType,
ExtractPropTypes,
provide
provide,
InjectionKey,
Ref,
renderSlot
} from 'vue'
import { createId } from 'seemly'
import { useTheme } from '../../_mixins'
import { useConfig, useTheme } from '../../_mixins'
import type { MergedTheme, ThemeProps } from '../../_mixins'
import { omit } from '../../_utils'
import { ExtractPublicPropTypes, omit } from '../../_utils'
import { notificationLight, NotificationTheme } from '../styles'
import NotificationContainer from './NotificationContainer'
import NotificationEnvironment, {
@ -26,9 +29,14 @@ ExtractPropTypes<typeof notificationEnvOptions>
>
export interface NotificationProviderInjection {
mergedTheme: MergedTheme<NotificationTheme>
cPrefixRef: Ref<string>
mergedThemeRef: Ref<MergedTheme<NotificationTheme>>
}
export const notificationProviderInjectionKey: InjectionKey<NotificationProviderInjection> = Symbol(
'notificationProvider'
)
type Create = (options: NotificationOptions) => NotificationReactive
type TypedCreate = (
options: Omit<NotificationOptions, 'type'>
@ -44,6 +52,12 @@ export interface NotificationApiInjection {
open: Create
}
export type NotificationProviderInst = NotificationApiInjection
export const notificationApiInjectionKey: InjectionKey<NotificationApiInjection> = Symbol(
'notificationApi'
)
type NotificationReactive = {
readonly key: string
readonly destroy: () => void
@ -57,23 +71,29 @@ interface NotificationRef {
hide: () => void
}
const notificationProviderProps = {
...(useTheme.props as ThemeProps<NotificationTheme>),
to: [String, Object] as PropType<string | HTMLElement>,
scrollable: {
type: Boolean,
default: true
}
}
export type NotificationProviderProps = ExtractPublicPropTypes<
typeof notificationProviderProps
>
export default defineComponent({
name: 'NotificationProvider',
props: {
...(useTheme.props as ThemeProps<NotificationTheme>),
to: [String, Object] as PropType<string | HTMLElement>,
scrollable: {
type: Boolean,
default: true
}
},
props: notificationProviderProps,
setup (props) {
const { mergedClsPrefix } = useConfig(props)
const notificationListRef = ref<NotificationReactive[]>([])
const notificationRefs: Record<string, NotificationRef> = {}
function create (options: NotificationOptions): NotificationReactive {
const key = createId()
const destroy = (): void =>
notificationRefs[`n-notification-${key}`].hide()
const destroy = (): void => notificationRefs[key].hide()
const notificationReactive = reactive({
...options,
key,
@ -103,72 +123,72 @@ export default defineComponent({
'Notification',
style,
notificationLight,
props
props,
mergedClsPrefix
)
provide<NotificationApiInjection>('notification', {
const api = {
create,
info: apis[0],
success: apis[1],
warning: apis[2],
error: apis[3],
open
}
provide(notificationApiInjectionKey, api)
provide(notificationProviderInjectionKey, {
cPrefixRef: mergedClsPrefix,
mergedThemeRef: themeRef
})
provide<NotificationProviderInjection>(
'NNotificationProvider',
reactive({
mergedTheme: themeRef
})
)
// deprecated
function open (options: NotificationOptions): NotificationReactive {
return create(options)
}
return {
handleAfterLeave,
notificationList: notificationListRef,
notificationRefs
}
return Object.assign(
{
cPrefix: mergedClsPrefix,
notificationList: notificationListRef,
notificationRefs,
handleAfterLeave
},
api
)
},
render () {
return h(Fragment, null, [
h(
Teleport,
{
to: this.to ?? 'body'
},
[
this.notificationList.length
? h(
NotificationContainer,
{
scrollable: this.scrollable
},
{
return (
<>
{renderSlot(this.$slots, 'default')}
{this.notificationList.length ? (
<Teleport to={this.to ?? 'body'}>
<NotificationContainer scrollable={this.scrollable}>
{{
default: () => {
return this.notificationList.map((notification) => {
return h(NotificationEnvironment, {
ref: ((inst: NotificationRef) => {
const refKey = `n-notification-${notification.key}`
if (inst === null) {
delete this.notificationRefs[refKey]
} else this.notificationRefs[refKey] = inst
}) as any,
...omit(notification, [
'destroy',
'hide',
'deactivate'
]),
internalKey: notification.key,
onInternalAfterLeave: this.handleAfterLeave
})
return (
<NotificationEnvironment
ref={
((inst: NotificationRef) => {
const refKey = notification.key
if (inst === null) {
delete this.notificationRefs[refKey]
} else this.notificationRefs[refKey] = inst
}) as any
}
{...omit(notification, [
'destroy',
'hide',
'deactivate'
])}
internalKey={notification.key}
onInternalAfterLeave={this.handleAfterLeave}
/>
)
})
}
}
)
: null
]
),
this.$slots.default?.()
])
}}
</NotificationContainer>
</Teleport>
) : null}
</>
)
}
})

View File

@ -62,16 +62,16 @@ export default c([
top: 0
}),
cB('notification', [
c('&-transition-enter-from, &-transition-leave-to', `
c('&.notification-transition-enter-from, &.notification-transition-leave-to', `
opacity: 0;
margin-bottom: 0 !important;
transform: translateX(calc(100% + 16px));
`),
c('&-transition-leave-from, &-transition-enter-to', `
c('&.notification-transition-leave-from, &.notification-transition-enter-to', `
opacity: 1;
transform: translateX(0);
`),
c('&-transition-leave-active', `
c('&.notification-transition-leave-active', `
transition:
background-color .3s var(--bezier),
color .3s var(--bezier),

View File

@ -1,6 +1,12 @@
import { inject } from 'vue'
import { NotificationApiInjection } from './NotificationProvider'
import { notificationApiInjectionKey } from './NotificationProvider'
import type { NotificationApiInjection } from './NotificationProvider'
import { throwError } from '../../_utils'
export function useNotification (): NotificationApiInjection | undefined {
return inject<NotificationApiInjection>('notification')
export function useNotification (): NotificationApiInjection {
const api = inject(notificationApiInjectionKey, null)
if (api === null) {
throwError('use-notification', 'No outer `n-notification-provider` found.')
}
return api
}