@codeleap/portals 6.3.0 → 6.8.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.
Files changed (41) hide show
  1. package/dist/factors/alert.d.ts +44 -0
  2. package/dist/factors/alert.d.ts.map +1 -0
  3. package/dist/factors/bottomSheet.d.ts +61 -0
  4. package/dist/factors/bottomSheet.d.ts.map +1 -0
  5. package/dist/factors/drawer.d.ts +32 -0
  6. package/dist/factors/drawer.d.ts.map +1 -0
  7. package/dist/factors/modal.d.ts +32 -0
  8. package/dist/factors/modal.d.ts.map +1 -0
  9. package/dist/globals.d.ts +19 -0
  10. package/dist/globals.d.ts.map +1 -0
  11. package/dist/index.d.ts +13 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/lib/Portal.d.ts +121 -0
  14. package/dist/lib/Portal.d.ts.map +1 -0
  15. package/dist/lib/PortalRegistry.d.ts +59 -0
  16. package/dist/lib/PortalRegistry.d.ts.map +1 -0
  17. package/dist/lib/PortalRequest.d.ts +44 -0
  18. package/dist/lib/PortalRequest.d.ts.map +1 -0
  19. package/dist/lib/PortalState.d.ts +83 -0
  20. package/dist/lib/PortalState.d.ts.map +1 -0
  21. package/dist/types/index.d.ts +2 -0
  22. package/dist/types/index.d.ts.map +1 -0
  23. package/dist/types/misc.d.ts +7 -0
  24. package/dist/types/misc.d.ts.map +1 -0
  25. package/dist/types/portal.d.ts +40 -0
  26. package/dist/types/portal.d.ts.map +1 -0
  27. package/dist/utils.d.ts +12 -0
  28. package/dist/utils.d.ts.map +1 -0
  29. package/package.json +25 -11
  30. package/src/factors/alert.ts +20 -60
  31. package/src/factors/bottomSheet.ts +20 -3
  32. package/src/factors/drawer.ts +11 -1
  33. package/src/factors/modal.ts +11 -1
  34. package/src/globals.ts +6 -0
  35. package/src/lib/Portal.tsx +62 -14
  36. package/src/lib/PortalRequest.ts +5 -5
  37. package/src/lib/PortalState.ts +64 -25
  38. package/src/types/misc.ts +2 -0
  39. package/src/types/portal.ts +11 -5
  40. package/src/utils.ts +9 -0
  41. package/package.json.bak +0 -32
@@ -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: Params | (() => Promise<Params>)
17
- startsOpen: boolean | (() => Promise<boolean>)
17
+ initialParams?: Params | (() => Promise<Params>)
18
+ startsOpen?: boolean | (() => Promise<boolean>)
18
19
  resetParamsOnClose?: boolean
19
- transitionDuration: number
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
- visible: IAtom<boolean>
30
+ /** Nanostores atom managing portal visibility state. */
31
+ visible!: IAtom<boolean>
30
32
 
31
- params: IAtom<Params>
33
+ /** Nanostores atom managing portal parameters. */
34
+ params!: IAtom<Params>
32
35
 
33
- _initialParams: Params
36
+ /** Initial parameter values used when resetting. */
37
+ _initialParams!: Params
34
38
 
35
- private config: PortalStateConfig<Params>
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
- return this.visible.get()
88
+ const { visible } = this.assertInitialized()
89
+ return visible.get()
68
90
  }
69
91
 
92
+ /** Gets the current parameter values. */
70
93
  get currentParams() {
71
- return this.params.get()
94
+ const { params } = this.assertInitialized()
95
+ return params.get()
72
96
  }
73
97
 
74
98
  private awaitTransition(count = 1) {
75
- return awaitTransition(count, this.config.transitionDuration)
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
- if (this.visible.get()) {
111
+ const { visible, params: paramsAtom } = this.assertInitialized()
112
+
113
+ if (visible.get()) {
86
114
  return
87
115
  }
88
116
 
89
117
  if (params) {
90
- this.params.set({
91
- ...this.params.get(),
118
+ paramsAtom.set({
119
+ ...paramsAtom.get(),
92
120
  ...params,
93
121
  })
94
122
  }
95
123
 
96
- this.visible.set(true)
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
- if (!this.visible.get()) {
138
+ const { visible, config } = this.assertInitialized()
139
+
140
+ if (!visible.get()) {
110
141
  return this.awaitTransition()
111
142
  }
112
143
 
113
- this.visible.set(false)
144
+ visible.set(false)
114
145
 
115
- if (this.config.resetParamsOnClose) {
116
- this.resetParams()
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
- if (this.visible.get()) {
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 prev = this.params.get()
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
- this.params.set({
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
- this.params.set(this._initialParams ?? {} as Params)
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
- return this.params.get()
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: boolean) => void) {
171
- return this.visible.subscribe(callback)
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
@@ -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: (result: Result) => void
11
- reject: (reason: unknown) => void
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
- }