@ithinkdt/naive
Version:
iThinkDT Naive UI
306 lines (268 loc) • 11.8 kB
JSX
import {
ref,
computed,
defineComponent,
useSlots,
h,
nextTick,
provide,
watch,
markRaw,
defineAsyncComponent,
Teleport,
} from 'vue'
import { useRouter } from 'vue-router'
import { asyncComputed } from '@vueuse/core'
import {
NConfigProvider,
NLoadingBarProvider,
modalProps,
NGlobalStyle,
NNotificationProvider,
useNotification,
NMessageProvider,
useMessage,
NDialogProvider,
useDialog,
drawerContentProps,
NScrollbar,
} from 'ithinkdt-ui'
import zhCN from 'ithinkdt-ui/es/locales/common/zhCN'
import dateZhCN from 'ithinkdt-ui/es/locales/date/zhCN'
import { useI18n, lang, useTheme, $notice, Route, useAuth, modals } from '@ithinkdt/core'
import { TooltipProvider } from '../directives/tooltip'
import { SpinProvider } from '../directives/spin'
export let message
export let dialog
export let notification
const NModal = defineAsyncComponent(() => import('./lazy.js').then((m) => m.NModal))
const NDrawer = defineAsyncComponent(() => import('./lazy.js').then((m) => m.NDrawer))
const NDrawerContent = defineAsyncComponent(() => import('./lazy.js').then((m) => m.NDrawerContent))
const NWatermark = defineAsyncComponent(() => import('./lazy.js').then((m) => m.NWatermark))
const Inject = defineComponent({
name: 'DtNaiveUIInject',
setup() {
message = useMessage()
dialog = useDialog()
notification = useNotification()
const slots = useSlots()
return slots.default
},
})
const ModalProvider = defineComponent({
name: 'DtNaiveUIModalProvider',
setup() {
const inited = Symbol()
return () =>
modals.map((modal) => {
if (!modal[inited]) {
modal.__date = (Date.now() % 1_000_000) + '-' + Math.floor(Math.random() * 1000)
modal.visible = false
modal.show = () => {
modal.visible = true
}
modal.hide = () => {
// eslint-disable-next-line unicorn/no-null
modal.visible = null
}
modal.close = () => {
modal.visible = false
}
modal[inited] = markRaw(
defineComponent({
provide: { __modal: modal },
render: () => {
const {
type = 'dialog',
onClose,
content: _1,
__destroyed: _2,
show: _3,
hide: _4,
destroy: _5,
visible: _6,
nativeScrollbar,
wrap,
class: cls,
style,
...props
} = modal
const node =
typeof modal.content === 'function' ? { default: modal.content } : modal.content
if (type === 'drawer') {
const drawerBind = {
class: cls,
style,
},
contentBind = {
nativeScrollbar: nativeScrollbar ?? false,
}
for (const k of Object.keys(props)) {
if (k in drawerContentProps) {
contentBind[k] = modal[k]
} else {
drawerBind[k] = modal[k]
}
}
return (
<NDrawer
{...drawerBind}
show={modal.visible || false}
onUpdateShow={(show) => {
modal.visible = show
if (!show) return onClose?.()
}}
maskClosable={modal.closable !== false && modal.maskClosable}
>
{wrap === false ? (
node.default()
) : (
<NDrawerContent {...contentBind}>{node}</NDrawerContent>
)}
</NDrawer>
)
}
const modalBind = type === 'dialog' ? { preset: 'card' } : {}
for (const k of Object.keys(props)) {
if (Object.hasOwn(modalProps, k)) {
modalBind[k] = modal[k]
}
}
if (typeof modalBind.contentStyle === 'string') {
modalBind.contentStyle =
'display: flex; flex-direction: column; overflow: hidden;' +
modalBind.contentStyle
} else {
if (!modalBind.contentStyle) {
modalBind.contentStyle = {}
}
modalBind.contentStyle.display = 'flex'
modalBind.contentStyle.flexDirection = 'column'
modalBind.contentStyle.overflow = 'auto'
}
if (!nativeScrollbar) {
const _def = node.default
node.default = () => (
<div>
<NScrollbar style="display: flex; flex-direction: column;">
<div>{_def?.()}</div>
</NScrollbar>
</div>
)
}
return (
<NModal
{...modalBind}
class={cls}
show={modal.visible || false}
style={{
...style,
width: typeof modal.width === 'number' ? modal.width + 'px' : modal.width,
maxHeight: style?.maxHeight ?? `calc(80vh - 20px)`,
}}
onClose={onClose}
onUpdateShow={(v) => (modal.visible = v)}
displayDirective={modal.visible === null ? 'show' : modalBind.displayDirective}
maskClosable={modal.closable !== false && modal.maskClosable}
autoFocus={false}
>
{node}
</NModal>
)
},
}),
)
}
if (modal.__destroyed) return
return h(modal[inited], { key: modal.__date })
})
},
})
export const DtNaiveUI = defineComponent({
name: 'DtNaiveUI',
setup() {
const { t, locale } = useI18n()
const nConfig = computed(() => {
const isZhCN = locale.value === lang.zhCN
return {
locale: isZhCN ? zhCN : undefined,
dateLocale: isZhCN ? dateZhCN : undefined,
}
})
const auth = useAuth()
provide('__INJECTED_AUTH__', auth)
const theme = useTheme()
provide('__INJECTED_THEME__', theme)
const router = useRouter()
watch(
[() => theme.isDark, () => auth.user, () => auth.app],
([isDark, user, app]) => {
if (theme.watermark === false) return
theme.watermark = theme._watermark?.({ isDark, user, app }) ?? undefined
},
{ immediate: true },
)
const slots = useSlots()
const loadingbarInst = ref()
router.beforeEach(() => {
loadingbarInst.value?.start()
})
router.afterEach(async (to) => {
await nextTick()
if (to.name === Route.NOT_FOUND || theme.currentPage?.error) {
loadingbarInst.value?.error()
} else {
loadingbarInst.value?.finish()
}
})
router.onError(() => {
// TODO 优化提示
$notice({
type: 'error',
title: t('sys.tip'),
content: t('sys.error.500'),
})
loadingbarInst.value?.error()
})
const Content = () => (
<NMessageProvider keepAliveOnHover>
<NNotificationProvider>
<NDialogProvider>
<NLoadingBarProvider ref={loadingbarInst}>
<Inject>{slots}</Inject>
{theme.watermark && !router.currentRoute.value?.meta.hideWatermark ? (
<NWatermark fullscreen cross {...theme.watermark} />
) : undefined}
<ModalProvider />
<TooltipProvider />
<Teleport to="body">
<SpinProvider />
</Teleport>
</NLoadingBarProvider>
</NDialogProvider>
</NNotificationProvider>
</NMessageProvider>
)
const themeOverrides = computed(() => ({
...theme.naiveThemeOverrides,
common: theme.vars,
}))
const darkTheme = asyncComputed(() => import('ithinkdt-ui/es/themes/dark').then((m) => m.darkTheme))
return () => {
return (
<NConfigProvider
abstract
preflight-style-disabled
locale={nConfig.value.locale}
dateLocale={nConfig.value.dateLocale}
// eslint-disable-next-line unicorn/no-null
theme={theme.isDark ? darkTheme.value : null}
themeOverrides={themeOverrides.value}
>
<NGlobalStyle />
<Content />
</NConfigProvider>
)
}
},
})