Forked from AlexandrHoroshih/effector-history-wrap.ts
Created
April 18, 2023 04:33
-
-
Save mistical2008/9034c95b06dd1f81b3748b65ffac0465 to your computer and use it in GitHub Desktop.
Effector history wrapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // an option to create bindings to history | |
| // usage is like | |
| // | |
| // export const historyUpdated = createEvent<HistoryUpdate>(); | |
| // export const clocks = createHistoryClocks(); | |
| // | |
| // clocks have fields like `push, replace, go` and so on | |
| // can be used like clocks.push({ to: "path" }) | |
| // | |
| // wrapHistory({ | |
| // historySource: $history, // put history instance to store | |
| // clocks, | |
| // target: historyUpdated.prepend<HistoryUpdate>((update) => klona(update)), | |
| // // history is mutable, it is much safer having an immutable copy of every update | |
| // }); | |
| // | |
| import type { Action, History, Location, LocationState, Path } from "history"; | |
| import type { Domain, Event, Store } from "effector"; | |
| import { createEvent, sample, scopeBind } from "effector"; | |
| export type ToParams<S extends LocationState = LocationState> = { | |
| to: Path; | |
| LocationState?: S; | |
| }; | |
| export type HistoryUpdate = { action: Action; location: Location }; | |
| export type Clocks<S extends LocationState> = { | |
| push: Event<ToParams<S>>; | |
| replace: Event<ToParams<S>>; | |
| go: Event<number>; | |
| back: Event<unknown>; | |
| forward: Event<unknown>; | |
| }; | |
| type Config<S extends LocationState = LocationState> = { | |
| historySource: Store<History<S> | null>; | |
| clocks: Clocks<S>; | |
| target: Event<HistoryUpdate>; | |
| }; | |
| const checkHistory = <S extends LocationState>( | |
| history?: History<S> | null, | |
| ): history is History<S> => { | |
| const historyProvided = Boolean(history); | |
| if (!historyProvided) { | |
| console.warn("No history was provided"); | |
| return false; | |
| } | |
| return historyProvided; | |
| }; | |
| export const createHistoryClocks = (domain?: Domain) => { | |
| if (domain) { | |
| return { | |
| push: domain.createEvent<ToParams>(), | |
| replace: domain.createEvent<ToParams>(), | |
| go: domain.createEvent<number>(), | |
| back: domain.createEvent<unknown>(), | |
| forward: domain.createEvent<unknown>(), | |
| }; | |
| } | |
| return { | |
| push: createEvent<ToParams>(), | |
| replace: createEvent<ToParams>(), | |
| go: createEvent<number>(), | |
| back: createEvent<unknown>(), | |
| forward: createEvent<unknown>(), | |
| }; | |
| }; | |
| export const wrapHistory = <S extends LocationState = LocationState>( | |
| config: Config<S>, | |
| ) => { | |
| const { historySource, clocks, target } = config; | |
| historySource.updates.watch((history) => { | |
| // Hacky way to support both in and out of scope listeners | |
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | |
| let listener = (_: any) => console.warn("History listener is not set"); | |
| try { | |
| listener = scopeBind(target); | |
| } catch (e) { | |
| listener = target; | |
| } | |
| if (!checkHistory<S>(history)) return; | |
| listener({ location: history.location, action: history.action }); | |
| history.listen(() => { | |
| listener({ location: history.location, action: history.action }); | |
| }); | |
| }); | |
| // push | |
| const historyPushed = sample({ | |
| source: historySource, | |
| clock: clocks.push, | |
| fn: (history, params) => ({ history, params }), | |
| }); | |
| historyPushed.watch( | |
| ({ history, params }) => | |
| checkHistory(history) && history.push(params.to, params.LocationState), | |
| ); | |
| // replace | |
| const historyReplaced = sample({ | |
| source: historySource, | |
| clock: clocks.replace, | |
| fn: (history, params) => ({ history, params }), | |
| }); | |
| historyReplaced.watch( | |
| ({ history, params }) => | |
| checkHistory(history) && history.replace(params.to, params.LocationState), | |
| ); | |
| // go | |
| const historyGo = sample({ | |
| source: historySource, | |
| clock: clocks.go, | |
| fn: (history, params) => ({ history, params }), | |
| }); | |
| historyGo.watch( | |
| ({ history, params }) => checkHistory(history) && history.go(params), | |
| ); | |
| // back | |
| const historyBack = sample({ | |
| source: historySource, | |
| clock: clocks.back, | |
| fn: (history) => history, | |
| }); | |
| historyBack.watch((history) => checkHistory(history) && history.goBack()); | |
| // forward | |
| const historyForward = sample({ | |
| source: historySource, | |
| clock: clocks.forward, | |
| fn: (history) => history, | |
| }); | |
| historyForward.watch( | |
| (history) => checkHistory(history) && history.goForward(), | |
| ); | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment