@codeleap/portals 6.3.0 → 7.0.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/dist/factors/alert.d.ts +44 -0
- package/dist/factors/alert.d.ts.map +1 -0
- package/dist/factors/bottomSheet.d.ts +61 -0
- package/dist/factors/bottomSheet.d.ts.map +1 -0
- package/dist/factors/drawer.d.ts +32 -0
- package/dist/factors/drawer.d.ts.map +1 -0
- package/dist/factors/modal.d.ts +32 -0
- package/dist/factors/modal.d.ts.map +1 -0
- package/dist/globals.d.ts +19 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/Portal.d.ts +121 -0
- package/dist/lib/Portal.d.ts.map +1 -0
- package/dist/lib/PortalRegistry.d.ts +59 -0
- package/dist/lib/PortalRegistry.d.ts.map +1 -0
- package/dist/lib/PortalRequest.d.ts +44 -0
- package/dist/lib/PortalRequest.d.ts.map +1 -0
- package/dist/lib/PortalState.d.ts +83 -0
- package/dist/lib/PortalState.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/misc.d.ts +7 -0
- package/dist/types/misc.d.ts.map +1 -0
- package/dist/types/portal.d.ts +40 -0
- package/dist/types/portal.d.ts.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +25 -11
- package/src/factors/alert.ts +20 -60
- package/src/factors/bottomSheet.ts +20 -3
- package/src/factors/drawer.ts +11 -1
- package/src/factors/modal.ts +11 -1
- package/src/globals.ts +6 -0
- package/src/lib/Portal.tsx +62 -14
- package/src/lib/PortalRequest.ts +5 -5
- package/src/lib/PortalState.ts +64 -25
- package/src/types/misc.ts +2 -0
- package/src/types/portal.ts +11 -5
- package/src/utils.ts +9 -0
- package/package.json.bak +0 -32
package/src/lib/PortalState.ts
CHANGED
|
@@ -12,11 +12,12 @@ function initAtomWithPromise<T>(a: IAtom<T>, promise: Promise<T>) {
|
|
|
12
12
|
})
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/** Configuration for initializing portal state: initial params, visibility, reset behavior, and transition timing. */
|
|
15
16
|
export type PortalStateConfig<Params> = {
|
|
16
|
-
initialParams
|
|
17
|
-
startsOpen
|
|
17
|
+
initialParams?: Params | (() => Promise<Params>)
|
|
18
|
+
startsOpen?: boolean | (() => Promise<boolean>)
|
|
18
19
|
resetParamsOnClose?: boolean
|
|
19
|
-
transitionDuration
|
|
20
|
+
transitionDuration?: number
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -26,13 +27,32 @@ export type PortalStateConfig<Params> = {
|
|
|
26
27
|
* @template Params - Type of parameters managed by the state
|
|
27
28
|
*/
|
|
28
29
|
export class PortalState<Params = {}> {
|
|
29
|
-
|
|
30
|
+
/** Nanostores atom managing portal visibility state. */
|
|
31
|
+
visible!: IAtom<boolean>
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
/** Nanostores atom managing portal parameters. */
|
|
34
|
+
params!: IAtom<Params>
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
/** Initial parameter values used when resetting. */
|
|
37
|
+
_initialParams!: Params
|
|
34
38
|
|
|
35
|
-
private config
|
|
39
|
+
private config!: PortalStateConfig<Params>
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Asserts the portal state has been initialized and returns the initialized stores.
|
|
43
|
+
* @throws Error if `initializeState()` has not been called yet
|
|
44
|
+
*/
|
|
45
|
+
assertInitialized() {
|
|
46
|
+
if (!this.visible || !this.params || !this.config || this._initialParams === undefined) {
|
|
47
|
+
throw new Error('PortalState has not been initialized. Call initializeState() first.')
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
visible: this.visible,
|
|
51
|
+
params: this.params,
|
|
52
|
+
config: this.config,
|
|
53
|
+
initialParams: this._initialParams,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
36
56
|
|
|
37
57
|
/**
|
|
38
58
|
* Initializes the portal state with configuration.
|
|
@@ -63,18 +83,24 @@ export class PortalState<Params = {}> {
|
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
85
|
|
|
86
|
+
/** Gets the current visibility state. */
|
|
66
87
|
get isVisible() {
|
|
67
|
-
|
|
88
|
+
const { visible } = this.assertInitialized()
|
|
89
|
+
return visible.get()
|
|
68
90
|
}
|
|
69
91
|
|
|
92
|
+
/** Gets the current parameter values. */
|
|
70
93
|
get currentParams() {
|
|
71
|
-
|
|
94
|
+
const { params } = this.assertInitialized()
|
|
95
|
+
return params.get()
|
|
72
96
|
}
|
|
73
97
|
|
|
74
98
|
private awaitTransition(count = 1) {
|
|
75
|
-
|
|
99
|
+
const { config } = this.assertInitialized()
|
|
100
|
+
return awaitTransition(count, config.transitionDuration)
|
|
76
101
|
}
|
|
77
102
|
|
|
103
|
+
/** Lifecycle hook called after the portal opens. Override in subclasses to run custom logic. */
|
|
78
104
|
protected handleOpen() { }
|
|
79
105
|
|
|
80
106
|
/**
|
|
@@ -82,38 +108,45 @@ export class PortalState<Params = {}> {
|
|
|
82
108
|
* @param params - Parameters to merge with current params
|
|
83
109
|
*/
|
|
84
110
|
async open(params?: Params) {
|
|
85
|
-
|
|
111
|
+
const { visible, params: paramsAtom } = this.assertInitialized()
|
|
112
|
+
|
|
113
|
+
if (visible.get()) {
|
|
86
114
|
return
|
|
87
115
|
}
|
|
88
116
|
|
|
89
117
|
if (params) {
|
|
90
|
-
|
|
91
|
-
...
|
|
118
|
+
paramsAtom.set({
|
|
119
|
+
...paramsAtom.get(),
|
|
92
120
|
...params,
|
|
93
121
|
})
|
|
94
122
|
}
|
|
95
123
|
|
|
96
|
-
|
|
124
|
+
visible.set(true)
|
|
97
125
|
|
|
98
126
|
await this.awaitTransition()
|
|
99
127
|
|
|
100
128
|
this.handleOpen()
|
|
101
129
|
}
|
|
102
130
|
|
|
131
|
+
/** Lifecycle hook called after the portal closes. Override in subclasses to run custom logic. */
|
|
103
132
|
protected handleClose() { }
|
|
104
133
|
|
|
105
134
|
/**
|
|
106
135
|
* Closes the portal and optionally resets parameters.
|
|
107
136
|
*/
|
|
108
137
|
async close() {
|
|
109
|
-
|
|
138
|
+
const { visible, config } = this.assertInitialized()
|
|
139
|
+
|
|
140
|
+
if (!visible.get()) {
|
|
110
141
|
return this.awaitTransition()
|
|
111
142
|
}
|
|
112
143
|
|
|
113
|
-
|
|
144
|
+
visible.set(false)
|
|
114
145
|
|
|
115
|
-
if (
|
|
116
|
-
|
|
146
|
+
if (config.resetParamsOnClose) {
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
this.resetParams()
|
|
149
|
+
}, 1000)
|
|
117
150
|
}
|
|
118
151
|
|
|
119
152
|
this.handleClose()
|
|
@@ -125,7 +158,9 @@ export class PortalState<Params = {}> {
|
|
|
125
158
|
* Toggles portal visibility (opens if closed, closes if open).
|
|
126
159
|
*/
|
|
127
160
|
toggle() {
|
|
128
|
-
|
|
161
|
+
const { visible } = this.assertInitialized()
|
|
162
|
+
|
|
163
|
+
if (visible.get()) {
|
|
129
164
|
return this.close()
|
|
130
165
|
} else {
|
|
131
166
|
return this.open()
|
|
@@ -137,11 +172,12 @@ export class PortalState<Params = {}> {
|
|
|
137
172
|
* @param next - Partial params or updater function
|
|
138
173
|
*/
|
|
139
174
|
setParams(next: Partial<Params> | ((prev: Params) => Partial<Params>)) {
|
|
140
|
-
const
|
|
175
|
+
const { params } = this.assertInitialized()
|
|
176
|
+
const prev = params.get()
|
|
141
177
|
|
|
142
178
|
const patch = TypeGuards.isFunction(next) ? next(prev) : next
|
|
143
179
|
|
|
144
|
-
|
|
180
|
+
params.set({
|
|
145
181
|
...prev,
|
|
146
182
|
...patch,
|
|
147
183
|
})
|
|
@@ -151,7 +187,8 @@ export class PortalState<Params = {}> {
|
|
|
151
187
|
* Resets parameters to initial values.
|
|
152
188
|
*/
|
|
153
189
|
resetParams() {
|
|
154
|
-
|
|
190
|
+
const { params, initialParams } = this.assertInitialized()
|
|
191
|
+
params.set(initialParams)
|
|
155
192
|
}
|
|
156
193
|
|
|
157
194
|
/**
|
|
@@ -159,7 +196,8 @@ export class PortalState<Params = {}> {
|
|
|
159
196
|
* @returns Current params object
|
|
160
197
|
*/
|
|
161
198
|
getParams() {
|
|
162
|
-
|
|
199
|
+
const { params } = this.assertInitialized()
|
|
200
|
+
return params.get()
|
|
163
201
|
}
|
|
164
202
|
|
|
165
203
|
/**
|
|
@@ -167,7 +205,8 @@ export class PortalState<Params = {}> {
|
|
|
167
205
|
* @param callback - Function called when visibility changes
|
|
168
206
|
* @returns Unsubscribe function
|
|
169
207
|
*/
|
|
170
|
-
subscribe(callback: (visible: boolean, wasVisible
|
|
171
|
-
|
|
208
|
+
subscribe(callback: (visible: boolean, wasVisible?: boolean) => void) {
|
|
209
|
+
const { visible } = this.assertInitialized()
|
|
210
|
+
return visible.subscribe(callback)
|
|
172
211
|
}
|
|
173
212
|
}
|
package/src/types/misc.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { AnyFunction } from '@codeleap/types'
|
|
2
2
|
|
|
3
|
+
/** Extracts all but the first element of a tuple type. */
|
|
3
4
|
type AllButFirst<T extends any[]> = T extends [infer _, ...infer Rest] ? Rest : []
|
|
4
5
|
|
|
6
|
+
/** Produces a version of function `T` whose first parameter excludes type `E`. */
|
|
5
7
|
export type ExcludeFromParam<
|
|
6
8
|
T extends AnyFunction,
|
|
7
9
|
E = any
|
package/src/types/portal.ts
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { Ref } from 'react'
|
|
2
2
|
|
|
3
|
+
/** Props injected into portal content: visibility flag, lifecycle methods, and the portal's current params. */
|
|
3
4
|
export type PortalParams<P = {}, Result = {}> = {
|
|
4
5
|
visible: boolean
|
|
5
|
-
toggle: () => void
|
|
6
|
-
close: () => void
|
|
7
|
-
open: () => void
|
|
6
|
+
toggle: () => void | Promise<void>
|
|
7
|
+
close: () => void | Promise<void>
|
|
8
|
+
open: (params?: P) => void | Promise<void>
|
|
8
9
|
setParams: (next: Partial<P> | ((prev: P) => Partial<P>)) => void
|
|
9
10
|
request?: {
|
|
10
|
-
resolve
|
|
11
|
-
reject
|
|
11
|
+
resolve?: (result: Result) => void
|
|
12
|
+
reject?: (reason: unknown) => void
|
|
12
13
|
}
|
|
13
14
|
} & P
|
|
14
15
|
|
|
16
|
+
/** Function that renders portal content, receiving params and an optional ref. */
|
|
15
17
|
export type RenderPortalContent<Params = {}, Result = {}, RefType = any> = (
|
|
16
18
|
props: PortalParams<Params, Result>,
|
|
17
19
|
ref?: React.RefObject<RefType>
|
|
18
20
|
) => React.ReactElement
|
|
19
21
|
|
|
22
|
+
/** Configuration options for portal initialization and runtime behavior. */
|
|
20
23
|
export type PortalConfig<P, M = {}> = {
|
|
21
24
|
initialParams?: P | (() => Promise<P>)
|
|
22
25
|
startsOpen?: boolean | (() => Promise<boolean>)
|
|
@@ -25,10 +28,13 @@ export type PortalConfig<P, M = {}> = {
|
|
|
25
28
|
metadata?: M
|
|
26
29
|
transitionDuration?: number
|
|
27
30
|
resetParamsOnClose?: boolean
|
|
31
|
+
ephemeral?: boolean
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
/** Constructor parameter — either a portal ID string or a config object with an optional `id` field. */
|
|
30
35
|
export type PortalConstructorParam<Params = {}, Metadata = {}> = (Partial<PortalConfig<Params, Metadata>> & { id?: string }) | string
|
|
31
36
|
|
|
37
|
+
/** Props passed to the wrapper component (modal/drawer/sheet) including lifecycle handlers and z-index. */
|
|
32
38
|
export type PortalWrapperProps<P, R> = P & {
|
|
33
39
|
close: () => void
|
|
34
40
|
open: () => void
|
package/src/utils.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { waitFor } from '@codeleap/utils'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Generates a random 7-character alphanumeric ID.
|
|
5
|
+
* @returns Random ID string
|
|
6
|
+
*/
|
|
3
7
|
export const randomId = () => {
|
|
4
8
|
return Math.random().toString(36).slice(2, 9)
|
|
5
9
|
}
|
|
6
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Waits for one or more animation transitions to complete.
|
|
13
|
+
* @param count - Number of transitions to wait for (defaults to 1)
|
|
14
|
+
* @param duration - Duration per transition in milliseconds (defaults to 1000)
|
|
15
|
+
*/
|
|
7
16
|
export async function awaitTransition(count?: number, duration = 1000) {
|
|
8
17
|
for (let i = 0; i < (count ?? 1); i++) {
|
|
9
18
|
await waitFor(duration)
|
package/package.json.bak
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@codeleap/portals",
|
|
3
|
-
"version": "6.3.0",
|
|
4
|
-
"main": "src/index.ts",
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"repository": {
|
|
7
|
-
"url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
|
|
8
|
-
"type": "git",
|
|
9
|
-
"directory": "packages/portals"
|
|
10
|
-
},
|
|
11
|
-
"devDependencies": {
|
|
12
|
-
"@codeleap/types": "workspace:*",
|
|
13
|
-
"@codeleap/config": "workspace:*",
|
|
14
|
-
"@codeleap/utils": "workspace:*",
|
|
15
|
-
"@codeleap/logger": "workspace:*",
|
|
16
|
-
"ts-node-dev": "1.1.8"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "echo 'No build needed'",
|
|
20
|
-
"lint": "eslint -c .eslintrc.js --fix \"./src/**/*.{ts,tsx,js,jsx}\"",
|
|
21
|
-
"run-sc": "tsnd --transpile-only"
|
|
22
|
-
},
|
|
23
|
-
"peerDependencies": {
|
|
24
|
-
"@codeleap/types": "workspace:*",
|
|
25
|
-
"@codeleap/utils": "workspace:*",
|
|
26
|
-
"@codeleap/logger": "workspace:*",
|
|
27
|
-
"typescript": "5.5.2",
|
|
28
|
-
"react": "19.1.0",
|
|
29
|
-
"nanostores": "*",
|
|
30
|
-
"@nanostores/react": "*"
|
|
31
|
-
}
|
|
32
|
-
}
|