@attio/react-native-bottom-sheet-toolbox
Version:
124 lines (110 loc) • 4.29 kB
text/typescript
import {nanoid} from "nanoid"
export interface BottomSheetToolboxDevToolsAdapter {
// biome-ignore lint/suspicious/noExplicitAny: not useful to type
sendMessage(method: string, parameters: any): void
// biome-ignore lint/suspicious/noExplicitAny: not useful to type
addMessageListener(method: string, listener: (parameters: any) => void): () => void
}
export type BottomSheetToolboxDevToolsMessageFromRN =
| {
type: "upsert-sheet"
payload: {
sheetId: string
currentPosition: number
wrapperHeight: number | null
snapPoints: number[] | null
isReady: boolean
}
}
| {
type: "delete-sheet"
payload: {
sheetId: string
}
}
| {
type: "update-device-config"
payload: {
width: number
height: number
theme: "light" | "dark"
}
}
export type BottomSheetToolboxDevToolsMessageFromDevTools = {
type: "did-connect"
}
class BottomSheetToolboxDevTools {
private _adaptersWithListenerRemovers: {
adapter: BottomSheetToolboxDevToolsAdapter
listenerRemovers: Map<string, () => void>
}[] = []
private readonly _messageListeners = new Map<
string,
// biome-ignore lint/suspicious/noExplicitAny: not useful to type
readonly [string, (parameters: any) => void]
>()
public addAdapter(adapter: BottomSheetToolboxDevToolsAdapter) {
const adapterWithListenerRemovers = {
adapter,
listenerRemovers: new Map(),
}
this._adaptersWithListenerRemovers.push(adapterWithListenerRemovers)
for (const [messageListenerId, listenerArgs] of this._messageListeners.entries()) {
const [method, listener] = listenerArgs
const removeListener = adapter.addMessageListener(method, listener)
adapterWithListenerRemovers.listenerRemovers.set(messageListenerId, removeListener)
}
return () => {
this._adaptersWithListenerRemovers = this._adaptersWithListenerRemovers.filter(
(adapterWithListenerRemovers) => adapterWithListenerRemovers.adapter !== adapter
)
}
}
public sendMessage<Method extends BottomSheetToolboxDevToolsMessageFromRN["type"]>(
method: Method,
parameters: Extract<BottomSheetToolboxDevToolsMessageFromRN, {type: Method}> extends {
payload: infer Payload
}
? Payload
: never
) {
// Message sent before an adapter is added are lost.
this._adaptersWithListenerRemovers.forEach((adapterWithListenerRemovers) => {
adapterWithListenerRemovers.adapter.sendMessage(method, parameters)
})
}
public addMessageListener<Method extends BottomSheetToolboxDevToolsMessageFromDevTools["type"]>(
method: Method,
listener: (
parameters: Extract<
BottomSheetToolboxDevToolsMessageFromDevTools,
{type: Method}
> extends {payload: infer Payload}
? Payload
: never
) => void
) {
const messageListenerId = nanoid()
const listenerArgs = [method, listener] as const
this._messageListeners.set(messageListenerId, listenerArgs)
for (const adapterWithListenerRemovers of this._adaptersWithListenerRemovers) {
const removeListener = adapterWithListenerRemovers.adapter.addMessageListener(
method,
listener
)
adapterWithListenerRemovers.listenerRemovers.set(messageListenerId, removeListener)
}
return () => {
this._messageListeners.delete(messageListenerId)
for (const adapterWithListenerRemovers of this._adaptersWithListenerRemovers) {
const removeListener =
adapterWithListenerRemovers.listenerRemovers.get(messageListenerId)
removeListener?.()
}
}
}
}
/**
* This is for internal use, so we can log to our separate devtools package.
*/
export const BOTTOM_SHEET_DEV_TOOLS = new BottomSheetToolboxDevTools()