bloc-react
Advanced tools
Comparing version 0.1.21 to 0.1.22
@@ -158,8 +158,8 @@ import React, { ReactElement } from 'react'; | ||
BlocProvider<T extends BlocBase<any>>(props: { | ||
children?: ReactElement | ReactElement[]; | ||
children?: ReactElement | ReactElement[] | false; | ||
bloc: T | ((id: string) => T); | ||
}): ReactElement; | ||
withBlocProvider: <P extends {}>(bloc: BlocBase<any> | ((id: string) => BlocBase<any>)) => (Component: React.ComponentType<P>) => React.FC<P>; | ||
withBlocProvider: <P extends object>(bloc: BlocBase<any> | (() => BlocBase<any>)) => (Component: React.ComponentType<P>) => React.ComponentType<P>; | ||
} | ||
export { Bloc, BlocObserver, BlocReact, Cubit }; |
@@ -14,5 +14,7 @@ import React, { useMemo, useContext, useState, useCallback, useEffect } from 'react'; | ||
class BehaviorSubject { | ||
isClosed = false; | ||
prevValue; | ||
value; | ||
observers = []; | ||
constructor(initialValue) { | ||
this.isClosed = false; | ||
this.observers = []; | ||
this.value = initialValue; | ||
@@ -49,51 +51,7 @@ } | ||
class StreamAbstraction { | ||
isClosed = false; | ||
removeListeners = []; | ||
_options; | ||
_subject; | ||
constructor(initialValue, blocOptions = {}) { | ||
this.isClosed = false; | ||
this.removeListeners = []; | ||
this.removeRemoveListener = (index) => { | ||
this.removeListeners.splice(index, 1); | ||
}; | ||
this.addRemoveListener = (method) => { | ||
const index = this.removeListeners.length; | ||
this.removeListeners.push(method); | ||
return () => this.removeRemoveListener(index); | ||
}; | ||
this.subscribe = (observer) => this._subject.subscribe({ | ||
next: observer.next | ||
}); | ||
this.complete = () => { | ||
this.isClosed = true; | ||
this._subject.complete(); | ||
}; | ||
this.clearCache = () => { | ||
const key = this._options.persistKey; | ||
if (key) { | ||
localStorage.removeItem(`${LOCAL_STORAGE_PREFIX}${key}`); | ||
} | ||
}; | ||
this.next = (value) => { | ||
this._subject.next(value); | ||
this.updateCache(); | ||
}; | ||
this.getCachedValue = () => { | ||
const cachedValue = localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${this._options.persistKey}`); | ||
if (cachedValue) { | ||
try { | ||
return this.jsonToState(cachedValue); | ||
} catch (e) { | ||
const error = new Error(`Failed to parse JSON in localstorage for the key: "${LOCAL_STORAGE_PREFIX}${this._options.persistKey}"`); | ||
console.error(error); | ||
return error; | ||
} | ||
} | ||
return new Error("Key not found"); | ||
}; | ||
this.updateCache = () => { | ||
const { persistData, persistKey } = this._options; | ||
if (persistData && persistKey) { | ||
localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${persistKey}`, this.stateToJson(this.state)); | ||
} else { | ||
this.clearCache(); | ||
} | ||
}; | ||
let value = initialValue; | ||
@@ -113,2 +71,23 @@ const options = { ...cubitDefaultOptions, ...blocOptions }; | ||
} | ||
removeRemoveListener = (index) => { | ||
this.removeListeners.splice(index, 1); | ||
}; | ||
addRemoveListener = (method) => { | ||
const index = this.removeListeners.length; | ||
this.removeListeners.push(method); | ||
return () => this.removeRemoveListener(index); | ||
}; | ||
subscribe = (observer) => this._subject.subscribe({ | ||
next: observer.next | ||
}); | ||
complete = () => { | ||
this.isClosed = true; | ||
this._subject.complete(); | ||
}; | ||
clearCache = () => { | ||
const key = this._options.persistKey; | ||
if (key) { | ||
localStorage.removeItem(`${LOCAL_STORAGE_PREFIX}${key}`); | ||
} | ||
}; | ||
jsonToState(state) { | ||
@@ -120,113 +99,137 @@ return JSON.parse(state).state; | ||
} | ||
next = (value) => { | ||
this._subject.next(value); | ||
this.updateCache(); | ||
}; | ||
getCachedValue = () => { | ||
const cachedValue = localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${this._options.persistKey}`); | ||
if (cachedValue) { | ||
try { | ||
return this.jsonToState(cachedValue); | ||
} catch (e) { | ||
const error = new Error(`Failed to parse JSON in localstorage for the key: "${LOCAL_STORAGE_PREFIX}${this._options.persistKey}"`); | ||
console.error(error); | ||
return error; | ||
} | ||
} | ||
return new Error("Key not found"); | ||
}; | ||
updateCache = () => { | ||
const { persistData, persistKey } = this._options; | ||
if (persistData && persistKey) { | ||
localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${persistKey}`, this.stateToJson(this.state)); | ||
} else { | ||
this.clearCache(); | ||
} | ||
}; | ||
} | ||
class BlocBase extends StreamAbstraction { | ||
id = createId(); | ||
createdAt = new Date(); | ||
meta = { | ||
scope: "unknown" | ||
}; | ||
changeListeners = []; | ||
registerListeners = []; | ||
valueChangeListeners = []; | ||
consumer = null; | ||
constructor(initialValue, blocOptions = {}) { | ||
super(initialValue, blocOptions); | ||
this.id = createId(); | ||
this.createdAt = new Date(); | ||
this.meta = { | ||
scope: "unknown" | ||
}; | ||
this.changeListeners = []; | ||
this.registerListeners = []; | ||
this.valueChangeListeners = []; | ||
this.consumer = null; | ||
this.removeChangeListener = (index) => { | ||
this.changeListeners.splice(index, 1); | ||
}; | ||
this.addChangeListener = (method) => { | ||
const index = this.changeListeners.length; | ||
this.changeListeners.push(method); | ||
return () => this.removeChangeListener(index); | ||
}; | ||
this.removeValueChangeListener = (index) => { | ||
this.valueChangeListeners.splice(index, 1); | ||
}; | ||
this.addValueChangeListener = (method) => { | ||
const index = this.valueChangeListeners.length; | ||
this.valueChangeListeners.push(method); | ||
return () => this.removeValueChangeListener(index); | ||
}; | ||
this.removeRegisterListener = (index) => { | ||
this.registerListeners.splice(index, 1); | ||
}; | ||
this.addRegisterListener = (method) => { | ||
const index = this.registerListeners.length; | ||
this.registerListeners.push(method); | ||
return () => this.removeRegisterListener(index); | ||
}; | ||
this.notifyChange = (state) => { | ||
this.consumer?.notifyChange(this, state); | ||
this.changeListeners.forEach((fn) => fn({ | ||
currentState: this.state, | ||
nextState: state | ||
}, this)); | ||
}; | ||
this.notifyValueChange = () => { | ||
this.consumer?.notifyValueChange(this); | ||
this.valueChangeListeners.forEach((fn) => fn(this.state, this)); | ||
}; | ||
} | ||
removeChangeListener = (index) => { | ||
this.changeListeners.splice(index, 1); | ||
}; | ||
addChangeListener = (method) => { | ||
const index = this.changeListeners.length; | ||
this.changeListeners.push(method); | ||
return () => this.removeChangeListener(index); | ||
}; | ||
removeValueChangeListener = (index) => { | ||
this.valueChangeListeners.splice(index, 1); | ||
}; | ||
addValueChangeListener = (method) => { | ||
const index = this.valueChangeListeners.length; | ||
this.valueChangeListeners.push(method); | ||
return () => this.removeValueChangeListener(index); | ||
}; | ||
removeRegisterListener = (index) => { | ||
this.registerListeners.splice(index, 1); | ||
}; | ||
addRegisterListener = (method) => { | ||
const index = this.registerListeners.length; | ||
this.registerListeners.push(method); | ||
return () => this.removeRegisterListener(index); | ||
}; | ||
notifyChange = (state) => { | ||
this.consumer?.notifyChange(this, state); | ||
this.changeListeners.forEach((fn) => fn({ | ||
currentState: this.state, | ||
nextState: state | ||
}, this)); | ||
}; | ||
notifyValueChange = () => { | ||
this.consumer?.notifyValueChange(this); | ||
this.valueChangeListeners.forEach((fn) => fn(this.state, this)); | ||
}; | ||
} | ||
class Bloc extends BlocBase { | ||
onTransition = null; | ||
mapEventToState = null; | ||
constructor(initialState, options) { | ||
super(initialState, options); | ||
this.onTransition = null; | ||
this.mapEventToState = null; | ||
this.add = (event) => { | ||
if (this.mapEventToState) { | ||
const newState = this.mapEventToState(event); | ||
this.notifyChange(newState); | ||
this.notifyTransition(newState, event); | ||
this.next(newState); | ||
this.notifyValueChange(); | ||
} else { | ||
console.error(`"mapEventToState" not implemented for "${this.constructor.name}"`); | ||
} | ||
}; | ||
this.notifyTransition = (state, event) => { | ||
this.consumer?.notifyTransition(this, state, event); | ||
this.onTransition?.({ | ||
currentState: this.state, | ||
event, | ||
nextState: state | ||
}); | ||
}; | ||
} | ||
add = (event) => { | ||
if (this.mapEventToState) { | ||
const newState = this.mapEventToState(event); | ||
this.notifyChange(newState); | ||
this.notifyTransition(newState, event); | ||
this.next(newState); | ||
this.notifyValueChange(); | ||
} else { | ||
console.error(`"mapEventToState" not implemented for "${this.constructor.name}"`); | ||
} | ||
}; | ||
notifyTransition = (state, event) => { | ||
this.consumer?.notifyTransition(this, state, event); | ||
this.onTransition?.({ | ||
currentState: this.state, | ||
event, | ||
nextState: state | ||
}); | ||
}; | ||
} | ||
class Cubit extends BlocBase { | ||
constructor() { | ||
super(...arguments); | ||
this.emit = (value) => { | ||
this.notifyChange(value); | ||
this.next(value); | ||
this.notifyValueChange(); | ||
}; | ||
} | ||
emit = (value) => { | ||
this.notifyChange(value); | ||
this.next(value); | ||
this.notifyValueChange(); | ||
}; | ||
} | ||
class BlocObserver { | ||
onChange; | ||
onTransition; | ||
constructor(methods = {}) { | ||
this.addChange = (bloc, state) => { | ||
this.onChange(bloc, this.createChangeEvent(bloc, state)); | ||
}; | ||
this.addTransition = (bloc, state, event) => { | ||
this.onTransition(bloc, this.createTransitionEvent(bloc, state, event)); | ||
}; | ||
this.addBlocAdded = (bloc) => { | ||
this.onBlocAdded(bloc); | ||
}; | ||
this.addBlocRemoved = (bloc) => { | ||
this.onBlocRemoved(bloc); | ||
}; | ||
this.defaultAction = () => { | ||
}; | ||
this.onBlocAdded = this.defaultAction; | ||
this.onBlocRemoved = this.defaultAction; | ||
this.onChange = methods.onChange ? methods.onChange : this.defaultAction; | ||
this.onTransition = methods.onTransition ? methods.onTransition : this.defaultAction; | ||
} | ||
addChange = (bloc, state) => { | ||
this.onChange(bloc, this.createChangeEvent(bloc, state)); | ||
}; | ||
addTransition = (bloc, state, event) => { | ||
this.onTransition(bloc, this.createTransitionEvent(bloc, state, event)); | ||
}; | ||
addBlocAdded = (bloc) => { | ||
this.onBlocAdded(bloc); | ||
}; | ||
addBlocRemoved = (bloc) => { | ||
this.onBlocRemoved(bloc); | ||
}; | ||
defaultAction = () => { | ||
}; | ||
onBlocAdded = this.defaultAction; | ||
onBlocRemoved = this.defaultAction; | ||
createTransitionEvent(bloc, state, event) { | ||
@@ -248,8 +251,10 @@ return { | ||
class BlocConsumer { | ||
observer; | ||
mocksEnabled = false; | ||
providerList = []; | ||
blocListGlobal; | ||
blocChangeObservers = []; | ||
blocValueChangeObservers = []; | ||
mockBlocs = []; | ||
constructor(blocs, options = {}) { | ||
this.mocksEnabled = false; | ||
this.providerList = []; | ||
this.blocChangeObservers = []; | ||
this.blocValueChangeObservers = []; | ||
this.mockBlocs = []; | ||
this.blocListGlobal = blocs; | ||
@@ -370,2 +375,3 @@ this.observer = options.observer || new BlocObserver(); | ||
class BlocRuntimeError { | ||
error; | ||
constructor(message) { | ||
@@ -378,21 +384,26 @@ this.error = new Error(message); | ||
class BlocReact extends BlocConsumer { | ||
providerCount = 0; | ||
_blocsGlobal; | ||
_contextLocalProviderKey = React.createContext("none"); | ||
constructor(blocs, options) { | ||
super(blocs, options); | ||
this.providerCount = 0; | ||
this._contextLocalProviderKey = React.createContext("none"); | ||
this.useBloc = (blocClass, options = {}) => { | ||
const mergedOptions = { | ||
...defaultBlocHookOptions, | ||
...options | ||
}; | ||
let blocInstance = useMemo(() => options.create ? options.create() : void 0, []); | ||
if (!blocInstance) { | ||
const localProviderKey = useContext(this._contextLocalProviderKey); | ||
const localBlocInstance = useMemo(() => this.getLocalBlocForProvider(localProviderKey, blocClass), []); | ||
blocInstance = useMemo(() => localBlocInstance || this.getGlobalBlocInstance(this._blocsGlobal, blocClass), []); | ||
} | ||
const { subscribe, shouldUpdate = true } = mergedOptions; | ||
if (!blocInstance) { | ||
const name = blocClass.prototype.constructor.name; | ||
const error = new BlocRuntimeError(`"${name}" | ||
this._blocsGlobal = blocs; | ||
this.BlocProvider = this.BlocProvider.bind(this); | ||
this.BlocBuilder = this.BlocBuilder.bind(this); | ||
} | ||
useBloc = (blocClass, options = {}) => { | ||
const mergedOptions = { | ||
...defaultBlocHookOptions, | ||
...options | ||
}; | ||
let blocInstance = useMemo(() => options.create ? options.create() : void 0, []); | ||
if (!blocInstance) { | ||
const localProviderKey = useContext(this._contextLocalProviderKey); | ||
const localBlocInstance = useMemo(() => this.getLocalBlocForProvider(localProviderKey, blocClass), []); | ||
blocInstance = useMemo(() => localBlocInstance || this.getGlobalBlocInstance(this._blocsGlobal, blocClass), []); | ||
} | ||
const { subscribe, shouldUpdate = true } = mergedOptions; | ||
if (!blocInstance) { | ||
const name = blocClass.prototype.constructor.name; | ||
const error = new BlocRuntimeError(`"${name}" | ||
no bloc with this name was found in the global context. | ||
@@ -412,55 +423,33 @@ | ||
`); | ||
console.error(error.error); | ||
return [ | ||
NoValue, | ||
{}, | ||
{ | ||
error, | ||
complete: true | ||
} | ||
]; | ||
} | ||
const [data, setData] = useState(blocInstance.state); | ||
const updateData = useCallback((nextState) => { | ||
if (shouldUpdate === true || shouldUpdate({ nextState, currentState: data })) { | ||
setData(nextState); | ||
console.error(error.error); | ||
return [ | ||
NoValue, | ||
{}, | ||
{ | ||
error, | ||
complete: true | ||
} | ||
}, []); | ||
useEffect(() => { | ||
if (subscribe) { | ||
const subscription = blocInstance?.subscribe({ | ||
next: updateData | ||
}); | ||
return () => { | ||
subscription?.unsubscribe(); | ||
}; | ||
} | ||
}, []); | ||
return [ | ||
data, | ||
blocInstance | ||
]; | ||
}; | ||
this.withBlocProvider = (bloc) => (Component) => { | ||
const displayName = Component.displayName || Component.name; | ||
const Provider = (props) => { | ||
return /* @__PURE__ */ React.createElement(this.BlocProvider, { | ||
bloc | ||
}, props.children); | ||
}; | ||
Provider.displayName = `withBlocProvider`; | ||
const Wrapper = (props) => { | ||
return /* @__PURE__ */ React.createElement(Provider, { | ||
bloc | ||
}, /* @__PURE__ */ React.createElement(Component, { | ||
...props | ||
})); | ||
}; | ||
Wrapper.displayName = `withBlocProvider(${displayName})`; | ||
return Wrapper; | ||
}; | ||
this._blocsGlobal = blocs; | ||
this.BlocProvider = this.BlocProvider.bind(this); | ||
this.BlocBuilder = this.BlocBuilder.bind(this); | ||
} | ||
} | ||
const [data, setData] = useState(blocInstance.state); | ||
const updateData = useCallback((nextState) => { | ||
if (shouldUpdate === true || shouldUpdate({ nextState, currentState: data })) { | ||
setData(nextState); | ||
} | ||
}, []); | ||
useEffect(() => { | ||
if (subscribe) { | ||
const subscription = blocInstance?.subscribe({ | ||
next: updateData | ||
}); | ||
return () => { | ||
subscription?.unsubscribe(); | ||
}; | ||
} | ||
}, []); | ||
return [ | ||
data, | ||
blocInstance | ||
]; | ||
}; | ||
BlocBuilder(props) { | ||
@@ -502,2 +491,14 @@ const hook = this.useBloc(props.blocClass, { | ||
} | ||
withBlocProvider = (bloc) => (Component) => { | ||
const { BlocProvider } = this; | ||
return class WithBlocProvider extends React.Component { | ||
render() { | ||
return /* @__PURE__ */ React.createElement(BlocProvider, { | ||
bloc | ||
}, /* @__PURE__ */ React.createElement(Component, { | ||
...this.props | ||
})); | ||
} | ||
}; | ||
}; | ||
} | ||
@@ -504,0 +505,0 @@ |
@@ -22,5 +22,7 @@ 'use strict'; | ||
class BehaviorSubject { | ||
isClosed = false; | ||
prevValue; | ||
value; | ||
observers = []; | ||
constructor(initialValue) { | ||
this.isClosed = false; | ||
this.observers = []; | ||
this.value = initialValue; | ||
@@ -57,51 +59,7 @@ } | ||
class StreamAbstraction { | ||
isClosed = false; | ||
removeListeners = []; | ||
_options; | ||
_subject; | ||
constructor(initialValue, blocOptions = {}) { | ||
this.isClosed = false; | ||
this.removeListeners = []; | ||
this.removeRemoveListener = (index) => { | ||
this.removeListeners.splice(index, 1); | ||
}; | ||
this.addRemoveListener = (method) => { | ||
const index = this.removeListeners.length; | ||
this.removeListeners.push(method); | ||
return () => this.removeRemoveListener(index); | ||
}; | ||
this.subscribe = (observer) => this._subject.subscribe({ | ||
next: observer.next | ||
}); | ||
this.complete = () => { | ||
this.isClosed = true; | ||
this._subject.complete(); | ||
}; | ||
this.clearCache = () => { | ||
const key = this._options.persistKey; | ||
if (key) { | ||
localStorage.removeItem(`${LOCAL_STORAGE_PREFIX}${key}`); | ||
} | ||
}; | ||
this.next = (value) => { | ||
this._subject.next(value); | ||
this.updateCache(); | ||
}; | ||
this.getCachedValue = () => { | ||
const cachedValue = localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${this._options.persistKey}`); | ||
if (cachedValue) { | ||
try { | ||
return this.jsonToState(cachedValue); | ||
} catch (e) { | ||
const error = new Error(`Failed to parse JSON in localstorage for the key: "${LOCAL_STORAGE_PREFIX}${this._options.persistKey}"`); | ||
console.error(error); | ||
return error; | ||
} | ||
} | ||
return new Error("Key not found"); | ||
}; | ||
this.updateCache = () => { | ||
const { persistData, persistKey } = this._options; | ||
if (persistData && persistKey) { | ||
localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${persistKey}`, this.stateToJson(this.state)); | ||
} else { | ||
this.clearCache(); | ||
} | ||
}; | ||
let value = initialValue; | ||
@@ -121,2 +79,23 @@ const options = { ...cubitDefaultOptions, ...blocOptions }; | ||
} | ||
removeRemoveListener = (index) => { | ||
this.removeListeners.splice(index, 1); | ||
}; | ||
addRemoveListener = (method) => { | ||
const index = this.removeListeners.length; | ||
this.removeListeners.push(method); | ||
return () => this.removeRemoveListener(index); | ||
}; | ||
subscribe = (observer) => this._subject.subscribe({ | ||
next: observer.next | ||
}); | ||
complete = () => { | ||
this.isClosed = true; | ||
this._subject.complete(); | ||
}; | ||
clearCache = () => { | ||
const key = this._options.persistKey; | ||
if (key) { | ||
localStorage.removeItem(`${LOCAL_STORAGE_PREFIX}${key}`); | ||
} | ||
}; | ||
jsonToState(state) { | ||
@@ -128,113 +107,137 @@ return JSON.parse(state).state; | ||
} | ||
next = (value) => { | ||
this._subject.next(value); | ||
this.updateCache(); | ||
}; | ||
getCachedValue = () => { | ||
const cachedValue = localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${this._options.persistKey}`); | ||
if (cachedValue) { | ||
try { | ||
return this.jsonToState(cachedValue); | ||
} catch (e) { | ||
const error = new Error(`Failed to parse JSON in localstorage for the key: "${LOCAL_STORAGE_PREFIX}${this._options.persistKey}"`); | ||
console.error(error); | ||
return error; | ||
} | ||
} | ||
return new Error("Key not found"); | ||
}; | ||
updateCache = () => { | ||
const { persistData, persistKey } = this._options; | ||
if (persistData && persistKey) { | ||
localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${persistKey}`, this.stateToJson(this.state)); | ||
} else { | ||
this.clearCache(); | ||
} | ||
}; | ||
} | ||
class BlocBase extends StreamAbstraction { | ||
id = createId(); | ||
createdAt = new Date(); | ||
meta = { | ||
scope: "unknown" | ||
}; | ||
changeListeners = []; | ||
registerListeners = []; | ||
valueChangeListeners = []; | ||
consumer = null; | ||
constructor(initialValue, blocOptions = {}) { | ||
super(initialValue, blocOptions); | ||
this.id = createId(); | ||
this.createdAt = new Date(); | ||
this.meta = { | ||
scope: "unknown" | ||
}; | ||
this.changeListeners = []; | ||
this.registerListeners = []; | ||
this.valueChangeListeners = []; | ||
this.consumer = null; | ||
this.removeChangeListener = (index) => { | ||
this.changeListeners.splice(index, 1); | ||
}; | ||
this.addChangeListener = (method) => { | ||
const index = this.changeListeners.length; | ||
this.changeListeners.push(method); | ||
return () => this.removeChangeListener(index); | ||
}; | ||
this.removeValueChangeListener = (index) => { | ||
this.valueChangeListeners.splice(index, 1); | ||
}; | ||
this.addValueChangeListener = (method) => { | ||
const index = this.valueChangeListeners.length; | ||
this.valueChangeListeners.push(method); | ||
return () => this.removeValueChangeListener(index); | ||
}; | ||
this.removeRegisterListener = (index) => { | ||
this.registerListeners.splice(index, 1); | ||
}; | ||
this.addRegisterListener = (method) => { | ||
const index = this.registerListeners.length; | ||
this.registerListeners.push(method); | ||
return () => this.removeRegisterListener(index); | ||
}; | ||
this.notifyChange = (state) => { | ||
this.consumer?.notifyChange(this, state); | ||
this.changeListeners.forEach((fn) => fn({ | ||
currentState: this.state, | ||
nextState: state | ||
}, this)); | ||
}; | ||
this.notifyValueChange = () => { | ||
this.consumer?.notifyValueChange(this); | ||
this.valueChangeListeners.forEach((fn) => fn(this.state, this)); | ||
}; | ||
} | ||
removeChangeListener = (index) => { | ||
this.changeListeners.splice(index, 1); | ||
}; | ||
addChangeListener = (method) => { | ||
const index = this.changeListeners.length; | ||
this.changeListeners.push(method); | ||
return () => this.removeChangeListener(index); | ||
}; | ||
removeValueChangeListener = (index) => { | ||
this.valueChangeListeners.splice(index, 1); | ||
}; | ||
addValueChangeListener = (method) => { | ||
const index = this.valueChangeListeners.length; | ||
this.valueChangeListeners.push(method); | ||
return () => this.removeValueChangeListener(index); | ||
}; | ||
removeRegisterListener = (index) => { | ||
this.registerListeners.splice(index, 1); | ||
}; | ||
addRegisterListener = (method) => { | ||
const index = this.registerListeners.length; | ||
this.registerListeners.push(method); | ||
return () => this.removeRegisterListener(index); | ||
}; | ||
notifyChange = (state) => { | ||
this.consumer?.notifyChange(this, state); | ||
this.changeListeners.forEach((fn) => fn({ | ||
currentState: this.state, | ||
nextState: state | ||
}, this)); | ||
}; | ||
notifyValueChange = () => { | ||
this.consumer?.notifyValueChange(this); | ||
this.valueChangeListeners.forEach((fn) => fn(this.state, this)); | ||
}; | ||
} | ||
class Bloc extends BlocBase { | ||
onTransition = null; | ||
mapEventToState = null; | ||
constructor(initialState, options) { | ||
super(initialState, options); | ||
this.onTransition = null; | ||
this.mapEventToState = null; | ||
this.add = (event) => { | ||
if (this.mapEventToState) { | ||
const newState = this.mapEventToState(event); | ||
this.notifyChange(newState); | ||
this.notifyTransition(newState, event); | ||
this.next(newState); | ||
this.notifyValueChange(); | ||
} else { | ||
console.error(`"mapEventToState" not implemented for "${this.constructor.name}"`); | ||
} | ||
}; | ||
this.notifyTransition = (state, event) => { | ||
this.consumer?.notifyTransition(this, state, event); | ||
this.onTransition?.({ | ||
currentState: this.state, | ||
event, | ||
nextState: state | ||
}); | ||
}; | ||
} | ||
add = (event) => { | ||
if (this.mapEventToState) { | ||
const newState = this.mapEventToState(event); | ||
this.notifyChange(newState); | ||
this.notifyTransition(newState, event); | ||
this.next(newState); | ||
this.notifyValueChange(); | ||
} else { | ||
console.error(`"mapEventToState" not implemented for "${this.constructor.name}"`); | ||
} | ||
}; | ||
notifyTransition = (state, event) => { | ||
this.consumer?.notifyTransition(this, state, event); | ||
this.onTransition?.({ | ||
currentState: this.state, | ||
event, | ||
nextState: state | ||
}); | ||
}; | ||
} | ||
class Cubit extends BlocBase { | ||
constructor() { | ||
super(...arguments); | ||
this.emit = (value) => { | ||
this.notifyChange(value); | ||
this.next(value); | ||
this.notifyValueChange(); | ||
}; | ||
} | ||
emit = (value) => { | ||
this.notifyChange(value); | ||
this.next(value); | ||
this.notifyValueChange(); | ||
}; | ||
} | ||
class BlocObserver { | ||
onChange; | ||
onTransition; | ||
constructor(methods = {}) { | ||
this.addChange = (bloc, state) => { | ||
this.onChange(bloc, this.createChangeEvent(bloc, state)); | ||
}; | ||
this.addTransition = (bloc, state, event) => { | ||
this.onTransition(bloc, this.createTransitionEvent(bloc, state, event)); | ||
}; | ||
this.addBlocAdded = (bloc) => { | ||
this.onBlocAdded(bloc); | ||
}; | ||
this.addBlocRemoved = (bloc) => { | ||
this.onBlocRemoved(bloc); | ||
}; | ||
this.defaultAction = () => { | ||
}; | ||
this.onBlocAdded = this.defaultAction; | ||
this.onBlocRemoved = this.defaultAction; | ||
this.onChange = methods.onChange ? methods.onChange : this.defaultAction; | ||
this.onTransition = methods.onTransition ? methods.onTransition : this.defaultAction; | ||
} | ||
addChange = (bloc, state) => { | ||
this.onChange(bloc, this.createChangeEvent(bloc, state)); | ||
}; | ||
addTransition = (bloc, state, event) => { | ||
this.onTransition(bloc, this.createTransitionEvent(bloc, state, event)); | ||
}; | ||
addBlocAdded = (bloc) => { | ||
this.onBlocAdded(bloc); | ||
}; | ||
addBlocRemoved = (bloc) => { | ||
this.onBlocRemoved(bloc); | ||
}; | ||
defaultAction = () => { | ||
}; | ||
onBlocAdded = this.defaultAction; | ||
onBlocRemoved = this.defaultAction; | ||
createTransitionEvent(bloc, state, event) { | ||
@@ -256,8 +259,10 @@ return { | ||
class BlocConsumer { | ||
observer; | ||
mocksEnabled = false; | ||
providerList = []; | ||
blocListGlobal; | ||
blocChangeObservers = []; | ||
blocValueChangeObservers = []; | ||
mockBlocs = []; | ||
constructor(blocs, options = {}) { | ||
this.mocksEnabled = false; | ||
this.providerList = []; | ||
this.blocChangeObservers = []; | ||
this.blocValueChangeObservers = []; | ||
this.mockBlocs = []; | ||
this.blocListGlobal = blocs; | ||
@@ -378,2 +383,3 @@ this.observer = options.observer || new BlocObserver(); | ||
class BlocRuntimeError { | ||
error; | ||
constructor(message) { | ||
@@ -386,21 +392,26 @@ this.error = new Error(message); | ||
class BlocReact extends BlocConsumer { | ||
providerCount = 0; | ||
_blocsGlobal; | ||
_contextLocalProviderKey = React__default["default"].createContext("none"); | ||
constructor(blocs, options) { | ||
super(blocs, options); | ||
this.providerCount = 0; | ||
this._contextLocalProviderKey = React__default['default'].createContext("none"); | ||
this.useBloc = (blocClass, options = {}) => { | ||
const mergedOptions = { | ||
...defaultBlocHookOptions, | ||
...options | ||
}; | ||
let blocInstance = React.useMemo(() => options.create ? options.create() : void 0, []); | ||
if (!blocInstance) { | ||
const localProviderKey = React.useContext(this._contextLocalProviderKey); | ||
const localBlocInstance = React.useMemo(() => this.getLocalBlocForProvider(localProviderKey, blocClass), []); | ||
blocInstance = React.useMemo(() => localBlocInstance || this.getGlobalBlocInstance(this._blocsGlobal, blocClass), []); | ||
} | ||
const { subscribe, shouldUpdate = true } = mergedOptions; | ||
if (!blocInstance) { | ||
const name = blocClass.prototype.constructor.name; | ||
const error = new BlocRuntimeError(`"${name}" | ||
this._blocsGlobal = blocs; | ||
this.BlocProvider = this.BlocProvider.bind(this); | ||
this.BlocBuilder = this.BlocBuilder.bind(this); | ||
} | ||
useBloc = (blocClass, options = {}) => { | ||
const mergedOptions = { | ||
...defaultBlocHookOptions, | ||
...options | ||
}; | ||
let blocInstance = React.useMemo(() => options.create ? options.create() : void 0, []); | ||
if (!blocInstance) { | ||
const localProviderKey = React.useContext(this._contextLocalProviderKey); | ||
const localBlocInstance = React.useMemo(() => this.getLocalBlocForProvider(localProviderKey, blocClass), []); | ||
blocInstance = React.useMemo(() => localBlocInstance || this.getGlobalBlocInstance(this._blocsGlobal, blocClass), []); | ||
} | ||
const { subscribe, shouldUpdate = true } = mergedOptions; | ||
if (!blocInstance) { | ||
const name = blocClass.prototype.constructor.name; | ||
const error = new BlocRuntimeError(`"${name}" | ||
no bloc with this name was found in the global context. | ||
@@ -420,55 +431,33 @@ | ||
`); | ||
console.error(error.error); | ||
return [ | ||
NoValue, | ||
{}, | ||
{ | ||
error, | ||
complete: true | ||
} | ||
]; | ||
} | ||
const [data, setData] = React.useState(blocInstance.state); | ||
const updateData = React.useCallback((nextState) => { | ||
if (shouldUpdate === true || shouldUpdate({ nextState, currentState: data })) { | ||
setData(nextState); | ||
console.error(error.error); | ||
return [ | ||
NoValue, | ||
{}, | ||
{ | ||
error, | ||
complete: true | ||
} | ||
}, []); | ||
React.useEffect(() => { | ||
if (subscribe) { | ||
const subscription = blocInstance?.subscribe({ | ||
next: updateData | ||
}); | ||
return () => { | ||
subscription?.unsubscribe(); | ||
}; | ||
} | ||
}, []); | ||
return [ | ||
data, | ||
blocInstance | ||
]; | ||
}; | ||
this.withBlocProvider = (bloc) => (Component) => { | ||
const displayName = Component.displayName || Component.name; | ||
const Provider = (props) => { | ||
return /* @__PURE__ */ React__default['default'].createElement(this.BlocProvider, { | ||
bloc | ||
}, props.children); | ||
}; | ||
Provider.displayName = `withBlocProvider`; | ||
const Wrapper = (props) => { | ||
return /* @__PURE__ */ React__default['default'].createElement(Provider, { | ||
bloc | ||
}, /* @__PURE__ */ React__default['default'].createElement(Component, { | ||
...props | ||
})); | ||
}; | ||
Wrapper.displayName = `withBlocProvider(${displayName})`; | ||
return Wrapper; | ||
}; | ||
this._blocsGlobal = blocs; | ||
this.BlocProvider = this.BlocProvider.bind(this); | ||
this.BlocBuilder = this.BlocBuilder.bind(this); | ||
} | ||
} | ||
const [data, setData] = React.useState(blocInstance.state); | ||
const updateData = React.useCallback((nextState) => { | ||
if (shouldUpdate === true || shouldUpdate({ nextState, currentState: data })) { | ||
setData(nextState); | ||
} | ||
}, []); | ||
React.useEffect(() => { | ||
if (subscribe) { | ||
const subscription = blocInstance?.subscribe({ | ||
next: updateData | ||
}); | ||
return () => { | ||
subscription?.unsubscribe(); | ||
}; | ||
} | ||
}, []); | ||
return [ | ||
data, | ||
blocInstance | ||
]; | ||
}; | ||
BlocBuilder(props) { | ||
@@ -497,3 +486,3 @@ const hook = this.useBloc(props.blocClass, { | ||
const context = React.useMemo(() => { | ||
return React__default['default'].createContext(bloc); | ||
return React__default["default"].createContext(bloc); | ||
}, [bloc]); | ||
@@ -505,8 +494,20 @@ React.useEffect(() => { | ||
}, []); | ||
return /* @__PURE__ */ React__default['default'].createElement(this._contextLocalProviderKey.Provider, { | ||
return /* @__PURE__ */ React__default["default"].createElement(this._contextLocalProviderKey.Provider, { | ||
value: id | ||
}, /* @__PURE__ */ React__default['default'].createElement(context.Provider, { | ||
}, /* @__PURE__ */ React__default["default"].createElement(context.Provider, { | ||
value: bloc | ||
}, props.children)); | ||
} | ||
withBlocProvider = (bloc) => (Component) => { | ||
const { BlocProvider } = this; | ||
return class WithBlocProvider extends React__default["default"].Component { | ||
render() { | ||
return /* @__PURE__ */ React__default["default"].createElement(BlocProvider, { | ||
bloc | ||
}, /* @__PURE__ */ React__default["default"].createElement(Component, { | ||
...this.props | ||
})); | ||
} | ||
}; | ||
}; | ||
} | ||
@@ -513,0 +514,0 @@ |
{ | ||
"name": "bloc-react", | ||
"version": "0.1.21", | ||
"version": "0.1.22", | ||
"license": "MIT", | ||
@@ -27,6 +27,6 @@ "main": "dist/bloc-react.js", | ||
"devDependencies": { | ||
"@babel/core": "^7.16.0", | ||
"@babel/preset-env": "^7.16.4", | ||
"@babel/preset-react": "^7.16.0", | ||
"@babel/preset-typescript": "^7.16.0", | ||
"@babel/core": "^7.16.5", | ||
"@babel/preset-env": "^7.16.5", | ||
"@babel/preset-react": "^7.16.5", | ||
"@babel/preset-typescript": "^7.16.5", | ||
"@material-ui/core": "^4.12.3", | ||
@@ -36,3 +36,3 @@ "@material-ui/icons": "^4.11.2", | ||
"@rollup/plugin-babel": "^5.3.0", | ||
"@rollup/plugin-node-resolve": "^13.0.6", | ||
"@rollup/plugin-node-resolve": "^13.1.1", | ||
"@testing-library/react": "^12.1.2", | ||
@@ -47,10 +47,10 @@ "@testing-library/react-hooks": "^7.0.2", | ||
"@types/react-router-dom": "^5.3.2", | ||
"@typescript-eslint/eslint-plugin": "^5.5.0", | ||
"@typescript-eslint/parser": "^5.5.0", | ||
"@typescript-eslint/eslint-plugin": "^5.7.0", | ||
"@typescript-eslint/parser": "^5.7.0", | ||
"@vitejs/plugin-react-refresh": "^1.3.6", | ||
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.5", | ||
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.6", | ||
"codecov": "^3.8.3", | ||
"enzyme": "^3.11.0", | ||
"esbuild": "^0.14.2", | ||
"eslint": "^8.4.0", | ||
"esbuild": "^0.14.5", | ||
"eslint": "^8.4.1", | ||
"eslint-config-prettier": "^8.3.0", | ||
@@ -71,3 +71,3 @@ "eslint-config-standard": "^16.0.3", | ||
"react-router-dom": "^5.3.0", | ||
"rollup": "^2.60.2", | ||
"rollup": "^2.61.1", | ||
"rollup-plugin-babel": "^4.4.0", | ||
@@ -81,4 +81,4 @@ "rollup-plugin-commonjs": "^10.1.0", | ||
"ts-jest": "^26.5.6", | ||
"typescript": "^4.5.2", | ||
"vite": "^2.6.14" | ||
"typescript": "^4.5.4", | ||
"vite": "^2.7.2" | ||
}, | ||
@@ -85,0 +85,0 @@ "jest": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1132
107773