diff --git a/docs/en-US/component/loading.md b/docs/en-US/component/loading.md index 299e4956d2..604faa77c3 100644 --- a/docs/en-US/component/loading.md +++ b/docs/en-US/component/loading.md @@ -79,6 +79,27 @@ Calling the `close` method on any one of them can close this full screen Loading If Element Plus is imported entirely, a globally method `$loading` will be registered to `app.config.globalProperties`. You can invoke it like this: `this.$loading(options)`, and it also returns a Loading instance. +## App context inheritance ^(2.9.10) + +Now loading accepts a `context` as second parameter of the loading constructor which allows you to inject current app's context to loading which allows you to inherit all the properties of the app. + +You can use it like this: + +:::tip + +If you globally registered ElLoading component, it will automatically inherit your app context. + +::: + +```ts +import { getCurrentInstance } from 'vue' +import { ElLoading } from 'element-plus' + +// in your setup method +const { appContext } = getCurrentInstance()! +ElLoading.service({}, appContext) +``` + ## API ### Options diff --git a/packages/components/loading/__tests__/loading.test.tsx b/packages/components/loading/__tests__/loading.test.tsx index ae3cdf6b7b..7516c894fd 100644 --- a/packages/components/loading/__tests__/loading.test.tsx +++ b/packages/components/loading/__tests__/loading.test.tsx @@ -1,8 +1,8 @@ import { nextTick, ref } from 'vue' import { mount } from '@vue/test-utils' import { afterEach, describe, expect, test, vi } from 'vitest' -import { Loading } from '../src/service' -import { vLoading } from '../src/directive' +import Loading from '../src/service' +import vLoading from '../src/directive' import ElInput from '../../input' import type { VNode } from 'vue' diff --git a/packages/components/loading/index.ts b/packages/components/loading/index.ts index 3ea667c4ec..58635cccd9 100644 --- a/packages/components/loading/index.ts +++ b/packages/components/loading/index.ts @@ -1,11 +1,18 @@ -import { Loading } from './src/service' -import { vLoading } from './src/directive' +import Loading from './src/service' +import vLoading from './src/directive' -import type { App } from 'vue' +import type { App, AppContext, Directive } from 'vue' +import type { ElementLoading, LoadingBinding } from './src/directive' // installer and everything in all export const ElLoading = { install(app: App) { + Loading._context = app._context + ;( + vLoading as Directive & { + _context: AppContext | null + } + )._context = app._context app.directive('loading', vLoading) app.config.globalProperties.$loading = Loading }, diff --git a/packages/components/loading/src/directive.ts b/packages/components/loading/src/directive.ts index ae876d405d..9e54dfddbb 100644 --- a/packages/components/loading/src/directive.ts +++ b/packages/components/loading/src/directive.ts @@ -1,7 +1,7 @@ // @ts-nocheck import { isRef, ref } from 'vue' import { hyphenate, isObject, isString } from '@element-plus/utils' -import { Loading } from './service' +import Loading from './service' import type { Directive, DirectiveBinding, UnwrapRef } from 'vue' import type { LoadingOptions } from './types' import type { LoadingInstance } from './loading' @@ -54,9 +54,11 @@ const createInstance = ( body: getBindingProp('body') ?? binding.modifiers.body, lock: getBindingProp('lock') ?? binding.modifiers.lock, } + const instance = Loading(options) + instance._context = vLoading._context el[INSTANCE_KEY] = { options, - instance: Loading(options), + instance, } } @@ -70,7 +72,7 @@ const updateOptions = ( } } -export const vLoading: Directive = { +const vLoading: Directive = { mounted(el, binding) { if (binding.value) { createInstance(el, binding) @@ -94,3 +96,6 @@ export const vLoading: Directive = { el[INSTANCE_KEY] = null }, } + +vLoading._context = null +export default vLoading diff --git a/packages/components/loading/src/loading.ts b/packages/components/loading/src/loading.ts index 289090994b..927a2013b4 100644 --- a/packages/components/loading/src/loading.ts +++ b/packages/components/loading/src/loading.ts @@ -14,10 +14,14 @@ import { import { removeClass } from '@element-plus/utils' import { useGlobalComponentSettings } from '@element-plus/components/config-provider' +import type { AppContext } from 'vue' import type { UseNamespaceReturn } from '@element-plus/hooks' import type { LoadingOptionsResolved } from './types' -export function createLoadingComponent(options: LoadingOptionsResolved) { +export function createLoadingComponent( + options: LoadingOptionsResolved, + appContext: AppContext | null +) { let afterLeaveTimer: ReturnType // IMPORTANT NOTE: this is only a hacking way to expose the injections on an // instance, DO NOT FOLLOW this pattern in your own code. @@ -149,6 +153,7 @@ export function createLoadingComponent(options: LoadingOptionsResolved) { }) const loadingInstance = createApp(elLoadingComponent) + Object.assign(loadingInstance._context, appContext ?? {}) const vm = loadingInstance.mount(document.createElement('div')) return { diff --git a/packages/components/loading/src/service.ts b/packages/components/loading/src/service.ts index b784891d98..fca60b1ca0 100644 --- a/packages/components/loading/src/service.ts +++ b/packages/components/loading/src/service.ts @@ -13,13 +13,11 @@ import type { UseNamespaceReturn, UseZIndexReturn } from '@element-plus/hooks' import type { LoadingInstance } from './loading' import type { LoadingOptionsResolved } from '..' import type { LoadingOptions } from './types' -import type { CSSProperties } from 'vue' +import type { AppContext, CSSProperties } from 'vue' let fullscreenInstance: LoadingInstance | undefined = undefined -export const Loading = function ( - options: LoadingOptions = {} -): LoadingInstance { +const Loading = function (options: LoadingOptions = {}): LoadingInstance { if (!isClient) return undefined as any const resolved = resolveOptions(options) @@ -28,13 +26,16 @@ export const Loading = function ( return fullscreenInstance } - const instance = createLoadingComponent({ - ...resolved, - closed: () => { - resolved.closed?.() - if (resolved.fullscreen) fullscreenInstance = undefined + const instance = createLoadingComponent( + { + ...resolved, + closed: () => { + resolved.closed?.() + if (resolved.fullscreen) fullscreenInstance = undefined + }, }, - }) + Loading._context + ) addStyle(resolved, resolved.parent, instance) addClassList(resolved, resolved.parent, instance) @@ -163,3 +164,6 @@ const addClassList = ( removeClass(parent, ns.bm('parent', 'hidden')) } } + +Loading._context = null as AppContext | null +export default Loading