promise-portal 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,30 +1,25 @@
1
1
  # promise-portal
2
2
 
3
- let you use react portal in vue, and with promise
3
+ use component like a promisd-like function
4
4
 
5
- ## Install
5
+ ## Installation
6
6
 
7
7
  ```bash
8
8
  // pnpm
9
- pnpm add promise-portal -D
9
+ pnpm add promise-portal
10
10
 
11
11
  // npm
12
- npm install promise-portal -D
12
+ npm install promise-portal
13
13
 
14
14
  // yarn
15
- yarn add promise-portal --D
15
+ yarn add promise-portal
16
16
  ```
17
17
 
18
18
  ## Online Demo
19
19
 
20
- [Demo on codesandbox](https://codesandbox.io/p/github/tjyuanpeng/promise-portal)
20
+ [https://codesandbox.io/p/github/tjyuanpeng/promise-portal](https://codesandbox.io/p/github/tjyuanpeng/promise-portal)
21
21
 
22
- ## Relative Resourece
23
-
24
- - [react protal](https://reactjs.org/docs/portals.html)
25
- - [vue teleport](https://vuejs.org/guide/built-ins/teleport.html)
26
-
27
- ## Why
22
+ ## Motivation
28
23
 
29
24
  like element-plus, the modal is a vue component
30
25
 
@@ -34,11 +29,13 @@ no `show` property to control show/hide, gettting result is more explicit
34
29
 
35
30
  easier to control workflow, and easier to handle life-cycles
36
31
 
37
- ### Before
32
+ so you can use Promise-Portal to save your life-time
38
33
 
39
- use as acomponent, with ref value to control visibility and life-cycles
34
+ ### before
40
35
 
41
- ```ts
36
+ use as a component, with ref value to control visibility and life-cycles
37
+
38
+ ```vue
42
39
  <script setup lang="ts">
43
40
  import Comp from './components/name.vue'
44
41
  const show = ref(false)
@@ -55,27 +52,27 @@ const onClosed = () => {
55
52
  </template>
56
53
  ```
57
54
 
58
- ### After
55
+ ### after
59
56
 
60
57
  use as a normal promise-style function, so happy to develop
61
58
 
62
- ```ts
59
+ ```vue
63
60
  <script setup lang="ts">
64
- import Comp, { Input, Output } from './components/name.vue'
65
- const func = definePortal<Output, Input>(Comp)
61
+ import Comp from './components/name.vue'
62
+ const func = definePortal(Comp)
66
63
  const onClick = async () => {
67
64
  const data = await func()
68
65
  console.log(data)
69
66
  }
70
67
  </script>
71
68
  <template>
72
- <el-button @click="onClick"> click to open the Dialog </el-button>
69
+ <el-button @click="onClick"> open the Dialog </el-button>
73
70
  </template>
74
71
  ```
75
72
 
76
- ## Use
73
+ ## Use Case
77
74
 
78
- ### install in the entry file
75
+ ### create promise-portal instance in the entry file
79
76
 
80
77
  ```ts
81
78
  // ./main.ts
@@ -83,183 +80,241 @@ import { createApp } from 'vue'
83
80
  import { createPromisePortal } from 'promise-portal'
84
81
 
85
82
  const app = createApp(App)
86
- app.use(createPromisePortal())
83
+ app.use(
84
+ createPromisePortal({
85
+ unmountDelay: 200,
86
+ })
87
+ )
87
88
  ```
88
89
 
89
- ### in component, use `usePortalContext` to get portal context
90
+ ### in component, use `usePortalContext` to use portal context
90
91
 
91
- ```ts
92
- // ./components/name.vue
92
+ ```vue
93
+ <script setup lang="ts">
94
+ // ./components/comp.vue
93
95
  import { usePortalContext } from 'promise-portal'
94
-
95
- const { resolve } = usePortalContext<Output>()
96
- const onClose = () => {
97
- resolve({ confirm: false, fullName: '' })
96
+ export interface Output {
97
+ confirm: boolean
98
+ }
99
+ export interface Output {
100
+ input: string
101
+ }
102
+ const props = defineProps<Input>()
103
+ const { resolve, show } = usePortalContext<Output>()
104
+ const onCancel = () => {
105
+ resolve({ confirm: false })
98
106
  }
107
+ </script>
108
+ <template>
109
+ <a-modal v-model:open="show" @cancel="resolve">{{ props.input }}</a-modal>
110
+ </template>
99
111
  ```
100
112
 
101
- ### define portal, use it like a promise-style function
113
+ ### define portal in anywhere, then use it like a promise-style function
102
114
 
103
115
  ```ts
104
116
  // ./App.vue
105
117
  import { definePortal } from 'promise-portal'
106
- import Comp, { Input, Output } from './components/name.vue'
107
-
108
- const func = definePortal<Output, Input>(Comp)
118
+ import Comp, { Input, Output } from './components/comp.vue'
119
+ const [func] = definePortal<Output, Input>(Comp)
109
120
  const onClick = async () => {
110
- const data = await func({ firstName: 'joe', lastName: 'watson' })
111
- if (!data.confirm) {
112
- return
113
- }
114
- console.log(data)
121
+ const result = await func({
122
+ input: 'foo',
123
+ })
124
+ console.log(result)
115
125
  }
116
126
  ```
117
127
 
118
128
  ## API Reference
119
129
 
120
- - createPromisePortal
130
+ ### createPromisePortal
121
131
 
122
- create promise-portal instance, set to vue instance
132
+ create promise-portal instance, set to vue instance
123
133
 
124
- ```ts
125
- const instance = createPromisePortal()
126
- app.use(instance)
127
- ```
134
+ ```ts
135
+ const instance = createPromisePortal()
136
+ app.use(instance)
137
+ ```
128
138
 
129
- you can set default config to instance
139
+ you can set default options to instance
130
140
 
131
- ```ts
132
- const instance = createPromisePortal({
133
- unmountDelay: 100,
134
- })
135
- ```
141
+ ```ts
142
+ const instance = createPromisePortal({
143
+ // set a time gap before portal unmount,
144
+ // in general, it is to wait for animation effect
145
+ unmountDelay: 200,
146
+
147
+ // initial value to property show, default value is true
148
+ initialShowValue: true,
149
+ })
150
+ ```
136
151
 
137
- - getActiveInstance
152
+ ### getActiveInstance
138
153
 
139
- get active promise-portal instance
154
+ get active promise-portal instance
140
155
 
141
- ```ts
142
- const instance = getActiveInstance()
143
- ```
156
+ ```ts
157
+ const instance = getActiveInstance()
158
+ ```
144
159
 
145
- - setActiveInstance
160
+ ### setActiveInstance
146
161
 
147
- set promise-portal instance to be active
162
+ set active promise-portal instance
148
163
 
149
- ```ts
150
- setActiveInstance(instance)
151
- ```
164
+ ```ts
165
+ setActiveInstance(instance)
166
+ ```
152
167
 
153
- - usePortalContext
168
+ ### usePortalContext
154
169
 
155
- a vue composition api, use in portal component to get context of portal
170
+ a vue composition api, use in portal component to get context of portal
156
171
 
157
- ```ts
158
- const { resolve, reject, el, vNode, setUnmountDelay } = usePortalContext()
159
- // resolve: promise resolve handler
160
- // reject: promise reject handler
161
- // el: portal base element, injecting to body element
162
- // vNode: portal base vue vnode
163
- // setUnmountDelay: set unmount delay to this portal
164
- ```
172
+ ```ts
173
+ const { resolve } = usePortalContext()
174
+
175
+ // detail
176
+ const {
177
+ resolve, // promise resolve handler
178
+ reject, // promise reject handler
179
+ el, // portal base element, injecting to body element
180
+ vnode, // portal base vue vnode
181
+ setUnmountDelay, // set delay to unmount
182
+ show, // a ref value to use in modal component
183
+ } = usePortalContext({
184
+ // set a time gap before portal unmount,
185
+ // in general, it is to wait for animation effect
186
+ unmountDelay: 200,
187
+
188
+ // initial value to property show above, default value is true
189
+ initialShowValue: true,
190
+ })
191
+ ```
165
192
 
166
- you can use typescript generic types
193
+ you can use typescript generic types to promise fulfilled result
167
194
 
168
- ```ts
169
- const { resolve } = usePortalContext<Output>()
170
- resolve({ ... }) // an object of type Output
171
- ```
195
+ ```ts
196
+ export interface Output {
197
+ confirm: boolean
198
+ }
199
+ const { resolve } = usePortalContext<Output>()
200
+ resolve({
201
+ confirm: true,
202
+ })
203
+ ```
172
204
 
173
- - definePortal
205
+ you can use `show` to control modal component
174
206
 
175
- define a portal, return a portal function
207
+ before `unmount`, `show.value = false` will be setted
176
208
 
177
- ```ts
178
- import Comp from './component.vue'
179
- const portal = definePortal(Comp)
180
- portal() // return a promise
181
- ```
209
+ use `initialShowValue` to set inital value, default inital value is `true`
182
210
 
183
- you can define generic types to check input object and output object
211
+ ```vue
212
+ <script setup lang="ts">
213
+ const { resolve, show } = usePortalContext<Output>({ initialShowValue: true })
214
+ </script>
215
+ <template>
216
+ <a-modal v-model:open="show" @cancel="resolve"></a-modal>
217
+ </template>
218
+ ```
184
219
 
185
- ```ts
186
- // component.vue
187
- export interface Input {
188
- firstName: string
189
- lastName: string
190
- }
220
+ ### definePortal
191
221
 
192
- export interface Output {
193
- fullName: string
194
- confirm: boolean
195
- }
222
+ define a portal, return a portal function
196
223
 
197
- const props = defineProps<Input>()
198
- const { resolve } = usePortalContext<Output>()
224
+ ```ts
225
+ import Comp from './component.vue'
226
+ const portalFunc = definePortal(Comp)
227
+ portalFunc()
228
+ ```
199
229
 
200
- // App.vue
201
- import Comp, { Input, Output } from './component.vue'
202
- const portal = definePortal<Output, Input>(Comp)
203
- const output = await portal({
204
- firstName: 'joe',
205
- lastName: 'watson',
206
- })
207
- ```
208
-
209
- how to define a portal with empty parameter
210
-
211
- ```ts
212
- // component.vue
213
- export interface Output {
214
- fullName: string
215
- confirm: boolean
216
- }
217
-
218
- const { resolve } = usePortalContext<Output>()
219
-
220
- // App.vue
221
- import Comp, { Output } from './component.vue'
222
- const portal = definePortal<Output, void>(Comp)
223
- const output = await portal() // only allow empty parameter
224
- ```
225
-
226
- you can set a config to definePortal
227
-
228
- ```ts
229
- definePortal(Comp, {
230
- // set a time gap before portal unmount,
231
- // in general, it to wait for animation effect
232
- unmountDelay: 1000,
233
- // set promise-portal instance explicitly to render this portal
234
- // not use the active instance internally
235
- // of course, you can use `setActiveInstance` to set active instance
236
- instance: promisePortalInstance,
237
- })
238
- ```
230
+ you can define generic types to check input object and output object
231
+
232
+ ```ts
233
+ // component.vue
234
+ export interface Input {
235
+ firstName: string
236
+ lastName: string
237
+ }
238
+
239
+ export interface Output {
240
+ fullName: string
241
+ confirm: boolean
242
+ }
239
243
 
240
- - detectPromisePortalInstance
244
+ const props = defineProps<Input>()
245
+ const { resolve } = usePortalContext<Output>()
241
246
 
242
- Check if the instance has been properly destroyed
247
+ // App.vue
248
+ import Comp, { Input, Output } from './component.vue'
249
+ const portal = definePortal<Output, Input>(Comp)
250
+ const output = await portal({
251
+ firstName: 'joe',
252
+ lastName: 'watson',
253
+ })
254
+ ```
243
255
 
244
- ```ts
245
- // main.ts
246
- if (import.meta.env.DEV) {
247
- detectPromisePortalInstance()
248
- }
249
- ```
256
+ define a portal with empty parameter
250
257
 
251
- You can pass in other values to customize it.
258
+ ```ts
259
+ // component.vue
260
+ export interface Output {
261
+ fullName: string
262
+ confirm: boolean
263
+ }
252
264
 
253
- ```ts
254
- // default value
255
- detectPromisePortalInstance({
256
- style = 'position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;',
257
- text = `Detected that the promise-portal instance has not been properly destroyed<br>
258
- Please make sure to call resolve/reject to release the instance correctly.`,
259
- })
260
- ```
265
+ const { resolve } = usePortalContext<Output>()
266
+
267
+ // App.vue
268
+ import Comp, { Output } from './component.vue'
269
+ const portal = definePortal<Output, void>(Comp)
270
+ const output = await portal() // only allow empty parameter
271
+ ```
272
+
273
+ you can set a options to definePortal
274
+
275
+ ```ts
276
+ definePortal(Comp, {
277
+ // set a time gap before portal unmount,
278
+ unmountDelay: 200,
279
+
280
+ // initial value to property show
281
+ initialShowValue: true,
282
+
283
+ // set promise-portal instance explicitly to render this portal
284
+ instance: promisePortalInstance,
285
+ })
286
+ ```
287
+
288
+ ### detectPromisePortalInstance
289
+
290
+ detect whether the instance has been properly destroyed
291
+
292
+ ```ts
293
+ // main.ts
294
+ if (import.meta.env.DEV) {
295
+ detectPromisePortalInstance()
296
+ }
297
+ ```
261
298
 
262
- ## Link
299
+ the return value is a function to stop detecting
263
300
 
301
+ ```ts
302
+ const stopHandler = detectPromisePortalInstance()
303
+ stopHandler() // stop detecting
304
+ ```
305
+
306
+ You can pass in other values to customize it.
307
+
308
+ ```ts
309
+ detectPromisePortalInstance({
310
+ text: 'Detected unreleased promise-portal instance',
311
+ style: ' /styles you like/ ',
312
+ })
313
+ ```
314
+
315
+ # Relative Resourece
316
+
317
+ - [react protal](https://reactjs.org/docs/portals.html)
318
+ - [vue teleport](https://vuejs.org/guide/built-ins/teleport.html)
264
319
  - [@filez/portal](https://github.com/lenovo-filez/portal)
265
320
  - [promise-modal](https://github.com/liruifengv/promise-modal)
package/dist/index.cjs CHANGED
@@ -28,6 +28,8 @@ __export(src_exports, {
28
28
  usePortalContext: () => usePortalContext
29
29
  });
30
30
  module.exports = __toCommonJS(src_exports);
31
+
32
+ // src/portal.ts
31
33
  var import_vue = require("vue");
32
34
  var promisePortalSymbol = process.env.NODE_ENV !== "production" ? Symbol("promise-portal") : Symbol();
33
35
  var activeInstance;
@@ -46,73 +48,94 @@ var createPromisePortal = (defaultOptions = {}) => {
46
48
  };
47
49
  return instance;
48
50
  };
49
- var usePortalContext = () => {
51
+ var usePortalContext = (options = {}) => {
50
52
  const instance = (0, import_vue.inject)(promisePortalSymbol);
51
53
  if (!instance) {
52
54
  throw new Error("[promise-portal]: no instance found.");
53
55
  }
54
- const ins = (0, import_vue.getCurrentInstance)();
55
- if (!ins?.vnode) {
56
+ const vnode = (0, import_vue.getCurrentInstance)()?.vnode;
57
+ if (!vnode) {
56
58
  throw new Error("[promise-portal]: no vnode found.");
57
59
  }
58
- const data = instance.map.get(ins.vnode);
60
+ const data = instance.map.get(vnode);
59
61
  if (!data) {
60
62
  throw new Error("[promise-portal]: no inject data found.");
61
63
  }
64
+ if (options.unmountDelay != void 0) {
65
+ data.setUnmountDelay(options.unmountDelay);
66
+ }
67
+ if (options.initialShowValue != void 0) {
68
+ data.show.value = options.initialShowValue;
69
+ }
62
70
  return data;
63
71
  };
64
- var definePortal = (component, { instance, unmountDelay } = {}) => {
65
- const _instance = instance || (0, import_vue.getCurrentInstance)() && (0, import_vue.inject)(promisePortalSymbol) || activeInstance;
66
- if (!_instance) {
72
+ var definePortal = (component, options = {}) => {
73
+ const instance = options.instance ?? ((0, import_vue.getCurrentInstance)() && (0, import_vue.inject)(promisePortalSymbol)) ?? getActiveInstance();
74
+ if (!instance) {
67
75
  throw new Error("[promise-portal]: no instance found. Do you forget install promise-portal?");
68
76
  }
69
- return (props, children) => {
70
- let el = document.createElement("div");
77
+ let appContext = (0, import_vue.getCurrentInstance)()?.appContext;
78
+ let contextHolderProvides = null;
79
+ const ContextHolder = (0, import_vue.defineComponent)(() => () => {
80
+ appContext = (0, import_vue.getCurrentInstance)()?.appContext;
81
+ contextHolderProvides = (0, import_vue.getCurrentInstance)()?.provides;
82
+ });
83
+ const portal = (props, children) => {
84
+ const el = document.createElement("div");
71
85
  el.setAttribute("data-promise-portal-container", "");
72
86
  document.body.appendChild(el);
73
- let _delay = unmountDelay || _instance.defaultOptions?.unmountDelay;
87
+ let unmountDelay = options.unmountDelay ?? instance.defaultOptions.unmountDelay;
74
88
  const setUnmountDelay = (delay) => {
75
- _delay = delay;
89
+ unmountDelay = delay;
76
90
  };
77
- let vNode;
91
+ const show = (0, import_vue.ref)(options.initialShowValue ?? instance.defaultOptions.initialShowValue ?? true);
92
+ let vnode;
78
93
  const p = new Promise((resolve, reject) => {
79
- vNode = (0, import_vue.createVNode)(component, props, children);
80
- _instance.map.set(vNode, { resolve, reject, el, vNode, setUnmountDelay });
81
- vNode.appContext = _instance.app._context;
82
- (0, import_vue.render)(vNode, el);
94
+ vnode = (0, import_vue.createVNode)(component, props, children);
95
+ instance.map.set(vnode, { resolve, reject, el, vnode, setUnmountDelay, show });
96
+ const ac = appContext ?? instance.app._context;
97
+ vnode.appContext = Object.create(ac, {
98
+ provides: {
99
+ value: contextHolderProvides ?? ac.provides
100
+ }
101
+ });
102
+ (0, import_vue.render)(vnode, el);
83
103
  });
84
104
  p.finally(() => {
105
+ show.value = false;
85
106
  setTimeout(() => {
86
- if (el) {
87
- (0, import_vue.render)(null, el);
88
- document.body.removeChild(el);
89
- }
90
- el = null;
91
- vNode = null;
92
- }, _delay);
107
+ (0, import_vue.render)(null, el);
108
+ document.body.removeChild(el);
109
+ }, unmountDelay);
93
110
  });
94
111
  return p;
95
112
  };
113
+ return [portal, ContextHolder];
96
114
  };
97
- var detectPromisePortalInstance = (config = {}) => {
115
+
116
+ // src/detector.ts
117
+ function detect(options) {
98
118
  const {
99
119
  style = "position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;",
100
120
  text = `Detected that the promise-portal instance has not been properly destroyed<br>Please make sure to call resolve/reject to release the instance correctly.`
101
- } = config;
102
- const nodes = document.querySelectorAll("[data-promise-portal-container]");
103
- if (nodes.length > 0) {
104
- let el = document.querySelector("[data-promise-portal-detector]");
105
- if (!el) {
106
- el = document.createElement("div");
107
- el.setAttribute("data-promise-portal-detector", "");
108
- el.setAttribute("style", style);
109
- el.innerHTML = text;
110
- document.body.appendChild(el);
111
- }
112
- } else {
113
- document.querySelector("[data-promise-portal-detector]")?.remove();
121
+ } = options;
122
+ const containers = document.querySelectorAll("[data-promise-portal-container]");
123
+ const detector = document.querySelector("[data-promise-portal-detector]");
124
+ if (containers.length === 0) {
125
+ detector?.remove();
126
+ return;
127
+ }
128
+ if (!detector) {
129
+ const el = document.createElement("div");
130
+ el.setAttribute("data-promise-portal-detector", "");
131
+ el.setAttribute("style", style);
132
+ el.innerHTML = text;
133
+ document.body.appendChild(el);
114
134
  }
115
- setTimeout(() => detectPromisePortalInstance(config), 200);
135
+ }
136
+ var detectPromisePortalInstance = (options = {}) => {
137
+ const timer = setInterval(() => detect(options), 200);
138
+ return () => clearInterval(timer);
116
139
  };
117
140
  // Annotate the CommonJS export names for ESM import in node:
118
141
  0 && (module.exports = {
package/dist/index.d.ts CHANGED
@@ -1,33 +1,36 @@
1
- import { VNode, App, Component } from 'vue';
1
+ import * as vue from 'vue';
2
+ import { VNode, Ref, App, Component } from 'vue';
2
3
 
3
- interface PortalContext<R> {
4
+ interface Options {
5
+ unmountDelay?: number;
6
+ initialShowValue?: boolean;
7
+ }
8
+ interface Context<R> {
4
9
  resolve: (value: R | PromiseLike<R>) => void;
5
10
  reject: (reason?: any) => void;
6
11
  el: HTMLDivElement;
7
- vNode: VNode;
12
+ vnode: VNode;
8
13
  setUnmountDelay: (unmountDelay: number) => void;
14
+ show: Ref<boolean>;
9
15
  }
10
- interface PromisePortal<R = any> {
11
- defaultOptions: {
12
- unmountDelay?: number;
13
- };
16
+ interface Instance<R = any> {
17
+ defaultOptions: Options;
14
18
  app: App;
15
- map: WeakMap<VNode, PortalContext<R>>;
19
+ map: WeakMap<VNode, Context<R>>;
16
20
  install: (app: App) => void;
17
21
  }
18
- interface PortalOptions<R> {
19
- instance?: PromisePortal<R>;
20
- unmountDelay?: number;
21
- }
22
- declare const getActiveInstance: () => PromisePortal<any>;
23
- declare const setActiveInstance: (instance: PromisePortal) => PromisePortal<any>;
24
- declare const createPromisePortal: (defaultOptions?: {}) => PromisePortal<any>;
25
- declare const usePortalContext: <TOutput = any>() => PortalContext<TOutput>;
26
- declare const definePortal: <TOutput = any, TProps = any>(component: Component, { instance, unmountDelay }?: PortalOptions<TOutput>) => (props?: TProps | undefined, children?: unknown) => Promise<TOutput>;
27
- interface DetectPromisePortalInstanceConfig {
22
+ declare const getActiveInstance: () => Instance<any>;
23
+ declare const setActiveInstance: (instance: Instance) => Instance<any>;
24
+ declare const createPromisePortal: (defaultOptions?: Options) => Instance<any>;
25
+ declare const usePortalContext: <TOutput = any>(options?: Options) => Context<TOutput>;
26
+ declare const definePortal: <TOutput = any, TProps = any>(component: Component, options?: Options & {
27
+ instance?: Instance<TOutput> | undefined;
28
+ }) => [(props?: TProps | undefined, children?: unknown) => Promise<TOutput>, Component<any, any, any, vue.ComputedOptions, vue.MethodOptions>];
29
+
30
+ interface DetectorOptions {
28
31
  style?: string;
29
32
  text?: string;
30
33
  }
31
- declare const detectPromisePortalInstance: (config?: DetectPromisePortalInstanceConfig) => void;
34
+ declare const detectPromisePortalInstance: (options?: DetectorOptions) => () => void;
32
35
 
33
- export { DetectPromisePortalInstanceConfig, PortalContext, PortalOptions, PromisePortal, createPromisePortal, definePortal, detectPromisePortalInstance, getActiveInstance, setActiveInstance, usePortalContext };
36
+ export { Context, DetectorOptions, Instance, Options, createPromisePortal, definePortal, detectPromisePortalInstance, getActiveInstance, setActiveInstance, usePortalContext };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- // src/index.ts
2
- import { createVNode, render, inject, getCurrentInstance } from "vue";
1
+ // src/portal.ts
2
+ import { createVNode, render, inject, getCurrentInstance, defineComponent, ref } from "vue";
3
3
  var promisePortalSymbol = process.env.NODE_ENV !== "production" ? Symbol("promise-portal") : Symbol();
4
4
  var activeInstance;
5
5
  var getActiveInstance = () => activeInstance;
@@ -17,73 +17,94 @@ var createPromisePortal = (defaultOptions = {}) => {
17
17
  };
18
18
  return instance;
19
19
  };
20
- var usePortalContext = () => {
20
+ var usePortalContext = (options = {}) => {
21
21
  const instance = inject(promisePortalSymbol);
22
22
  if (!instance) {
23
23
  throw new Error("[promise-portal]: no instance found.");
24
24
  }
25
- const ins = getCurrentInstance();
26
- if (!ins?.vnode) {
25
+ const vnode = getCurrentInstance()?.vnode;
26
+ if (!vnode) {
27
27
  throw new Error("[promise-portal]: no vnode found.");
28
28
  }
29
- const data = instance.map.get(ins.vnode);
29
+ const data = instance.map.get(vnode);
30
30
  if (!data) {
31
31
  throw new Error("[promise-portal]: no inject data found.");
32
32
  }
33
+ if (options.unmountDelay != void 0) {
34
+ data.setUnmountDelay(options.unmountDelay);
35
+ }
36
+ if (options.initialShowValue != void 0) {
37
+ data.show.value = options.initialShowValue;
38
+ }
33
39
  return data;
34
40
  };
35
- var definePortal = (component, { instance, unmountDelay } = {}) => {
36
- const _instance = instance || getCurrentInstance() && inject(promisePortalSymbol) || activeInstance;
37
- if (!_instance) {
41
+ var definePortal = (component, options = {}) => {
42
+ const instance = options.instance ?? (getCurrentInstance() && inject(promisePortalSymbol)) ?? getActiveInstance();
43
+ if (!instance) {
38
44
  throw new Error("[promise-portal]: no instance found. Do you forget install promise-portal?");
39
45
  }
40
- return (props, children) => {
41
- let el = document.createElement("div");
46
+ let appContext = getCurrentInstance()?.appContext;
47
+ let contextHolderProvides = null;
48
+ const ContextHolder = defineComponent(() => () => {
49
+ appContext = getCurrentInstance()?.appContext;
50
+ contextHolderProvides = getCurrentInstance()?.provides;
51
+ });
52
+ const portal = (props, children) => {
53
+ const el = document.createElement("div");
42
54
  el.setAttribute("data-promise-portal-container", "");
43
55
  document.body.appendChild(el);
44
- let _delay = unmountDelay || _instance.defaultOptions?.unmountDelay;
56
+ let unmountDelay = options.unmountDelay ?? instance.defaultOptions.unmountDelay;
45
57
  const setUnmountDelay = (delay) => {
46
- _delay = delay;
58
+ unmountDelay = delay;
47
59
  };
48
- let vNode;
60
+ const show = ref(options.initialShowValue ?? instance.defaultOptions.initialShowValue ?? true);
61
+ let vnode;
49
62
  const p = new Promise((resolve, reject) => {
50
- vNode = createVNode(component, props, children);
51
- _instance.map.set(vNode, { resolve, reject, el, vNode, setUnmountDelay });
52
- vNode.appContext = _instance.app._context;
53
- render(vNode, el);
63
+ vnode = createVNode(component, props, children);
64
+ instance.map.set(vnode, { resolve, reject, el, vnode, setUnmountDelay, show });
65
+ const ac = appContext ?? instance.app._context;
66
+ vnode.appContext = Object.create(ac, {
67
+ provides: {
68
+ value: contextHolderProvides ?? ac.provides
69
+ }
70
+ });
71
+ render(vnode, el);
54
72
  });
55
73
  p.finally(() => {
74
+ show.value = false;
56
75
  setTimeout(() => {
57
- if (el) {
58
- render(null, el);
59
- document.body.removeChild(el);
60
- }
61
- el = null;
62
- vNode = null;
63
- }, _delay);
76
+ render(null, el);
77
+ document.body.removeChild(el);
78
+ }, unmountDelay);
64
79
  });
65
80
  return p;
66
81
  };
82
+ return [portal, ContextHolder];
67
83
  };
68
- var detectPromisePortalInstance = (config = {}) => {
84
+
85
+ // src/detector.ts
86
+ function detect(options) {
69
87
  const {
70
88
  style = "position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;",
71
89
  text = `Detected that the promise-portal instance has not been properly destroyed<br>Please make sure to call resolve/reject to release the instance correctly.`
72
- } = config;
73
- const nodes = document.querySelectorAll("[data-promise-portal-container]");
74
- if (nodes.length > 0) {
75
- let el = document.querySelector("[data-promise-portal-detector]");
76
- if (!el) {
77
- el = document.createElement("div");
78
- el.setAttribute("data-promise-portal-detector", "");
79
- el.setAttribute("style", style);
80
- el.innerHTML = text;
81
- document.body.appendChild(el);
82
- }
83
- } else {
84
- document.querySelector("[data-promise-portal-detector]")?.remove();
90
+ } = options;
91
+ const containers = document.querySelectorAll("[data-promise-portal-container]");
92
+ const detector = document.querySelector("[data-promise-portal-detector]");
93
+ if (containers.length === 0) {
94
+ detector?.remove();
95
+ return;
96
+ }
97
+ if (!detector) {
98
+ const el = document.createElement("div");
99
+ el.setAttribute("data-promise-portal-detector", "");
100
+ el.setAttribute("style", style);
101
+ el.innerHTML = text;
102
+ document.body.appendChild(el);
85
103
  }
86
- setTimeout(() => detectPromisePortalInstance(config), 200);
104
+ }
105
+ var detectPromisePortalInstance = (options = {}) => {
106
+ const timer = setInterval(() => detect(options), 200);
107
+ return () => clearInterval(timer);
87
108
  };
88
109
  export {
89
110
  createPromisePortal,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promise-portal",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "let you use react portal in vue, and with promise",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -22,7 +22,7 @@
22
22
  "devDependencies": {
23
23
  "tsup": "^6.2.3",
24
24
  "typescript": "^4.8.2",
25
- "vue": "^3.2.37"
25
+ "vue": "^3.3.8"
26
26
  },
27
27
  "author": {
28
28
  "name": "tjyuanpeng",