promise-portal 1.0.3 → 1.0.5
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 +95 -6
- package/dist/index.cjs +124 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +94 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,12 +15,71 @@ npm install promise-portal -D
|
|
|
15
15
|
yarn add promise-portal --D
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Online Demo
|
|
19
|
+
|
|
20
|
+
[Demo on codesandbox](https://codesandbox.io/p/github/tjyuanpeng/promise-portal)
|
|
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
|
|
28
|
+
|
|
29
|
+
like element-plus, the modal is a vue component
|
|
30
|
+
|
|
31
|
+
in development, we want use modal like a function
|
|
32
|
+
|
|
33
|
+
no `show` property to control show/hide, gettting result is more explicit
|
|
34
|
+
|
|
35
|
+
easier to control workflow, and easier to handle life-cycles
|
|
36
|
+
|
|
37
|
+
### Before
|
|
38
|
+
|
|
39
|
+
use as acomponent, with ref value to control visibility and life-cycles
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
<script setup lang="ts">
|
|
43
|
+
import Comp from './components/name.vue'
|
|
44
|
+
const show = ref(false)
|
|
45
|
+
const onClick = () => {
|
|
46
|
+
show.value = true
|
|
47
|
+
}
|
|
48
|
+
const onClosed = () => {
|
|
49
|
+
show.value = false
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
<template>
|
|
53
|
+
<el-button @click="onClick"> click to open the Dialog </el-button>
|
|
54
|
+
<Comp v-model="show" @closed="onClosed"> a dialog content </Comp>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### After
|
|
59
|
+
|
|
60
|
+
use as a normal promise-style function, so happy to develop
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
<script setup lang="ts">
|
|
64
|
+
import Comp, { Input, Output } from './components/name.vue'
|
|
65
|
+
const func = definePortal<Output, Input>(Comp)
|
|
66
|
+
const onClick = async () => {
|
|
67
|
+
const data = await func()
|
|
68
|
+
console.log(data)
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
<template>
|
|
72
|
+
<el-button @click="onClick"> click to open the Dialog </el-button>
|
|
73
|
+
</template>
|
|
74
|
+
```
|
|
75
|
+
|
|
18
76
|
## Use
|
|
19
77
|
|
|
20
78
|
### install in the entry file
|
|
21
79
|
|
|
22
80
|
```ts
|
|
23
81
|
// ./main.ts
|
|
82
|
+
import { createApp } from 'vue'
|
|
24
83
|
import { createPromisePortal } from 'promise-portal'
|
|
25
84
|
|
|
26
85
|
const app = createApp(App)
|
|
@@ -60,11 +119,19 @@ const onClick = async () => {
|
|
|
60
119
|
|
|
61
120
|
- createPromisePortal
|
|
62
121
|
|
|
63
|
-
create promise-portal instance
|
|
122
|
+
create promise-portal instance, set to vue instance
|
|
64
123
|
|
|
65
124
|
```ts
|
|
66
125
|
const instance = createPromisePortal()
|
|
67
|
-
app.use(instance)
|
|
126
|
+
app.use(instance)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
you can set default config to instance
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const instance = createPromisePortal({
|
|
133
|
+
unmountDelay: 100,
|
|
134
|
+
})
|
|
68
135
|
```
|
|
69
136
|
|
|
70
137
|
- getActiveInstance
|
|
@@ -88,11 +155,12 @@ const onClick = async () => {
|
|
|
88
155
|
a vue composition api, use in portal component to get context of portal
|
|
89
156
|
|
|
90
157
|
```ts
|
|
91
|
-
const { resolve, reject, el, vNode } = usePortalContext()
|
|
158
|
+
const { resolve, reject, el, vNode, setUnmountDelay } = usePortalContext()
|
|
92
159
|
// resolve: promise resolve handler
|
|
93
160
|
// reject: promise reject handler
|
|
94
161
|
// el: portal base element, injecting to body element
|
|
95
162
|
// vNode: portal base vue vnode
|
|
163
|
+
// setUnmountDelay: set unmount delay to this portal
|
|
96
164
|
```
|
|
97
165
|
|
|
98
166
|
you can use typescript generic types
|
|
@@ -169,8 +237,29 @@ const onClick = async () => {
|
|
|
169
237
|
})
|
|
170
238
|
```
|
|
171
239
|
|
|
172
|
-
|
|
240
|
+
- detectPromisePortalInstance
|
|
241
|
+
|
|
242
|
+
Check if the instance has been properly destroyed
|
|
173
243
|
|
|
174
|
-
|
|
244
|
+
```ts
|
|
245
|
+
// main.ts
|
|
246
|
+
if (import.meta.env.DEV) {
|
|
247
|
+
detectPromisePortalInstance()
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
You can pass in other values to customize it.
|
|
252
|
+
|
|
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
|
+
```
|
|
261
|
+
|
|
262
|
+
## Link
|
|
175
263
|
|
|
176
|
-
[
|
|
264
|
+
- [@filez/portal](https://github.com/lenovo-filez/portal)
|
|
265
|
+
- [promise-modal](https://github.com/liruifengv/promise-modal)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
createPromisePortal: () => createPromisePortal,
|
|
24
|
+
definePortal: () => definePortal,
|
|
25
|
+
detectPromisePortalInstance: () => detectPromisePortalInstance,
|
|
26
|
+
getActiveInstance: () => getActiveInstance,
|
|
27
|
+
setActiveInstance: () => setActiveInstance,
|
|
28
|
+
usePortalContext: () => usePortalContext
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(src_exports);
|
|
31
|
+
var import_vue = require("vue");
|
|
32
|
+
var promisePortalSymbol = process.env.NODE_ENV !== "production" ? Symbol("promise-portal") : Symbol();
|
|
33
|
+
var activeInstance;
|
|
34
|
+
var getActiveInstance = () => activeInstance;
|
|
35
|
+
var setActiveInstance = (instance) => activeInstance = instance;
|
|
36
|
+
var createPromisePortal = (defaultOptions = {}) => {
|
|
37
|
+
const instance = {
|
|
38
|
+
defaultOptions,
|
|
39
|
+
app: void 0,
|
|
40
|
+
map: /* @__PURE__ */ new WeakMap(),
|
|
41
|
+
install(app) {
|
|
42
|
+
instance.app = app;
|
|
43
|
+
setActiveInstance(instance);
|
|
44
|
+
app.provide(promisePortalSymbol, instance);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return instance;
|
|
48
|
+
};
|
|
49
|
+
var usePortalContext = () => {
|
|
50
|
+
const instance = (0, import_vue.inject)(promisePortalSymbol);
|
|
51
|
+
if (!instance) {
|
|
52
|
+
throw new Error("[promise-portal]: no instance found.");
|
|
53
|
+
}
|
|
54
|
+
const ins = (0, import_vue.getCurrentInstance)();
|
|
55
|
+
if (!ins?.vnode) {
|
|
56
|
+
throw new Error("[promise-portal]: no vnode found.");
|
|
57
|
+
}
|
|
58
|
+
const data = instance.map.get(ins.vnode);
|
|
59
|
+
if (!data) {
|
|
60
|
+
throw new Error("[promise-portal]: no inject data found.");
|
|
61
|
+
}
|
|
62
|
+
return data;
|
|
63
|
+
};
|
|
64
|
+
var definePortal = (component, { instance, unmountDelay } = {}) => {
|
|
65
|
+
const _instance = instance || (0, import_vue.getCurrentInstance)() && (0, import_vue.inject)(promisePortalSymbol) || activeInstance;
|
|
66
|
+
if (!_instance) {
|
|
67
|
+
throw new Error("[promise-portal]: no instance found. Do you forget install promise-portal?");
|
|
68
|
+
}
|
|
69
|
+
return (props, children) => {
|
|
70
|
+
let el = document.createElement("div");
|
|
71
|
+
el.setAttribute("data-promise-portal-container", "");
|
|
72
|
+
document.body.appendChild(el);
|
|
73
|
+
let _delay = unmountDelay || _instance.defaultOptions?.unmountDelay;
|
|
74
|
+
const setUnmountDelay = (delay) => {
|
|
75
|
+
_delay = delay;
|
|
76
|
+
};
|
|
77
|
+
let vNode;
|
|
78
|
+
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);
|
|
83
|
+
});
|
|
84
|
+
p.finally(() => {
|
|
85
|
+
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);
|
|
93
|
+
});
|
|
94
|
+
return p;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
var detectPromisePortalInstance = ({
|
|
98
|
+
style = "position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;",
|
|
99
|
+
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.`
|
|
100
|
+
} = {}) => {
|
|
101
|
+
const nodes = document.querySelectorAll("[data-promise-portal-container]");
|
|
102
|
+
if (nodes.length > 0) {
|
|
103
|
+
let el = document.querySelector("[data-promise-portal-detector]");
|
|
104
|
+
if (!el) {
|
|
105
|
+
el = document.createElement("div");
|
|
106
|
+
el.setAttribute("data-promise-portal-detector", "");
|
|
107
|
+
el.setAttribute("style", style);
|
|
108
|
+
el.innerHTML = text;
|
|
109
|
+
document.body.appendChild(el);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
document.querySelector("[data-promise-portal-detector]")?.remove();
|
|
113
|
+
}
|
|
114
|
+
setTimeout(detectPromisePortalInstance, 200);
|
|
115
|
+
};
|
|
116
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
117
|
+
0 && (module.exports = {
|
|
118
|
+
createPromisePortal,
|
|
119
|
+
definePortal,
|
|
120
|
+
detectPromisePortalInstance,
|
|
121
|
+
getActiveInstance,
|
|
122
|
+
setActiveInstance,
|
|
123
|
+
usePortalContext
|
|
124
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { VNode, App, Component } from 'vue';
|
|
2
|
+
|
|
3
|
+
interface PortalContext<R> {
|
|
4
|
+
resolve: (value: R | PromiseLike<R>) => void;
|
|
5
|
+
reject: (reason?: any) => void;
|
|
6
|
+
el: HTMLDivElement;
|
|
7
|
+
vNode: VNode;
|
|
8
|
+
setUnmountDelay: (unmountDelay: number) => void;
|
|
9
|
+
}
|
|
10
|
+
interface PromisePortal<R = any> {
|
|
11
|
+
defaultOptions: {
|
|
12
|
+
unmountDelay?: number;
|
|
13
|
+
};
|
|
14
|
+
app: App;
|
|
15
|
+
map: WeakMap<VNode, PortalContext<R>>;
|
|
16
|
+
install: (app: App) => void;
|
|
17
|
+
}
|
|
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
|
+
declare const detectPromisePortalInstance: ({ style, text, }?: {
|
|
28
|
+
style?: string | undefined;
|
|
29
|
+
text?: string | undefined;
|
|
30
|
+
}) => void;
|
|
31
|
+
|
|
32
|
+
export { PortalContext, PortalOptions, PromisePortal, createPromisePortal, definePortal, detectPromisePortalInstance, getActiveInstance, setActiveInstance, usePortalContext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createVNode, render, inject, getCurrentInstance } from "vue";
|
|
3
|
+
var promisePortalSymbol = process.env.NODE_ENV !== "production" ? Symbol("promise-portal") : Symbol();
|
|
4
|
+
var activeInstance;
|
|
5
|
+
var getActiveInstance = () => activeInstance;
|
|
6
|
+
var setActiveInstance = (instance) => activeInstance = instance;
|
|
7
|
+
var createPromisePortal = (defaultOptions = {}) => {
|
|
8
|
+
const instance = {
|
|
9
|
+
defaultOptions,
|
|
10
|
+
app: void 0,
|
|
11
|
+
map: /* @__PURE__ */ new WeakMap(),
|
|
12
|
+
install(app) {
|
|
13
|
+
instance.app = app;
|
|
14
|
+
setActiveInstance(instance);
|
|
15
|
+
app.provide(promisePortalSymbol, instance);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return instance;
|
|
19
|
+
};
|
|
20
|
+
var usePortalContext = () => {
|
|
21
|
+
const instance = inject(promisePortalSymbol);
|
|
22
|
+
if (!instance) {
|
|
23
|
+
throw new Error("[promise-portal]: no instance found.");
|
|
24
|
+
}
|
|
25
|
+
const ins = getCurrentInstance();
|
|
26
|
+
if (!ins?.vnode) {
|
|
27
|
+
throw new Error("[promise-portal]: no vnode found.");
|
|
28
|
+
}
|
|
29
|
+
const data = instance.map.get(ins.vnode);
|
|
30
|
+
if (!data) {
|
|
31
|
+
throw new Error("[promise-portal]: no inject data found.");
|
|
32
|
+
}
|
|
33
|
+
return data;
|
|
34
|
+
};
|
|
35
|
+
var definePortal = (component, { instance, unmountDelay } = {}) => {
|
|
36
|
+
const _instance = instance || getCurrentInstance() && inject(promisePortalSymbol) || activeInstance;
|
|
37
|
+
if (!_instance) {
|
|
38
|
+
throw new Error("[promise-portal]: no instance found. Do you forget install promise-portal?");
|
|
39
|
+
}
|
|
40
|
+
return (props, children) => {
|
|
41
|
+
let el = document.createElement("div");
|
|
42
|
+
el.setAttribute("data-promise-portal-container", "");
|
|
43
|
+
document.body.appendChild(el);
|
|
44
|
+
let _delay = unmountDelay || _instance.defaultOptions?.unmountDelay;
|
|
45
|
+
const setUnmountDelay = (delay) => {
|
|
46
|
+
_delay = delay;
|
|
47
|
+
};
|
|
48
|
+
let vNode;
|
|
49
|
+
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);
|
|
54
|
+
});
|
|
55
|
+
p.finally(() => {
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
if (el) {
|
|
58
|
+
render(null, el);
|
|
59
|
+
document.body.removeChild(el);
|
|
60
|
+
}
|
|
61
|
+
el = null;
|
|
62
|
+
vNode = null;
|
|
63
|
+
}, _delay);
|
|
64
|
+
});
|
|
65
|
+
return p;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
var detectPromisePortalInstance = ({
|
|
69
|
+
style = "position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;",
|
|
70
|
+
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.`
|
|
71
|
+
} = {}) => {
|
|
72
|
+
const nodes = document.querySelectorAll("[data-promise-portal-container]");
|
|
73
|
+
if (nodes.length > 0) {
|
|
74
|
+
let el = document.querySelector("[data-promise-portal-detector]");
|
|
75
|
+
if (!el) {
|
|
76
|
+
el = document.createElement("div");
|
|
77
|
+
el.setAttribute("data-promise-portal-detector", "");
|
|
78
|
+
el.setAttribute("style", style);
|
|
79
|
+
el.innerHTML = text;
|
|
80
|
+
document.body.appendChild(el);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
document.querySelector("[data-promise-portal-detector]")?.remove();
|
|
84
|
+
}
|
|
85
|
+
setTimeout(detectPromisePortalInstance, 200);
|
|
86
|
+
};
|
|
87
|
+
export {
|
|
88
|
+
createPromisePortal,
|
|
89
|
+
definePortal,
|
|
90
|
+
detectPromisePortalInstance,
|
|
91
|
+
getActiveInstance,
|
|
92
|
+
setActiveInstance,
|
|
93
|
+
usePortalContext
|
|
94
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "promise-portal",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "let you use react portal in vue, and with promise",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|