promise-portal 1.0.6 → 1.2.1
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 +243 -153
- package/dist/index.cjs +80 -40
- package/dist/index.d.ts +28 -20
- package/dist/index.js +77 -41
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,30 +1,25 @@
|
|
|
1
1
|
# promise-portal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
use component as 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,276 @@ 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
|
-
###
|
|
90
|
+
### use `ContextProvider` to set context globally
|
|
90
91
|
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
```vue
|
|
93
|
+
<!-- ./App.vue -->
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
import locale from 'ant-design-vue/es/locale/zh_CN'
|
|
96
|
+
import { ContextProvider } from 'promise-portal'
|
|
97
|
+
</script>
|
|
98
|
+
<template>
|
|
99
|
+
<a-config-provider :locale="locale">
|
|
100
|
+
<ContextProvider>
|
|
101
|
+
<router-view></router-view>
|
|
102
|
+
</ContextProvider>
|
|
103
|
+
</a-config-provider>
|
|
104
|
+
</template>
|
|
105
|
+
```
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
### in component, use `usePortalContext` to use portal context
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<!-- ./components/comp.vue -->
|
|
111
|
+
<script setup lang="ts">
|
|
112
|
+
import { usePortalContext } from 'promise-portal'
|
|
113
|
+
export interface Output {
|
|
114
|
+
confirm: boolean
|
|
98
115
|
}
|
|
116
|
+
export interface Output {
|
|
117
|
+
input: string
|
|
118
|
+
}
|
|
119
|
+
const props = defineProps<Input>()
|
|
120
|
+
const { resolve, show } = usePortalContext<Output>()
|
|
121
|
+
const onCancel = () => {
|
|
122
|
+
resolve({ confirm: false })
|
|
123
|
+
}
|
|
124
|
+
</script>
|
|
125
|
+
<template>
|
|
126
|
+
<a-modal v-model:open="show" @cancel="resolve">{{ props.input }}</a-modal>
|
|
127
|
+
</template>
|
|
99
128
|
```
|
|
100
129
|
|
|
101
|
-
### define portal, use it like a promise-style function
|
|
130
|
+
### define portal in anywhere, then use it like a promise-style function
|
|
102
131
|
|
|
103
132
|
```ts
|
|
104
133
|
// ./App.vue
|
|
105
134
|
import { definePortal } from 'promise-portal'
|
|
106
|
-
import Comp, { Input, Output } from './components/
|
|
107
|
-
|
|
108
|
-
const func = definePortal<Output, Input>(Comp)
|
|
135
|
+
import Comp, { Input, Output } from './components/comp.vue'
|
|
136
|
+
const [func] = definePortal<Output, Input>(Comp)
|
|
109
137
|
const onClick = async () => {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
console.log(data)
|
|
138
|
+
const result = await func({
|
|
139
|
+
input: 'foo',
|
|
140
|
+
})
|
|
141
|
+
console.log(result)
|
|
115
142
|
}
|
|
116
143
|
```
|
|
117
144
|
|
|
118
145
|
## API Reference
|
|
119
146
|
|
|
120
|
-
|
|
147
|
+
### createPromisePortal
|
|
148
|
+
|
|
149
|
+
create promise-portal instance, set to vue instance
|
|
121
150
|
|
|
122
|
-
|
|
151
|
+
```ts
|
|
152
|
+
const instance = createPromisePortal()
|
|
153
|
+
app.use(instance)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
you can set default options to instance
|
|
123
157
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
```ts
|
|
159
|
+
const instance = createPromisePortal({
|
|
160
|
+
// set a time gap before portal unmount,
|
|
161
|
+
// in general, it is to wait for animation effect
|
|
162
|
+
unmountDelay: 200,
|
|
163
|
+
|
|
164
|
+
// initial value to property show, default value is true
|
|
165
|
+
initialShowValue: true,
|
|
166
|
+
})
|
|
167
|
+
```
|
|
128
168
|
|
|
129
|
-
|
|
169
|
+
### getActiveInstance
|
|
130
170
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
171
|
+
get active promise-portal instance
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const instance = getActiveInstance()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### setActiveInstance
|
|
178
|
+
|
|
179
|
+
set active promise-portal instance
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
setActiveInstance(instance)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### ContextProvider
|
|
186
|
+
|
|
187
|
+
a component to set context globally
|
|
188
|
+
|
|
189
|
+
```vue
|
|
190
|
+
<script setup lang="ts">
|
|
191
|
+
import locale from 'ant-design-vue/es/locale/zh_CN'
|
|
192
|
+
import { ContextProvider } from 'promise-portal'
|
|
193
|
+
</script>
|
|
194
|
+
<template>
|
|
195
|
+
<a-config-provider :locale="locale">
|
|
196
|
+
<ContextProvider>
|
|
197
|
+
<router-view></router-view>
|
|
198
|
+
</ContextProvider>
|
|
199
|
+
</a-config-provider>
|
|
200
|
+
</template>
|
|
201
|
+
```
|
|
136
202
|
|
|
137
|
-
|
|
203
|
+
### usePortalContext
|
|
138
204
|
|
|
139
|
-
|
|
205
|
+
a vue composition api, use in portal component to get context of portal
|
|
140
206
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
207
|
+
```ts
|
|
208
|
+
const { resolve } = usePortalContext()
|
|
209
|
+
|
|
210
|
+
// detail
|
|
211
|
+
const {
|
|
212
|
+
resolve, // promise resolve handler
|
|
213
|
+
reject, // promise reject handler
|
|
214
|
+
el, // portal base element, injecting to body element
|
|
215
|
+
vnode, // portal base vue vnode
|
|
216
|
+
setUnmountDelay, // set delay to unmount
|
|
217
|
+
show, // a ref value to use in modal component
|
|
218
|
+
} = usePortalContext({
|
|
219
|
+
// set a time gap before portal unmount,
|
|
220
|
+
// in general, it is to wait for animation effect
|
|
221
|
+
unmountDelay: 200,
|
|
222
|
+
|
|
223
|
+
// initial value to property show above, default value is true
|
|
224
|
+
initialShowValue: true,
|
|
225
|
+
})
|
|
226
|
+
```
|
|
144
227
|
|
|
145
|
-
|
|
228
|
+
you can use typescript generic types to promise fulfilled result
|
|
146
229
|
|
|
147
|
-
|
|
230
|
+
```ts
|
|
231
|
+
export interface Output {
|
|
232
|
+
confirm: boolean
|
|
233
|
+
}
|
|
234
|
+
const { resolve } = usePortalContext<Output>()
|
|
235
|
+
resolve({
|
|
236
|
+
confirm: true,
|
|
237
|
+
})
|
|
238
|
+
```
|
|
148
239
|
|
|
149
|
-
|
|
150
|
-
setActiveInstance(instance)
|
|
151
|
-
```
|
|
240
|
+
you can use `show` to control modal component
|
|
152
241
|
|
|
153
|
-
|
|
242
|
+
before `unmount`, `show.value = false` will be setted
|
|
154
243
|
|
|
155
|
-
|
|
244
|
+
use `initialShowValue` to set inital value, default inital value is `true`
|
|
156
245
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
246
|
+
```vue
|
|
247
|
+
<script setup lang="ts">
|
|
248
|
+
const { resolve, show } = usePortalContext<Output>({ initialShowValue: true })
|
|
249
|
+
</script>
|
|
250
|
+
<template>
|
|
251
|
+
<a-modal v-model:open="show" @cancel="resolve"></a-modal>
|
|
252
|
+
</template>
|
|
253
|
+
```
|
|
165
254
|
|
|
166
|
-
|
|
255
|
+
### definePortal
|
|
167
256
|
|
|
168
|
-
|
|
169
|
-
const { resolve } = usePortalContext<Output>()
|
|
170
|
-
resolve({ ... }) // an object of type Output
|
|
171
|
-
```
|
|
257
|
+
define a portal, return a portal function
|
|
172
258
|
|
|
173
|
-
|
|
259
|
+
```ts
|
|
260
|
+
import Comp from './component.vue'
|
|
261
|
+
const portalFunc = definePortal(Comp)
|
|
262
|
+
portalFunc()
|
|
263
|
+
```
|
|
174
264
|
|
|
175
|
-
|
|
265
|
+
you can define generic types to check input object and output object
|
|
176
266
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
267
|
+
```ts
|
|
268
|
+
// component.vue
|
|
269
|
+
export interface Input {
|
|
270
|
+
firstName: string
|
|
271
|
+
lastName: string
|
|
272
|
+
}
|
|
182
273
|
|
|
183
|
-
|
|
274
|
+
export interface Output {
|
|
275
|
+
fullName: string
|
|
276
|
+
confirm: boolean
|
|
277
|
+
}
|
|
184
278
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
export interface Input {
|
|
188
|
-
firstName: string
|
|
189
|
-
lastName: string
|
|
190
|
-
}
|
|
279
|
+
const props = defineProps<Input>()
|
|
280
|
+
const { resolve } = usePortalContext<Output>()
|
|
191
281
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
282
|
+
// App.vue
|
|
283
|
+
import Comp, { Input, Output } from './component.vue'
|
|
284
|
+
const portal = definePortal<Output, Input>(Comp)
|
|
285
|
+
const output = await portal({
|
|
286
|
+
firstName: 'joe',
|
|
287
|
+
lastName: 'watson',
|
|
288
|
+
})
|
|
289
|
+
```
|
|
196
290
|
|
|
197
|
-
|
|
198
|
-
const { resolve } = usePortalContext<Output>()
|
|
291
|
+
define a portal with empty parameter
|
|
199
292
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
```
|
|
293
|
+
```ts
|
|
294
|
+
// component.vue
|
|
295
|
+
export interface Output {
|
|
296
|
+
fullName: string
|
|
297
|
+
confirm: boolean
|
|
298
|
+
}
|
|
239
299
|
|
|
240
|
-
|
|
300
|
+
const { resolve } = usePortalContext<Output>()
|
|
241
301
|
|
|
242
|
-
|
|
302
|
+
// App.vue
|
|
303
|
+
import Comp, { Output } from './component.vue'
|
|
304
|
+
const portal = definePortal<Output, void>(Comp)
|
|
305
|
+
const output = await portal() // only allow empty parameter
|
|
306
|
+
```
|
|
243
307
|
|
|
244
|
-
|
|
245
|
-
// main.ts
|
|
246
|
-
if (import.meta.env.DEV) {
|
|
247
|
-
detectPromisePortalInstance()
|
|
248
|
-
}
|
|
249
|
-
```
|
|
308
|
+
you can set a options to definePortal
|
|
250
309
|
|
|
251
|
-
|
|
310
|
+
```ts
|
|
311
|
+
definePortal(Comp, {
|
|
312
|
+
// set a time gap before portal unmount,
|
|
313
|
+
unmountDelay: 200,
|
|
252
314
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
315
|
+
// initial value to property show
|
|
316
|
+
initialShowValue: true,
|
|
317
|
+
|
|
318
|
+
// set promise-portal instance explicitly to render this portal
|
|
319
|
+
instance: promisePortalInstance,
|
|
320
|
+
})
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### detectPromisePortalInstance
|
|
324
|
+
|
|
325
|
+
detect whether the instance has been properly destroyed
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
// main.ts
|
|
329
|
+
if (import.meta.env.DEV) {
|
|
330
|
+
detectPromisePortalInstance()
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
the return value is a function to stop detecting
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
const stopHandler = detectPromisePortalInstance()
|
|
338
|
+
stopHandler() // stop detecting
|
|
339
|
+
```
|
|
261
340
|
|
|
262
|
-
|
|
341
|
+
You can pass in other values to customize it.
|
|
263
342
|
|
|
343
|
+
```ts
|
|
344
|
+
detectPromisePortalInstance({
|
|
345
|
+
text: 'Detected unreleased promise-portal instance',
|
|
346
|
+
style: ' /styles you like/ ',
|
|
347
|
+
})
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
# Relative Resourece
|
|
351
|
+
|
|
352
|
+
- [react protal](https://reactjs.org/docs/portals.html)
|
|
353
|
+
- [vue teleport](https://vuejs.org/guide/built-ins/teleport.html)
|
|
264
354
|
- [@filez/portal](https://github.com/lenovo-filez/portal)
|
|
265
355
|
- [promise-modal](https://github.com/liruifengv/promise-modal)
|
package/dist/index.cjs
CHANGED
|
@@ -20,16 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
ContextProvider: () => ContextProvider,
|
|
23
24
|
createPromisePortal: () => createPromisePortal,
|
|
24
25
|
definePortal: () => definePortal,
|
|
25
26
|
detectPromisePortalInstance: () => detectPromisePortalInstance,
|
|
26
27
|
getActiveInstance: () => getActiveInstance,
|
|
27
28
|
setActiveInstance: () => setActiveInstance,
|
|
28
|
-
usePortalContext: () => usePortalContext
|
|
29
|
+
usePortalContext: () => usePortalContext,
|
|
30
|
+
version: () => version
|
|
29
31
|
});
|
|
30
32
|
module.exports = __toCommonJS(src_exports);
|
|
33
|
+
|
|
34
|
+
// src/portal.ts
|
|
31
35
|
var import_vue = require("vue");
|
|
32
36
|
var promisePortalSymbol = process.env.NODE_ENV !== "production" ? Symbol("promise-portal") : Symbol();
|
|
37
|
+
var version = `1.2.1`;
|
|
33
38
|
var activeInstance;
|
|
34
39
|
var getActiveInstance = () => activeInstance;
|
|
35
40
|
var setActiveInstance = (instance) => activeInstance = instance;
|
|
@@ -38,6 +43,7 @@ var createPromisePortal = (defaultOptions = {}) => {
|
|
|
38
43
|
defaultOptions,
|
|
39
44
|
app: void 0,
|
|
40
45
|
map: /* @__PURE__ */ new WeakMap(),
|
|
46
|
+
provides: void 0,
|
|
41
47
|
install(app) {
|
|
42
48
|
instance.app = app;
|
|
43
49
|
setActiveInstance(instance);
|
|
@@ -46,80 +52,114 @@ var createPromisePortal = (defaultOptions = {}) => {
|
|
|
46
52
|
};
|
|
47
53
|
return instance;
|
|
48
54
|
};
|
|
49
|
-
var usePortalContext = () => {
|
|
55
|
+
var usePortalContext = (options = {}) => {
|
|
50
56
|
const instance = (0, import_vue.inject)(promisePortalSymbol);
|
|
51
57
|
if (!instance) {
|
|
52
58
|
throw new Error("[promise-portal]: no instance found.");
|
|
53
59
|
}
|
|
54
|
-
const
|
|
55
|
-
if (!
|
|
60
|
+
const vnode = (0, import_vue.getCurrentInstance)()?.vnode;
|
|
61
|
+
if (!vnode) {
|
|
56
62
|
throw new Error("[promise-portal]: no vnode found.");
|
|
57
63
|
}
|
|
58
|
-
const data = instance.map.get(
|
|
64
|
+
const data = instance.map.get(vnode);
|
|
59
65
|
if (!data) {
|
|
60
66
|
throw new Error("[promise-portal]: no inject data found.");
|
|
61
67
|
}
|
|
68
|
+
if (options.unmountDelay != void 0) {
|
|
69
|
+
data.setUnmountDelay(options.unmountDelay);
|
|
70
|
+
}
|
|
71
|
+
if (options.initialShowValue != void 0) {
|
|
72
|
+
data.show.value = options.initialShowValue;
|
|
73
|
+
}
|
|
62
74
|
return data;
|
|
63
75
|
};
|
|
64
|
-
var definePortal = (component,
|
|
65
|
-
const
|
|
66
|
-
if (!
|
|
76
|
+
var definePortal = (component, options = {}) => {
|
|
77
|
+
const instance = options.instance ?? ((0, import_vue.getCurrentInstance)() && (0, import_vue.inject)(promisePortalSymbol)) ?? getActiveInstance();
|
|
78
|
+
if (!instance) {
|
|
67
79
|
throw new Error("[promise-portal]: no instance found. Do you forget install promise-portal?");
|
|
68
80
|
}
|
|
69
|
-
|
|
70
|
-
|
|
81
|
+
let appContext = (0, import_vue.getCurrentInstance)()?.appContext;
|
|
82
|
+
let contextHolderProvides = null;
|
|
83
|
+
const ContextHolder = (0, import_vue.defineComponent)(() => () => {
|
|
84
|
+
appContext = (0, import_vue.getCurrentInstance)()?.appContext;
|
|
85
|
+
contextHolderProvides = (0, import_vue.getCurrentInstance)()?.provides;
|
|
86
|
+
});
|
|
87
|
+
const portal = (props, children) => {
|
|
88
|
+
const el = document.createElement("div");
|
|
71
89
|
el.setAttribute("data-promise-portal-container", "");
|
|
72
90
|
document.body.appendChild(el);
|
|
73
|
-
let
|
|
91
|
+
let unmountDelay = options.unmountDelay ?? instance.defaultOptions.unmountDelay;
|
|
74
92
|
const setUnmountDelay = (delay) => {
|
|
75
|
-
|
|
93
|
+
unmountDelay = delay;
|
|
76
94
|
};
|
|
77
|
-
|
|
95
|
+
const show = (0, import_vue.ref)(options.initialShowValue ?? instance.defaultOptions.initialShowValue ?? true);
|
|
96
|
+
let vnode;
|
|
78
97
|
const p = new Promise((resolve, reject) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
98
|
+
vnode = (0, import_vue.createVNode)(component, props, children);
|
|
99
|
+
instance.map.set(vnode, { resolve, reject, el, vnode, setUnmountDelay, show });
|
|
100
|
+
const ac = appContext ?? instance.app._context;
|
|
101
|
+
vnode.appContext = Object.create(ac, {
|
|
102
|
+
provides: {
|
|
103
|
+
value: contextHolderProvides ?? instance.provides ?? ac.provides
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
(0, import_vue.render)(vnode, el);
|
|
83
107
|
});
|
|
84
108
|
p.finally(() => {
|
|
109
|
+
show.value = false;
|
|
85
110
|
setTimeout(() => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
el = null;
|
|
91
|
-
vNode = null;
|
|
92
|
-
}, _delay);
|
|
111
|
+
(0, import_vue.render)(null, el);
|
|
112
|
+
document.body.removeChild(el);
|
|
113
|
+
}, unmountDelay);
|
|
93
114
|
});
|
|
94
115
|
return p;
|
|
95
116
|
};
|
|
117
|
+
return [portal, ContextHolder];
|
|
96
118
|
};
|
|
97
|
-
var
|
|
119
|
+
var ContextProvider = (0, import_vue.defineComponent)({
|
|
120
|
+
name: "PromisePortalContextProvider",
|
|
121
|
+
setup(_props, { slots }) {
|
|
122
|
+
const p = (0, import_vue.getCurrentInstance)()?.provides;
|
|
123
|
+
const instance = getActiveInstance();
|
|
124
|
+
if (p && instance) {
|
|
125
|
+
instance.provides = p;
|
|
126
|
+
}
|
|
127
|
+
return () => slots.default?.();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// src/detector.ts
|
|
132
|
+
function detect(options) {
|
|
98
133
|
const {
|
|
99
134
|
style = "position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;",
|
|
100
135
|
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.
|
|
136
|
+
} = options;
|
|
137
|
+
const containers = document.querySelectorAll("[data-promise-portal-container]");
|
|
138
|
+
const detector = document.querySelector("[data-promise-portal-detector]");
|
|
139
|
+
if (containers.length === 0) {
|
|
140
|
+
detector?.remove();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!detector) {
|
|
144
|
+
const el = document.createElement("div");
|
|
145
|
+
el.setAttribute("data-promise-portal-detector", "");
|
|
146
|
+
el.setAttribute("style", style);
|
|
147
|
+
el.innerHTML = text;
|
|
148
|
+
document.body.appendChild(el);
|
|
114
149
|
}
|
|
115
|
-
|
|
150
|
+
}
|
|
151
|
+
var detectPromisePortalInstance = (options = {}) => {
|
|
152
|
+
const timer = setInterval(() => detect(options), 200);
|
|
153
|
+
return () => clearInterval(timer);
|
|
116
154
|
};
|
|
117
155
|
// Annotate the CommonJS export names for ESM import in node:
|
|
118
156
|
0 && (module.exports = {
|
|
157
|
+
ContextProvider,
|
|
119
158
|
createPromisePortal,
|
|
120
159
|
definePortal,
|
|
121
160
|
detectPromisePortalInstance,
|
|
122
161
|
getActiveInstance,
|
|
123
162
|
setActiveInstance,
|
|
124
|
-
usePortalContext
|
|
163
|
+
usePortalContext,
|
|
164
|
+
version
|
|
125
165
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,41 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { VNode, Ref, App, Component } from 'vue';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
declare const version = "1.2.1";
|
|
5
|
+
interface Options {
|
|
6
|
+
unmountDelay?: number;
|
|
7
|
+
initialShowValue?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface Context<R> {
|
|
4
10
|
resolve: (value: R | PromiseLike<R>) => void;
|
|
5
11
|
reject: (reason?: any) => void;
|
|
6
12
|
el: HTMLDivElement;
|
|
7
|
-
|
|
13
|
+
vnode: VNode;
|
|
8
14
|
setUnmountDelay: (unmountDelay: number) => void;
|
|
15
|
+
show: Ref<boolean>;
|
|
9
16
|
}
|
|
10
|
-
interface
|
|
11
|
-
defaultOptions:
|
|
12
|
-
unmountDelay?: number;
|
|
13
|
-
};
|
|
17
|
+
interface Instance<R = any> {
|
|
18
|
+
defaultOptions: Options;
|
|
14
19
|
app: App;
|
|
15
|
-
map: WeakMap<VNode,
|
|
20
|
+
map: WeakMap<VNode, Context<R>>;
|
|
21
|
+
provides: any;
|
|
16
22
|
install: (app: App) => void;
|
|
17
23
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
declare const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
declare const
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
declare const getActiveInstance: () => Instance<any>;
|
|
25
|
+
declare const setActiveInstance: (instance: Instance) => Instance<any>;
|
|
26
|
+
declare const createPromisePortal: (defaultOptions?: Options) => Instance<any>;
|
|
27
|
+
declare const usePortalContext: <TOutput = any>(options?: Options) => Context<TOutput>;
|
|
28
|
+
declare const definePortal: <TOutput = any, TProps = any>(component: Component, options?: Options & {
|
|
29
|
+
instance?: Instance<TOutput> | undefined;
|
|
30
|
+
}) => [(props?: TProps | undefined, children?: unknown) => Promise<TOutput>, Component<any, any, any, vue.ComputedOptions, vue.MethodOptions>];
|
|
31
|
+
declare const ContextProvider: vue.DefineComponent<{}, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}>[] | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.VNodeProps & vue.AllowedComponentProps & vue.ComponentCustomProps, Readonly<vue.ExtractPropTypes<{}>>, {}, {}>;
|
|
34
|
+
|
|
35
|
+
interface DetectorOptions {
|
|
28
36
|
style?: string;
|
|
29
37
|
text?: string;
|
|
30
38
|
}
|
|
31
|
-
declare const detectPromisePortalInstance: (
|
|
39
|
+
declare const detectPromisePortalInstance: (options?: DetectorOptions) => () => void;
|
|
32
40
|
|
|
33
|
-
export {
|
|
41
|
+
export { Context, ContextProvider, DetectorOptions, Instance, Options, createPromisePortal, definePortal, detectPromisePortalInstance, getActiveInstance, setActiveInstance, usePortalContext, version };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
var version = `1.2.1`;
|
|
4
5
|
var activeInstance;
|
|
5
6
|
var getActiveInstance = () => activeInstance;
|
|
6
7
|
var setActiveInstance = (instance) => activeInstance = instance;
|
|
@@ -9,6 +10,7 @@ var createPromisePortal = (defaultOptions = {}) => {
|
|
|
9
10
|
defaultOptions,
|
|
10
11
|
app: void 0,
|
|
11
12
|
map: /* @__PURE__ */ new WeakMap(),
|
|
13
|
+
provides: void 0,
|
|
12
14
|
install(app) {
|
|
13
15
|
instance.app = app;
|
|
14
16
|
setActiveInstance(instance);
|
|
@@ -17,79 +19,113 @@ var createPromisePortal = (defaultOptions = {}) => {
|
|
|
17
19
|
};
|
|
18
20
|
return instance;
|
|
19
21
|
};
|
|
20
|
-
var usePortalContext = () => {
|
|
22
|
+
var usePortalContext = (options = {}) => {
|
|
21
23
|
const instance = inject(promisePortalSymbol);
|
|
22
24
|
if (!instance) {
|
|
23
25
|
throw new Error("[promise-portal]: no instance found.");
|
|
24
26
|
}
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
27
|
+
const vnode = getCurrentInstance()?.vnode;
|
|
28
|
+
if (!vnode) {
|
|
27
29
|
throw new Error("[promise-portal]: no vnode found.");
|
|
28
30
|
}
|
|
29
|
-
const data = instance.map.get(
|
|
31
|
+
const data = instance.map.get(vnode);
|
|
30
32
|
if (!data) {
|
|
31
33
|
throw new Error("[promise-portal]: no inject data found.");
|
|
32
34
|
}
|
|
35
|
+
if (options.unmountDelay != void 0) {
|
|
36
|
+
data.setUnmountDelay(options.unmountDelay);
|
|
37
|
+
}
|
|
38
|
+
if (options.initialShowValue != void 0) {
|
|
39
|
+
data.show.value = options.initialShowValue;
|
|
40
|
+
}
|
|
33
41
|
return data;
|
|
34
42
|
};
|
|
35
|
-
var definePortal = (component,
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
43
|
+
var definePortal = (component, options = {}) => {
|
|
44
|
+
const instance = options.instance ?? (getCurrentInstance() && inject(promisePortalSymbol)) ?? getActiveInstance();
|
|
45
|
+
if (!instance) {
|
|
38
46
|
throw new Error("[promise-portal]: no instance found. Do you forget install promise-portal?");
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
let appContext = getCurrentInstance()?.appContext;
|
|
49
|
+
let contextHolderProvides = null;
|
|
50
|
+
const ContextHolder = defineComponent(() => () => {
|
|
51
|
+
appContext = getCurrentInstance()?.appContext;
|
|
52
|
+
contextHolderProvides = getCurrentInstance()?.provides;
|
|
53
|
+
});
|
|
54
|
+
const portal = (props, children) => {
|
|
55
|
+
const el = document.createElement("div");
|
|
42
56
|
el.setAttribute("data-promise-portal-container", "");
|
|
43
57
|
document.body.appendChild(el);
|
|
44
|
-
let
|
|
58
|
+
let unmountDelay = options.unmountDelay ?? instance.defaultOptions.unmountDelay;
|
|
45
59
|
const setUnmountDelay = (delay) => {
|
|
46
|
-
|
|
60
|
+
unmountDelay = delay;
|
|
47
61
|
};
|
|
48
|
-
|
|
62
|
+
const show = ref(options.initialShowValue ?? instance.defaultOptions.initialShowValue ?? true);
|
|
63
|
+
let vnode;
|
|
49
64
|
const p = new Promise((resolve, reject) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
vnode = createVNode(component, props, children);
|
|
66
|
+
instance.map.set(vnode, { resolve, reject, el, vnode, setUnmountDelay, show });
|
|
67
|
+
const ac = appContext ?? instance.app._context;
|
|
68
|
+
vnode.appContext = Object.create(ac, {
|
|
69
|
+
provides: {
|
|
70
|
+
value: contextHolderProvides ?? instance.provides ?? ac.provides
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
render(vnode, el);
|
|
54
74
|
});
|
|
55
75
|
p.finally(() => {
|
|
76
|
+
show.value = false;
|
|
56
77
|
setTimeout(() => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
el = null;
|
|
62
|
-
vNode = null;
|
|
63
|
-
}, _delay);
|
|
78
|
+
render(null, el);
|
|
79
|
+
document.body.removeChild(el);
|
|
80
|
+
}, unmountDelay);
|
|
64
81
|
});
|
|
65
82
|
return p;
|
|
66
83
|
};
|
|
84
|
+
return [portal, ContextHolder];
|
|
67
85
|
};
|
|
68
|
-
var
|
|
86
|
+
var ContextProvider = defineComponent({
|
|
87
|
+
name: "PromisePortalContextProvider",
|
|
88
|
+
setup(_props, { slots }) {
|
|
89
|
+
const p = getCurrentInstance()?.provides;
|
|
90
|
+
const instance = getActiveInstance();
|
|
91
|
+
if (p && instance) {
|
|
92
|
+
instance.provides = p;
|
|
93
|
+
}
|
|
94
|
+
return () => slots.default?.();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// src/detector.ts
|
|
99
|
+
function detect(options) {
|
|
69
100
|
const {
|
|
70
101
|
style = "position:fixed;top:0;right:0;text-align:right;line-height:1.3;color:red;z-index:9999;",
|
|
71
102
|
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.
|
|
103
|
+
} = options;
|
|
104
|
+
const containers = document.querySelectorAll("[data-promise-portal-container]");
|
|
105
|
+
const detector = document.querySelector("[data-promise-portal-detector]");
|
|
106
|
+
if (containers.length === 0) {
|
|
107
|
+
detector?.remove();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (!detector) {
|
|
111
|
+
const el = document.createElement("div");
|
|
112
|
+
el.setAttribute("data-promise-portal-detector", "");
|
|
113
|
+
el.setAttribute("style", style);
|
|
114
|
+
el.innerHTML = text;
|
|
115
|
+
document.body.appendChild(el);
|
|
85
116
|
}
|
|
86
|
-
|
|
117
|
+
}
|
|
118
|
+
var detectPromisePortalInstance = (options = {}) => {
|
|
119
|
+
const timer = setInterval(() => detect(options), 200);
|
|
120
|
+
return () => clearInterval(timer);
|
|
87
121
|
};
|
|
88
122
|
export {
|
|
123
|
+
ContextProvider,
|
|
89
124
|
createPromisePortal,
|
|
90
125
|
definePortal,
|
|
91
126
|
detectPromisePortalInstance,
|
|
92
127
|
getActiveInstance,
|
|
93
128
|
setActiveInstance,
|
|
94
|
-
usePortalContext
|
|
129
|
+
usePortalContext,
|
|
130
|
+
version
|
|
95
131
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "promise-portal",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "use component as a promisd-like function",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -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",
|