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 +208 -153
- package/dist/index.cjs +61 -38
- package/dist/index.d.ts +23 -20
- package/dist/index.js +61 -40
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,30 +1,25 @@
|
|
|
1
1
|
# promise-portal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
use component like a promisd-like function
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
// pnpm
|
|
9
|
-
pnpm add promise-portal
|
|
9
|
+
pnpm add promise-portal
|
|
10
10
|
|
|
11
11
|
// npm
|
|
12
|
-
npm install promise-portal
|
|
12
|
+
npm install promise-portal
|
|
13
13
|
|
|
14
14
|
// yarn
|
|
15
|
-
yarn add promise-portal
|
|
15
|
+
yarn add promise-portal
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Online Demo
|
|
19
19
|
|
|
20
|
-
[
|
|
20
|
+
[https://codesandbox.io/p/github/tjyuanpeng/promise-portal](https://codesandbox.io/p/github/tjyuanpeng/promise-portal)
|
|
21
21
|
|
|
22
|
-
##
|
|
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
|
-
|
|
32
|
+
so you can use Promise-Portal to save your life-time
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
### before
|
|
40
35
|
|
|
41
|
-
|
|
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
|
-
###
|
|
55
|
+
### after
|
|
59
56
|
|
|
60
57
|
use as a normal promise-style function, so happy to develop
|
|
61
58
|
|
|
62
|
-
```
|
|
59
|
+
```vue
|
|
63
60
|
<script setup lang="ts">
|
|
64
|
-
import Comp
|
|
65
|
-
const func = definePortal
|
|
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">
|
|
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
|
-
###
|
|
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(
|
|
83
|
+
app.use(
|
|
84
|
+
createPromisePortal({
|
|
85
|
+
unmountDelay: 200,
|
|
86
|
+
})
|
|
87
|
+
)
|
|
87
88
|
```
|
|
88
89
|
|
|
89
|
-
### in component, use `usePortalContext` to
|
|
90
|
+
### in component, use `usePortalContext` to use portal context
|
|
90
91
|
|
|
91
|
-
```
|
|
92
|
-
|
|
92
|
+
```vue
|
|
93
|
+
<script setup lang="ts">
|
|
94
|
+
// ./components/comp.vue
|
|
93
95
|
import { usePortalContext } from 'promise-portal'
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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/
|
|
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
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
130
|
+
### createPromisePortal
|
|
121
131
|
|
|
122
|
-
|
|
132
|
+
create promise-portal instance, set to vue instance
|
|
123
133
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
```ts
|
|
135
|
+
const instance = createPromisePortal()
|
|
136
|
+
app.use(instance)
|
|
137
|
+
```
|
|
128
138
|
|
|
129
|
-
|
|
139
|
+
you can set default options to instance
|
|
130
140
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
152
|
+
### getActiveInstance
|
|
138
153
|
|
|
139
|
-
|
|
154
|
+
get active promise-portal instance
|
|
140
155
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
156
|
+
```ts
|
|
157
|
+
const instance = getActiveInstance()
|
|
158
|
+
```
|
|
144
159
|
|
|
145
|
-
|
|
160
|
+
### setActiveInstance
|
|
146
161
|
|
|
147
|
-
|
|
162
|
+
set active promise-portal instance
|
|
148
163
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
164
|
+
```ts
|
|
165
|
+
setActiveInstance(instance)
|
|
166
|
+
```
|
|
152
167
|
|
|
153
|
-
|
|
168
|
+
### usePortalContext
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
a vue composition api, use in portal component to get context of portal
|
|
156
171
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
//
|
|
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
|
-
|
|
193
|
+
you can use typescript generic types to promise fulfilled result
|
|
167
194
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
205
|
+
you can use `show` to control modal component
|
|
174
206
|
|
|
175
|
-
|
|
207
|
+
before `unmount`, `show.value = false` will be setted
|
|
176
208
|
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
186
|
-
// component.vue
|
|
187
|
-
export interface Input {
|
|
188
|
-
firstName: string
|
|
189
|
-
lastName: string
|
|
190
|
-
}
|
|
220
|
+
### definePortal
|
|
191
221
|
|
|
192
|
-
|
|
193
|
-
fullName: string
|
|
194
|
-
confirm: boolean
|
|
195
|
-
}
|
|
222
|
+
define a portal, return a portal function
|
|
196
223
|
|
|
197
|
-
|
|
198
|
-
|
|
224
|
+
```ts
|
|
225
|
+
import Comp from './component.vue'
|
|
226
|
+
const portalFunc = definePortal(Comp)
|
|
227
|
+
portalFunc()
|
|
228
|
+
```
|
|
199
229
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
244
|
+
const props = defineProps<Input>()
|
|
245
|
+
const { resolve } = usePortalContext<Output>()
|
|
241
246
|
|
|
242
|
-
|
|
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
|
-
|
|
245
|
-
// main.ts
|
|
246
|
-
if (import.meta.env.DEV) {
|
|
247
|
-
detectPromisePortalInstance()
|
|
248
|
-
}
|
|
249
|
-
```
|
|
256
|
+
define a portal with empty parameter
|
|
250
257
|
|
|
251
|
-
|
|
258
|
+
```ts
|
|
259
|
+
// component.vue
|
|
260
|
+
export interface Output {
|
|
261
|
+
fullName: string
|
|
262
|
+
confirm: boolean
|
|
263
|
+
}
|
|
252
264
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
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
|
|
55
|
-
if (!
|
|
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(
|
|
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,
|
|
65
|
-
const
|
|
66
|
-
if (!
|
|
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
|
-
|
|
70
|
-
|
|
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
|
|
87
|
+
let unmountDelay = options.unmountDelay ?? instance.defaultOptions.unmountDelay;
|
|
74
88
|
const setUnmountDelay = (delay) => {
|
|
75
|
-
|
|
89
|
+
unmountDelay = delay;
|
|
76
90
|
};
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
} =
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
document.
|
|
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
|
-
|
|
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
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { VNode, Ref, App, Component } from 'vue';
|
|
2
3
|
|
|
3
|
-
interface
|
|
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
|
-
|
|
12
|
+
vnode: VNode;
|
|
8
13
|
setUnmountDelay: (unmountDelay: number) => void;
|
|
14
|
+
show: Ref<boolean>;
|
|
9
15
|
}
|
|
10
|
-
interface
|
|
11
|
-
defaultOptions:
|
|
12
|
-
unmountDelay?: number;
|
|
13
|
-
};
|
|
16
|
+
interface Instance<R = any> {
|
|
17
|
+
defaultOptions: Options;
|
|
14
18
|
app: App;
|
|
15
|
-
map: WeakMap<VNode,
|
|
19
|
+
map: WeakMap<VNode, Context<R>>;
|
|
16
20
|
install: (app: App) => void;
|
|
17
21
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
declare const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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: (
|
|
34
|
+
declare const detectPromisePortalInstance: (options?: DetectorOptions) => () => void;
|
|
32
35
|
|
|
33
|
-
export {
|
|
36
|
+
export { Context, DetectorOptions, Instance, Options, createPromisePortal, definePortal, detectPromisePortalInstance, getActiveInstance, setActiveInstance, usePortalContext };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// src/
|
|
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
|
|
26
|
-
if (!
|
|
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(
|
|
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,
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
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
|
-
|
|
41
|
-
|
|
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
|
|
56
|
+
let unmountDelay = options.unmountDelay ?? instance.defaultOptions.unmountDelay;
|
|
45
57
|
const setUnmountDelay = (delay) => {
|
|
46
|
-
|
|
58
|
+
unmountDelay = delay;
|
|
47
59
|
};
|
|
48
|
-
|
|
60
|
+
const show = ref(options.initialShowValue ?? instance.defaultOptions.initialShowValue ?? true);
|
|
61
|
+
let vnode;
|
|
49
62
|
const p = new Promise((resolve, reject) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
} =
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
document.
|
|
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
|
-
|
|
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
|
|
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.
|
|
25
|
+
"vue": "^3.3.8"
|
|
26
26
|
},
|
|
27
27
|
"author": {
|
|
28
28
|
"name": "tjyuanpeng",
|