Skip to content

Commit

Permalink
Merge pull request #32 from desko27/feature/end-method-from-caller-scope
Browse files Browse the repository at this point in the history
Add end method from caller scope
  • Loading branch information
desko27 authored Jan 22, 2025
2 parents c46e7d8 + c1fe1dc commit 712e997
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 20 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ const accepted = await Confirm.call({ message: 'Continue?' })

Check out [the demo site](https://react-call.desko.dev/) to see some live examples of other React components being called.

# Extra controls

The returned promise can also be used to end the call from the caller scope:

```tsx
const promise = Confirm.call({ message: 'Continue?' })

// For example, you could dismiss it on some event subscription
onImportantEvent(() => {
Confirm.end(promise, false)
})

// While still awaiting the response where needed
const accepted = await promise
```

# Exit animations

To animate the exit of your component when `call.end()` is run, just pass the duration of your animation in milliseconds to createCallable as a second argument:
Expand Down
2 changes: 1 addition & 1 deletion bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"files": [
{
"path": "./dist/*.js",
"maxSize": "555 B"
"maxSize": "650 B"
}
]
}
46 changes: 27 additions & 19 deletions lib/createCallable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import type {
UserComponent as UserComponentType,
PrivateResolve,
PrivateStackState,
PrivateStackStateSetter,
Callable,
Expand All @@ -13,36 +14,43 @@ export function createCallable<Props = void, Response = void, RootProps = {}>(
let $setStack: PrivateStackStateSetter<Props, Response> | null = null
let $nextKey = 0

const createEnd = (promise: Promise<Response>) => (response: Response) => {
if (!$setStack) return
const scopedSetStack = $setStack

scopedSetStack((prev) => {
const target = prev.find((c) => c.promise === promise)
if (!target) return prev

target.resolve(response)
return prev.map((c) =>
c.promise !== promise ? c : { ...c, ended: true },
)
})

globalThis.setTimeout(
() => scopedSetStack((prev) => prev.filter((c) => c.promise !== promise)),
unmountingDelay,
)
}

return {
call: (props) => {
if (!$setStack) throw new Error('No <Root> found!')

const key = String($nextKey++)
let resolve: (value: Response | PromiseLike<Response>) => void
let resolve: PrivateResolve<Response>
const promise = new Promise<Response>((res) => {
resolve = res
})

const end = (response: Response) => {
resolve(response)
if (!$setStack) return
const scopedSetStack = $setStack

if (unmountingDelay > 0) {
scopedSetStack((prev) =>
prev.map((c) => (c.key !== key ? c : { ...c, ended: true })),
)
}

globalThis.setTimeout(
() => scopedSetStack((prev) => prev.filter((c) => c.key !== key)),
unmountingDelay,
)
}

$setStack((prev) => [...prev, { key, props, end, ended: false }])
$setStack((prev) => [
...prev,
{ key, props, promise, resolve, end: createEnd(promise), ended: false },
])
return promise
},
end: (promise, response) => createEnd(promise)(response),
Root: (rootProps: RootProps) => {
const [stack, setStack] = useState<PrivateStackState<Props, Response>>([])

Expand Down
7 changes: 7 additions & 0 deletions lib/createCallable/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export type PrivateResolve<Response> = (
value: Response | PromiseLike<Response>,
) => void

export interface PrivateCallContext<Props, Response> {
key: string
props: Props
promise: Promise<Response>
resolve: PrivateResolve<Response>
end: (response: Response) => void
ended: boolean
}
Expand Down Expand Up @@ -45,4 +51,5 @@ export type UserComponent<Props, Response, RootProps> = React.FunctionComponent<
export type Callable<Props, Response, RootProps> = {
Root: React.FunctionComponent<RootProps>
call: CallFunction<Props, Response>
end: (promise: Promise<Response>, response: Response) => void
}

0 comments on commit 712e997

Please sign in to comment.