npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

promise-portal

v2.0.1

Published

use component as a promisd-like function

Readme

promise-portal

use component as a promisd-like function

Installation

pnpm add promise-portal

Online Demo

https://codesandbox.io/p/github/tjyuanpeng/promise-portal

Motivation

like element-plus, the modal is a vue component

in development, we want use modal like a function

no show property to control show/hide, gettting result is more explicit

easier to control workflow, and easier to handle life-cycles

so you can use Promise-Portal to save your life-time

before

use as a component, with ref value to control visibility and life-cycles

<script setup lang="ts">
import Comp from './components/name.vue'

const show = ref(false)
const onClick = () => {
  show.value = true
}
const onClosed = () => {
  show.value = false
}
</script>

<template>
  <el-button @click="onClick">
    click to open the Dialog
  </el-button>
  <Comp v-model="show" @closed="onClosed">
    a dialog content
  </Comp>
</template>

after

use as a normal promise-style function, so happy to develop

<script setup lang="ts">
import Comp from './components/name.vue'

const func = definePortal(Comp)
const onClick = async () => {
  const data = await func()
  console.log(data)
}
</script>

<template>
  <el-button @click="onClick">
    open the Dialog
  </el-button>
</template>

Use Case

create promise-portal instance in the entry file

// ./main.ts
import { createPromisePortal } from 'promise-portal'
import { createApp } from 'vue'

const app = createApp(App)
app.use(createPromisePortal())

use ContextProvider to set context globally

<!-- ./App.vue -->
<script setup lang="ts">
import locale from 'ant-design-vue/es/locale/zh_CN'
import { ContextProvider } from 'promise-portal'
</script>

<template>
  <a-config-provider :locale="locale">
    <ContextProvider>
      <router-view />
    </ContextProvider>
  </a-config-provider>
</template>

in component, use usePortalContext to use portal context

<!-- ./components/comp.vue -->
<script setup lang="ts">
import { usePortalContext } from 'promise-portal'

export interface Output {
  confirm: boolean
}
export interface Output {
  input: string
}
const props = defineProps<Input>()
const { resolve, show } = usePortalContext<Output>()
const onCancel = () => {
  resolve({ confirm: false })
}
</script>

<template>
  <a-modal v-model:open="show" @cancel="resolve">
    {{ props.input }}
  </a-modal>
</template>

define portal in anywhere, then use it like a promise-style function

// ./App.vue
import { definePortal } from 'promise-portal'
import Comp, { Input, Output } from './components/comp.vue'

const [func] = definePortal<Output, Input>(Comp)
const onClick = async () => {
  const result = await func({
    input: 'foo',
  })
  console.log(result)
}

API Reference

createPromisePortal

create promise-portal instance, set to vue instance

const instance = createPromisePortal()
app.use(instance)

ContextProvider

a component to set context globally

<script setup lang="ts">
import locale from 'ant-design-vue/es/locale/zh_CN'
import { ContextProvider } from 'promise-portal'
</script>

<template>
  <a-config-provider :locale="locale">
    <ContextProvider>
      <router-view />
    </ContextProvider>
  </a-config-provider>
</template>

usePortalContext

a vue composition api, use in portal component to get context of portal

const { resolve } = usePortalContext()

// detail
const {
  resolve, // promise resolve handler
  reject, // promise reject handler
  el, // portal base element, injected into 'appendTo' element
  vnode, // portal base vue vnode
  unmountDelay, // Ref for portal unmount delay (ms)
  show, // Ref for modal display state (controlled by portal)
} = usePortalContext({
  // Unmount delay (ms) for portal, usually for animation effects
  unmountDelay: 200,

  // Initial value for the show ref (defaults to true)
  initialShowValue: true,
})

you can use typescript generic types to promise fulfilled result

export interface Output {
  confirm: boolean
}
const { resolve } = usePortalContext<Output>()
resolve({
  confirm: true,
})

you can use show to control modal component

before unmount, show.value = false will be setted

use initialShowValue to set inital value, default inital value is true

<script setup lang="ts">
const { resolve, show } = usePortalContext<Output>({ initialShowValue: true })
</script>

<template>
  <a-modal v-model:open="show" @cancel="resolve" />
</template>

definePortal

define a portal, return a portal function

import Comp from './component.vue'

const portalFunc = definePortal(Comp)
portalFunc()

you can define generic types to check input object and output object

// component.vue
// App.vue
import Comp, { Input, Output } from './component.vue'

export interface Input {
  firstName: string
  lastName: string
}

export interface Output {
  fullName: string
  confirm: boolean
}

const props = defineProps<Input>()
const { resolve } = usePortalContext<Output>()
const portal = definePortal<Output, Input>(Comp)
const output = await portal({
  firstName: 'joe',
  lastName: 'watson',
})

define a portal with empty parameter

// component.vue
// App.vue
import Comp, { Output } from './component.vue'

export interface Output {
  fullName: string
  confirm: boolean
}

const { resolve } = usePortalContext<Output>()
const portal = definePortal<Output, void>(Comp)
const output = await portal() // only allow empty parameter

you can set a options to definePortal

definePortal(Comp, {
  // Unmount delay (ms) for portal, usually for animation effects
  unmountDelay: 200,

  // Initial value for the show ref (defaults to true)
  initialShowValue: true,

  // a dom element or CSS selector or Ref value or a function returing a dom element,
  // append the portal element to (defaults to document.body)
  appendTo: document.body,
})

detectPromisePortalInstance

detect whether the instance has been properly destroyed

// main.ts
if (import.meta.env.DEV) {
  detectPromisePortalInstance()
}

the return value is a function to stop detecting

const stopHandler = detectPromisePortalInstance()
stopHandler() // stop detecting

You can pass in other values to customize it.

detectPromisePortalInstance({
  text: 'Detected unreleased promise-portal instance',
  style: ' /styles you like/ ',
})

Acknowledgements