Skip to content

Commit

Permalink
Revert "Fix#3336/allow using composables outside setup context (#3355)"
Browse files Browse the repository at this point in the history
This reverts commit 3b37d87.
  • Loading branch information
m0ksem authored and asvae committed Apr 26, 2023
1 parent 3b37d87 commit 7bcae7f
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 55 deletions.
8 changes: 6 additions & 2 deletions packages/ui/src/components/va-modal/hooks/useModal.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { getCurrentInstance } from 'vue'
import { createModalInstance } from '../modal'
import { ModalOptions } from '../types'
import { getCurrentApp } from '../../../services/current-app'

/** This hook can be used without plugin used */
export const useModal = () => {
const appContext = getCurrentApp()._context
const appContext = getCurrentInstance()?.appContext

if (!appContext) {
throw new Error('useModal can be used only in setup function. You can use app.config.globalProperties.$vaModal outside setup function')
}

/**
* @param options can be message string or options object
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/components/va-modal/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ExtractComponentPropTypes } from '../../utils/component-options'
import VaModal from './VaModal.vue'
import { ExtractPropTypes } from 'vue'

export type ModalSize = 'small' | 'medium' | 'large'

Expand All @@ -14,4 +15,4 @@ export type ModalEmits = {
'onUpdate:modelValue'?: (value: boolean) => void;
}

export type ModalOptions = Partial<Omit<ExtractComponentPropTypes<typeof VaModal>, 'anchorClass'>> & ModalEmits
export type ModalOptions = Partial<Omit<ExtractComponentPropTypes<typeof VaModal> & ModalEmits, 'anchorClass'>>
5 changes: 3 additions & 2 deletions packages/ui/src/components/va-toast/hooks/useToast.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { getCurrentInstance } from 'vue'

import { createToastInstance, closeById, closeAllNotifications, NotificationOptions } from '../toast'
import { getCurrentApp } from '../../../services/current-app'

/** This hook can be used without plugin used */
export const useToast = () => {
const appContext = getCurrentApp()._context
const appContext = getCurrentInstance()?.appContext

const createdInThisSetupContext: string[] = []

Expand Down
47 changes: 6 additions & 41 deletions packages/ui/src/services/current-app.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,17 @@
import type { App, provide as vueProvide } from 'vue'
import { inject as vueInject, getCurrentInstance } from 'vue'
import type { App } from 'vue'
import { inject as vueInject } from 'vue'

/**
* Similar to `getCurrentInstance` but for plugins, so we can use inject in plugins.
*/
let app: App | null
let singleApp: App | null | undefined

export const setCurrentApp = (instance: App | null) => {
app = instance
export const setCurrentApp = (instance: App | null) => { app = instance }
export const getCurrentApp = () => app

if (singleApp && instance !== singleApp) {
// This means that user has multiple apps on page.
singleApp = null
} else {
singleApp = instance
}
}

/**
* Returns current app if Vuestic UI is used in single app mode.
*
* @throws Error if Vuestic UI is used in multiple apps.
* @throws Error if Vuestic UI plugin is not installed.
*/
export const getCurrentApp = () => {
const app = getCurrentInstance()?.appContext.app
if (app) { return app }

if (singleApp === undefined) {
throw new Error('Vuestic UI plugin is not installed.')
}
if (singleApp === null) {
throw new Error('Vuestic UI is used in multiple apps. You`re not allowed to use composable outside of setup function context.')
}
return singleApp
}

/** Wrapper around vue inject, so we can use it in plugins and outside of setup context if only one app is used */
/** Wrapper around vue inject, so it can be used in plugins */
export const inject = ((key: string, value?: any) => {
const app = getCurrentApp()?._context.provides[key]

return app || vueInject(key, value)
}) as typeof vueInject

/** Wrapper around vue provide, so we can use it in plugins and outside of setup context if only one app is used */
export const provide = ((key: string, value: any) => {
const provides = getCurrentInstance()?.appContext.provides || getCurrentApp()._context.provides

provides[key] = value
}) as typeof vueProvide
}) as unknown as typeof vueInject
21 changes: 16 additions & 5 deletions packages/ui/src/services/global-config/global-config.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import cloneDeep from 'lodash/cloneDeep.js'
import { Ref, ref } from 'vue'
import { ref, getCurrentInstance } from 'vue'
import { GlobalConfig, GlobalConfigUpdater, PartialGlobalConfig, ProvidedGlobalConfig } from './types'
import { getComponentsDefaultConfig } from '../component-config'
import { getIconDefaultConfig } from '../icon'
import { getColorDefaultConfig } from '../color'
import { getI18nConfigDefaults } from '../i18n'
import { getBreakpointDefaultConfig } from '../breakpoint'
import { inject, provide } from '../current-app'
import { getGlobalProperty } from '../vue-plugin/utils'
import { getCurrentApp, inject } from '../current-app'
import { mergeDeep } from '../../utils/merge-deep'
import { getColorsClassesDefaultConfig } from '../colors-classes'

export const GLOBAL_CONFIG = Symbol('GLOBAL_CONFIG')

export const createGlobalConfig = (): ProvidedGlobalConfig => {
export const createGlobalConfig = () => {
const globalConfig = ref<GlobalConfig>({
colors: getColorDefaultConfig(),
icons: getIconDefaultConfig(),
Expand All @@ -26,7 +27,7 @@ export const createGlobalConfig = (): ProvidedGlobalConfig => {
* TODO: if this try won't be success, may be remake to provide/inject
*/
routerComponent: undefined,
}) as Ref<GlobalConfig>
})

const getGlobalConfig = (): GlobalConfig => globalConfig.value
const setGlobalConfig = (updater: GlobalConfig | GlobalConfigUpdater<GlobalConfig>) => {
Expand All @@ -47,14 +48,24 @@ export const createGlobalConfig = (): ProvidedGlobalConfig => {
}
}

const provideForCurrentApp = <T>(provide: T) => {
const provides = getCurrentInstance()?.appContext.provides || getCurrentApp()?._context.provides

if (!provides) { throw new Error('Vue app not found for provide') }

provides[GLOBAL_CONFIG] = provide

return provide
}

/** Use this function if you don't want to throw error if hook used outside setup function by useGlobalConfig */
export function useGlobalConfig () {
let injected = inject<ProvidedGlobalConfig>(GLOBAL_CONFIG) as ProvidedGlobalConfig

if (!injected) {
injected = createGlobalConfig()

provide(GLOBAL_CONFIG, injected)
provideForCurrentApp(injected)
}

return injected
Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/utils/component-options/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ declare type ExtractDefineComponentPropsType<T> = true extends boolean ? {
export type ExtractComponentProps<T extends DefineComponentOptions> = true extends boolean ? ExtractDefineComponentPropsType<T> : never
export type ExtractComponentEmits<T> = T extends ComponentOptionsBase<any, any, any, any, any, any, any, infer E> ? E: []

export type ExtractComponentPropTypes<T> =
T extends (props: infer P, ...args: any) => any ? P :
T extends new () => { $props: infer P } ? NonNullable<P> :
{}
type UnPropType<T> = T extends PropType<infer P> ? P : never
export type ExtractComponentPropTypes<T extends DefineComponentOptions> = {
[K in keyof ExtractComponentProps<T>]: UnPropType<ExtractComponentProps<T>[K]['type']>
}

0 comments on commit 7bcae7f

Please sign in to comment.