Browse Source

feat(nc-gui): extend `useDialog` by app context opts

pull/4141/head
braks 2 years ago
parent
commit
a82af3a53f
  1. 43
      packages/nc-gui/composables/useDialog/index.ts

43
packages/nc-gui/composables/useDialog/index.ts

@ -1,17 +1,22 @@
import type { VNode } from '@vue/runtime-dom' import type { AppContext, VNode } from '@vue/runtime-dom'
import { isVNode, render } from '@vue/runtime-dom' import { Suspense, isVNode, render } from '@vue/runtime-dom'
import type { ComponentPublicInstance } from '@vue/runtime-core' import type { ComponentPublicInstance } from '@vue/runtime-core'
import type { MaybeRef } from '@vueuse/core' import type { MaybeRef } from '@vueuse/core'
import { isClient } from '@vueuse/core' import { isClient } from '@vueuse/core'
import { createEventHook, h, ref, toReactive, tryOnScopeDispose, unref, useNuxtApp, watch } from '#imports' import { createEventHook, h, ref, toReactive, tryOnScopeDispose, unref, useNuxtApp, watch } from '#imports'
interface UseDialogOptions {
target: MaybeRef<HTMLElement | ComponentPublicInstance>
context: Partial<AppContext>
}
/** /**
* Programmatically create a component and attach it to the body (or a specific mount target), like a dialog or modal. * Programmatically create a component and attach it to the body (or a specific mount target), like a dialog or modal.
* This composable is not SSR friendly - it should be used only on the client. * This composable is not SSR friendly - it should be used only on the client.
* *
* @param componentOrVNode The component to create and attach. Can be a VNode or a component definition. * @param componentOrVNode The component to create and attach. Can be a VNode or a component definition.
* @param props The props to pass to the component. * @param props The props to pass to the component.
* @param mountTarget The target to attach the component to. Defaults to the document body * @param options Additional options to use {@see UseDialogOptions}
* *
* @example * @example
* import { useDialog } from '#imports' * import { useDialog } from '#imports'
@ -39,7 +44,7 @@ import { createEventHook, h, ref, toReactive, tryOnScopeDispose, unref, useNuxtA
export function useDialog( export function useDialog(
componentOrVNode: any, componentOrVNode: any,
props: NonNullable<Parameters<typeof h>[1]> = {}, props: NonNullable<Parameters<typeof h>[1]> = {},
mountTarget?: MaybeRef<Element | ComponentPublicInstance>, { target, context }: Partial<UseDialogOptions> = {},
) { ) {
if (typeof document === 'undefined' || !isClient) { if (typeof document === 'undefined' || !isClient) {
console.warn('[useDialog]: Cannot use outside of browser!') console.warn('[useDialog]: Cannot use outside of browser!')
@ -54,24 +59,36 @@ export function useDialog(
const vNodeRef = ref<VNode>() const vNodeRef = ref<VNode>()
let _mountTarget = unref(mountTarget) const mountTarget = ref<HTMLElement>()
_mountTarget = _mountTarget ? ('$el' in _mountTarget ? (_mountTarget.$el as HTMLElement) : _mountTarget) : document.body
/** if specified, append vnode to mount target instead of document.body */
_mountTarget.appendChild(domNode)
/** When props change, we want to re-render the element with the new prop values */ /** When props change, we want to re-render the element with the new prop values */
const stop = watch( const stop = watch(
toReactive(props), toReactive(props),
(reactiveProps) => { (reactiveProps) => {
const _mountTarget = unref(target)
/**
* If it's a component instance, use the instance's root element (`$el`), otherwise use the element itself
* If no target is specified, use the document body
*/
mountTarget.value = _mountTarget
? '$el' in _mountTarget
? (_mountTarget.$el as HTMLElement)
: _mountTarget
: document.body
/** if specified, append vnode to mount target instead of document.body */
mountTarget.value.appendChild(domNode)
// if it's a vnode, just render it, otherwise wrap in `h` to create a vnode
const vNode = isVNode(componentOrVNode) ? componentOrVNode : h(componentOrVNode, reactiveProps) const vNode = isVNode(componentOrVNode) ? componentOrVNode : h(componentOrVNode, reactiveProps)
vNode.appContext = useNuxtApp().vueApp._context vNode.appContext = { ...useNuxtApp().vueApp._context, ...context }
vNodeRef.value = vNode vNodeRef.value = vNode
render(vNode, domNode) // wrap in suspense to resolve potential promises
render(h(Suspense, vNode), domNode)
if (!isMounted) mountedHook.trigger() if (!isMounted) mountedHook.trigger()
}, },
@ -90,7 +107,7 @@ export function useDialog(
setTimeout(() => { setTimeout(() => {
try { try {
;(_mountTarget as HTMLElement)?.removeChild(domNode) ;(mountTarget.value as HTMLElement)?.removeChild(domNode)
} catch (e) {} } catch (e) {}
}, 100) }, 100)

Loading…
Cancel
Save