@chhsiao1981/use-thunk 9.0.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +437 -0
  3. package/package.json +63 -0
  4. package/src/action.ts +20 -0
  5. package/src/addChild.ts +13 -0
  6. package/src/addLink.ts +27 -0
  7. package/src/addRelation.ts +32 -0
  8. package/src/createReducer.ts +24 -0
  9. package/src/dispatch.ts +6 -0
  10. package/src/dispatchFuncMap.ts +22 -0
  11. package/src/genUUID.ts +38 -0
  12. package/src/getRelation.ts +35 -0
  13. package/src/index.ts +62 -0
  14. package/src/init.ts +82 -0
  15. package/src/reduceMap.ts +37 -0
  16. package/src/reducer.ts +9 -0
  17. package/src/remove.ts +76 -0
  18. package/src/removeChild.ts +46 -0
  19. package/src/removeLink.ts +47 -0
  20. package/src/removeRelation.ts +84 -0
  21. package/src/setData.ts +23 -0
  22. package/src/setRoot.ts +14 -0
  23. package/src/stateTypes.ts +62 -0
  24. package/src/states.ts +45 -0
  25. package/src/thunk.ts +16 -0
  26. package/src/thunkModuleFuncMap.ts +22 -0
  27. package/src/thunkReducer.ts +73 -0
  28. package/src/useThunk.ts +93 -0
  29. package/types/action.d.ts +11 -0
  30. package/types/addChild.d.ts +5 -0
  31. package/types/addLink.d.ts +6 -0
  32. package/types/addRelation.d.ts +6 -0
  33. package/types/createReducer.d.ts +4 -0
  34. package/types/dispatch.d.ts +4 -0
  35. package/types/dispatchFuncMap.d.ts +17 -0
  36. package/types/genUUID.d.ts +1 -0
  37. package/types/getRelation.d.ts +5 -0
  38. package/types/index.d.ts +19 -0
  39. package/types/init.d.ts +19 -0
  40. package/types/reduceMap.d.ts +6 -0
  41. package/types/reducer.d.ts +5 -0
  42. package/types/reducerModuleFuncMap.d.ts +10 -0
  43. package/types/remove.d.ts +5 -0
  44. package/types/removeChild.d.ts +9 -0
  45. package/types/removeLink.d.ts +9 -0
  46. package/types/removeRelation.d.ts +12 -0
  47. package/types/setData.d.ts +5 -0
  48. package/types/setRoot.d.ts +5 -0
  49. package/types/stateTypes.d.ts +42 -0
  50. package/types/states.d.ts +6 -0
  51. package/types/thunk-reducer.d.ts +16 -0
  52. package/types/thunk.d.ts +12 -0
  53. package/types/thunkModuleFuncMap.d.ts +10 -0
  54. package/types/thunkReducer.d.ts +18 -0
  55. package/types/useReducer.d.ts +8 -0
  56. package/types/useThunk.d.ts +8 -0
package/src/genUUID.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { v4 as uuidv4 } from 'uuid'
2
+
3
+ const _GLOBAL_IDS = new Set()
4
+
5
+ const _GEN_UUID_COUNT = 3
6
+
7
+ const _GEN_UUID_STATE = {
8
+ iterate: 1,
9
+ }
10
+
11
+ export const genUUID = (myuuidv4?: () => string): string => {
12
+ let theID = ''
13
+ let isAdd = false
14
+ for (let i = 0; i < _GEN_UUID_COUNT; i++) {
15
+ theID = genUUIDCore(myuuidv4)
16
+ if (_GLOBAL_IDS.has(theID)) {
17
+ continue
18
+ }
19
+ _GLOBAL_IDS.add(theID)
20
+ isAdd = true
21
+ break
22
+ }
23
+ if (isAdd) {
24
+ return theID
25
+ }
26
+
27
+ _GEN_UUID_STATE.iterate += 1
28
+ theID = genUUIDCore(myuuidv4)
29
+ return theID
30
+ }
31
+
32
+ const genUUIDCore = (myuuid: () => string = uuidv4): string => {
33
+ let theID = ''
34
+ for (let j = 0; j < _GEN_UUID_STATE.iterate; j++) {
35
+ theID += myuuid()
36
+ }
37
+ return theID
38
+ }
@@ -0,0 +1,35 @@
1
+ import { type NodeState, Relation, type State } from './stateTypes'
2
+
3
+ export const getChildIDs = <S extends State>(me: NodeState<S>, childClass: string): string[] => {
4
+ return getRelationIDs(me, childClass, Relation.CHILDREN)
5
+ }
6
+
7
+ export const getChildID = <S extends State>(me: NodeState<S>, childClass: string): string => {
8
+ return getRelationID(me, childClass, Relation.CHILDREN)
9
+ }
10
+
11
+ export const getLinkIDs = <S extends State>(me: NodeState<S>, linkClass: string): string[] => {
12
+ return getRelationIDs(me, linkClass, Relation.LINKS)
13
+ }
14
+
15
+ export const getLinkID = <S extends State>(me: NodeState<S>, linkClass: string): string => {
16
+ return getRelationID(me, linkClass, Relation.LINKS)
17
+ }
18
+
19
+ const getRelationIDs = <S extends State>(me: NodeState<S>, relationClass: string, relationName: Relation): string[] => {
20
+ const relations = me[relationName]
21
+ if (!relations) {
22
+ return []
23
+ }
24
+ const relationsByClass = relations[relationClass]
25
+ if (!relationsByClass) {
26
+ return []
27
+ }
28
+
29
+ return relationsByClass.list
30
+ }
31
+
32
+ const getRelationID = <S extends State>(me: NodeState<S>, relationClass: string, relationName: Relation): string => {
33
+ const ids = getRelationIDs(me, relationClass, relationName)
34
+ return ids.length ? ids[0] : ''
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,62 @@
1
+ import type { GetClassState, Thunk } from './action'
2
+ import { addChild } from './addChild'
3
+ import { addLink } from './addLink'
4
+ import type { AddRelationAction } from './addRelation'
5
+ import type { Dispatch } from './dispatch'
6
+ import type { DispatchFuncMap } from './dispatchFuncMap'
7
+ import { genUUID } from './genUUID'
8
+ import { getChildID, getChildIDs, getLinkID, getLinkIDs } from './getRelation'
9
+ import { type InitParams, init } from './init'
10
+ import { remove } from './remove'
11
+ import { removeChild } from './removeChild'
12
+ import { removeLink } from './removeLink'
13
+ import type { RemoveRelationAction } from './removeRelation'
14
+ import { setData } from './setData'
15
+ import { getNode, getRootID, getState } from './states'
16
+ import type { ClassState, NodeMeta, NodeState, NodeStateMap, State } from './stateTypes'
17
+ import type { ThunkModule, ThunkModuleToFunc } from './thunk'
18
+ import useThunk from './useThunk'
19
+
20
+ export {
21
+ useThunk,
22
+ // StateType, // XXX for global state
23
+ type State,
24
+ type NodeState,
25
+ type NodeMeta,
26
+ type NodeStateMap,
27
+ // type NodeStateMapByClass, // XXX for global state
28
+ type ClassState,
29
+ type GetClassState,
30
+ // type BaseAction, // XXX deemphasize action
31
+ type Thunk,
32
+ // type ActionOrThunk, // XXX deemphasize action
33
+ // type ActionFunc, // XXX deemphasize action
34
+ // type Reducer, // XXX deemphasize reducer
35
+ type ThunkModule,
36
+ type ThunkModuleToFunc,
37
+ // type ReduceFunc, // XXX deemphasize reducer
38
+ type Dispatch,
39
+ type DispatchFuncMap,
40
+ // type DefaultDispatchFuncMap, // XXX deemphasize default
41
+ getRootID,
42
+ getNode,
43
+ getState,
44
+ getChildIDs,
45
+ getChildID,
46
+ getLinkIDs,
47
+ getLinkID,
48
+ init,
49
+ type InitParams,
50
+ setData,
51
+ remove,
52
+ addChild,
53
+ removeChild,
54
+ addLink,
55
+ removeLink,
56
+ type AddRelationAction,
57
+ type RemoveRelationAction,
58
+ // type DefaultThunkModuleFuncMap as DefaultReducerModuleFuncMap, // XXX deemphasize default
59
+ // type ReduceMap, // XXX deemphasize reducer
60
+ // createReducer, // XXX deemphasize reducer
61
+ genUUID,
62
+ }
package/src/init.ts ADDED
@@ -0,0 +1,82 @@
1
+ import type { BaseAction, Thunk } from './action'
2
+ import { genUUID } from './genUUID'
3
+ import { setRoot } from './setRoot'
4
+ import { type ClassState, type NodeState, type NodeStateMap, PARENT, Relation, type State } from './stateTypes'
5
+
6
+ // InitParams
7
+ export interface InitParams<S extends State> {
8
+ myID?: string
9
+ parentID?: string
10
+ // @ts-expect-error doParent can be any type.
11
+ doParent?: DispatchFuncMap
12
+ parentClass?: string
13
+
14
+ state: S
15
+ }
16
+
17
+ export const init = <S extends State>(params: InitParams<S>, myuuidv4?: () => string): Thunk<S> => {
18
+ return (dispatch, getClassState) => {
19
+ const myID = params.myID ?? genUUID(myuuidv4)
20
+
21
+ const { parentID, doParent, parentClass, state } = params
22
+ dispatch(initCore(myID, state, parentID, doParent, parentClass))
23
+
24
+ // @ts-expect-error XXX doMe is a hidden variable for children.
25
+ const { myClass, doMe, root } = getClassState()
26
+
27
+ // parent or root
28
+ if (parentID && doParent) {
29
+ doParent.addChild(parentID, { id: myID, theClass: myClass, do: doMe })
30
+ } else if (!root) {
31
+ dispatch(setRoot(myID))
32
+ }
33
+ }
34
+ }
35
+
36
+ interface InitAction<S extends State> extends BaseAction {
37
+ parentID?: string
38
+ // @ts-expect-error doParent can be any type
39
+ doParent?: DispatchFuncMap
40
+ parentClass?: string
41
+
42
+ state: S
43
+ }
44
+
45
+ export const INIT = '@chhsiao1981/use-thunk/INIT'
46
+ const initCore = <S extends State>(
47
+ myID: string,
48
+ state: S,
49
+ parentID?: string,
50
+ // @ts-expect-error doParent can be any type
51
+ doParent?: DispatchFuncMap,
52
+ parentClass?: string,
53
+ ): InitAction<S> => {
54
+ return {
55
+ myID,
56
+ type: INIT,
57
+ parentID,
58
+ doParent,
59
+ parentClass,
60
+
61
+ state,
62
+ }
63
+ }
64
+
65
+ export const reduceInit = <S extends State>(state: ClassState<S>, action: InitAction<S>): ClassState<S> => {
66
+ const { myID, parentID, doParent, parentClass, state: initState } = action
67
+
68
+ const me: NodeState<S> = {
69
+ id: myID,
70
+ state: initState,
71
+ [Relation.CHILDREN]: {},
72
+ [Relation.LINKS]: {},
73
+ }
74
+ if (parentID && doParent) {
75
+ me[PARENT] = { id: parentID, do: doParent, theClass: parentClass ?? '' }
76
+ }
77
+
78
+ const newNodes: NodeStateMap<S> = Object.assign({}, state.nodes, { [myID]: me })
79
+ const newState: ClassState<S> = Object.assign({}, state, { nodes: newNodes })
80
+
81
+ return newState
82
+ }
@@ -0,0 +1,37 @@
1
+ import { ADD_CHILD, reduceAddChild } from './addChild'
2
+ import { ADD_LINK, reduceAddLink } from './addLink'
3
+ import { INIT, reduceInit } from './init'
4
+ import type { ReduceFunc } from './reducer'
5
+ import { REMOVE, reduceRemove } from './remove'
6
+ import { REMOVE_CHILD, reduceRemoveChild } from './removeChild'
7
+ import { REMOVE_LINK, reduceRemoveLink } from './removeLink'
8
+ import { reduceSetData, SET_DATA } from './setData'
9
+ import { reduceSetRoot, SET_ROOT } from './setRoot'
10
+ import type { State } from './stateTypes'
11
+
12
+ export interface ReduceMap<S extends State> {
13
+ [type: string]: ReduceFunc<S>
14
+ }
15
+
16
+ // default reduceMap
17
+ export const DEFAULT_REDUCE_MAP: <S extends State>() => ReduceMap<S> = () => ({
18
+ // @ts-expect-error baseAction in ReduceMap
19
+ [INIT]: reduceInit,
20
+ [SET_DATA]: reduceSetData,
21
+ [REMOVE]: reduceRemove,
22
+
23
+ // @ts-expect-error baseAction in ReduceMap
24
+ [ADD_CHILD]: reduceAddChild,
25
+ // @ts-expect-error baseAction in ReduceMap
26
+ [REMOVE_CHILD]: reduceRemoveChild,
27
+
28
+ // @ts-expect-error baseAction in ReduceMap
29
+ [ADD_LINK]: reduceAddLink,
30
+ // @ts-expect-error baseAction in ReduceMap
31
+ [REMOVE_LINK]: reduceRemoveLink,
32
+
33
+ // setRoot.
34
+ // Typically we don't need this in programming.
35
+ // The root is automatically determined if root is not set.
36
+ [SET_ROOT]: reduceSetRoot,
37
+ })
package/src/reducer.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { Reducer as rReducer } from 'react'
2
+ import type { ActionFunc, BaseAction } from './action'
3
+ import type { ClassState, State } from './stateTypes'
4
+
5
+ // Reducer
6
+ export type Reducer<S extends State> = rReducer<ClassState<S>, BaseAction>
7
+
8
+ // ReduceFunc
9
+ export type ReduceFunc<S extends State> = (state: ClassState<S>, action: BaseAction) => ClassState<S>
package/src/remove.ts ADDED
@@ -0,0 +1,76 @@
1
+ import type { BaseAction, Thunk } from './action'
2
+ import { removeChild } from './removeChild'
3
+ import { removeLink } from './removeLink'
4
+ import { type ClassState, type NodeStateMap, PARENT, Relation, type State } from './stateTypes'
5
+
6
+ export const remove = <S extends State>(myID: string, isFromParent = false): Thunk<S> => {
7
+ return (dispatch, getClassState) => {
8
+ const state = getClassState()
9
+ const {
10
+ myClass,
11
+ nodes: { [myID]: me },
12
+ } = state
13
+ if (!me) {
14
+ return
15
+ }
16
+
17
+ // parent removes me
18
+ const parent = me[PARENT]
19
+ if (!isFromParent && parent) {
20
+ const { id: parentID, do: doParent } = parent
21
+ if (parentID) {
22
+ doParent.removeChild(parentID, myID, myClass, true)
23
+ }
24
+ }
25
+
26
+ // remove children
27
+ const children = me[Relation.CHILDREN]
28
+ if (children) {
29
+ const realChildren = children
30
+ Object.keys(realChildren).map((eachClass) => {
31
+ const child = realChildren[eachClass]
32
+ child.list.map((eachID) => dispatch(removeChild(myID, eachID, eachClass, false)))
33
+ })
34
+ }
35
+
36
+ // remove links
37
+ const links = me[Relation.LINKS] ?? {}
38
+ Object.keys(links).map((eachClass) => {
39
+ const link = links[eachClass]
40
+ link.list.map((eachID) => dispatch(removeLink(myID, eachID, eachClass, false)))
41
+ })
42
+
43
+ // remove me from myClass list
44
+ dispatch(removeCore(myID))
45
+ }
46
+ }
47
+
48
+ export const REMOVE = '@chhsiao1981/use-thunk/REMOVE'
49
+ const removeCore = (myID: string): BaseAction => ({
50
+ myID,
51
+ type: REMOVE,
52
+ })
53
+
54
+ export const reduceRemove = <S extends State>(state: ClassState<S>, action: BaseAction): ClassState<S> => {
55
+ const { myID } = action
56
+
57
+ const me = state.nodes[myID]
58
+ if (!me) {
59
+ return state
60
+ }
61
+
62
+ const newNodes = Object.keys(state.nodes)
63
+ .filter((each) => each !== myID)
64
+ .reduce((r: NodeStateMap<S>, x) => {
65
+ r[x] = state.nodes[x]
66
+ return r
67
+ }, {})
68
+
69
+ // root
70
+ const newState = Object.assign({}, state, { nodes: newNodes })
71
+ if (newState.root === myID) {
72
+ newState.root = null
73
+ }
74
+
75
+ return newState
76
+ }
@@ -0,0 +1,46 @@
1
+ import type { Thunk } from './action'
2
+ import { type RemoveRelationAction, reduceRemoveRelation, removeRelation } from './removeRelation'
3
+ import { type ClassState, Relation, type State } from './stateTypes'
4
+
5
+ /***
6
+ * remove-child
7
+ */
8
+ export const removeChild = <S extends State>(
9
+ myID: string,
10
+ childID: string,
11
+ childClass: string,
12
+ isFromChild = false,
13
+ ): Thunk<S> => {
14
+ return (dispatch, getClassState) => {
15
+ // @ts-expect-error theDo (from child) can by any type
16
+ const relationRemove = (theDo: DispatchFuncMap) => theDo.remove(childID, true)
17
+ removeRelation(
18
+ dispatch,
19
+ getClassState,
20
+ myID,
21
+ childID,
22
+ childClass,
23
+ isFromChild,
24
+ relationRemove,
25
+ removeChildCore,
26
+ Relation.CHILDREN,
27
+ )
28
+ }
29
+ }
30
+
31
+ export const REMOVE_CHILD = '@chhsiao1981/use-thunk/REMOVE_CHILD'
32
+ const removeChildCore = (myID: string, childID: string, childClass: string): RemoveRelationAction => ({
33
+ myID,
34
+ type: REMOVE_CHILD,
35
+ relationID: childID,
36
+ relationClass: childClass,
37
+ })
38
+
39
+ export const reduceRemoveChild = <S extends State>(
40
+ state: ClassState<S>,
41
+ action: RemoveRelationAction,
42
+ ): ClassState<S> => {
43
+ const { myID, relationID, relationClass } = action
44
+
45
+ return reduceRemoveRelation(state, myID, relationID, relationClass, Relation.CHILDREN)
46
+ }
@@ -0,0 +1,47 @@
1
+ import type { Thunk } from './action'
2
+ import { type RemoveRelationAction, reduceRemoveRelation, removeRelation } from './removeRelation'
3
+ import { type ClassState, Relation, type State } from './stateTypes'
4
+
5
+ /***
6
+ * remove-link
7
+ */
8
+ export const removeLink = <S extends State>(
9
+ myID: string,
10
+ linkID: string,
11
+ linkClass: string,
12
+ isFromLink = false,
13
+ ): Thunk<S> => {
14
+ return (dispatch, getClassState) => {
15
+ const myClass = getClassState().myClass
16
+ // @ts-expect-error theDo (from link) can be any type
17
+ const relationRemove = (theDo: DispatchFuncMap) => theDo.removeLink(linkID, myID, myClass, true)
18
+ removeRelation(
19
+ dispatch,
20
+ getClassState,
21
+ myID,
22
+ linkID,
23
+ linkClass,
24
+ isFromLink,
25
+ relationRemove,
26
+ removeLinkCore,
27
+ Relation.LINKS,
28
+ )
29
+ }
30
+ }
31
+
32
+ export const REMOVE_LINK = '@chhsiao1981/use-thunk/REMOVE_LINK'
33
+ const removeLinkCore = (myID: string, linkID: string, linkClass: string): RemoveRelationAction => ({
34
+ myID,
35
+ type: REMOVE_LINK,
36
+ relationID: linkID,
37
+ relationClass: linkClass,
38
+ })
39
+
40
+ export const reduceRemoveLink = <S extends State>(
41
+ state: ClassState<S>,
42
+ action: RemoveRelationAction,
43
+ ): ClassState<S> => {
44
+ const { myID, relationID, relationClass } = action
45
+
46
+ return reduceRemoveRelation(state, myID, relationID, relationClass, Relation.LINKS)
47
+ }
@@ -0,0 +1,84 @@
1
+ import type { BaseAction, GetClassState } from './action'
2
+ import type { Dispatch } from './dispatch'
3
+ import type { ClassState, Relation, State } from './stateTypes'
4
+
5
+ export interface RemoveRelationAction extends BaseAction {
6
+ relationID: string
7
+ relationClass: string
8
+ }
9
+
10
+ // @ts-expect-error DispatchFuncMap can be any type
11
+ type RelationRemove = (theDo: DispatchFuncMap) => void
12
+ type RemoveRelationCore = (myID: string, relationID: string, relationClass: string) => BaseAction
13
+
14
+ export const removeRelation = <S extends State>(
15
+ dispatch: Dispatch<S>,
16
+ getClassState: GetClassState<S>,
17
+ myID: string,
18
+ relationID: string,
19
+ relationClass: string,
20
+ isFromRelation: boolean,
21
+ relationRemove: RelationRemove,
22
+ removeRelationCore: RemoveRelationCore,
23
+ relationName: Relation,
24
+ ) => {
25
+ const state = getClassState()
26
+
27
+ const me = state.nodes[myID]
28
+ if (!me) {
29
+ return
30
+ }
31
+
32
+ const relation = me[relationName]
33
+ if (!relation) {
34
+ return
35
+ }
36
+ const relationByClass = relation[relationClass]
37
+ if (!relationByClass) {
38
+ return
39
+ }
40
+
41
+ const newIDs = relationByClass.list.filter((eachID: string) => eachID !== relationID)
42
+ if (relationByClass.list.length === newIDs.length) return
43
+
44
+ if (!isFromRelation) {
45
+ relationRemove(relationByClass.do)
46
+ }
47
+
48
+ dispatch(removeRelationCore(myID, relationID, relationClass))
49
+ }
50
+
51
+ export const reduceRemoveRelation = <S extends State>(
52
+ state: ClassState<S>,
53
+ myID: string,
54
+ relationID: string,
55
+ relationClass: string,
56
+ relationName: Relation.LINKS | Relation.CHILDREN,
57
+ ): ClassState<S> => {
58
+ const me = state.nodes[myID]
59
+ if (!me) return state
60
+
61
+ const relation = me[relationName]
62
+ if (!relation) return state
63
+
64
+ const relationByClass = relation[relationClass]
65
+ if (!relationByClass) return state
66
+
67
+ const relationIDs = relationByClass.list || []
68
+ const newIDs = relationIDs.filter((eachID: string) => eachID !== relationID)
69
+ if (relationIDs.length === newIDs.length) return state
70
+
71
+ const newRelation = Object.assign({}, relation)
72
+ if (newIDs.length === 0) {
73
+ delete newRelation[relationClass]
74
+ } else {
75
+ const newRelationByClass = Object.assign({}, relationByClass, { list: newIDs })
76
+ newRelation[relationClass] = newRelationByClass
77
+ }
78
+
79
+ const newMe = Object.assign({}, me, { [relationName]: newRelation })
80
+ const newNodes = Object.assign({}, state.nodes, { [myID]: newMe })
81
+ const newState = Object.assign({}, state, { nodes: newNodes })
82
+
83
+ return newState
84
+ }
package/src/setData.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { BaseAction } from './action'
2
+ import type { ClassState, State } from './stateTypes'
3
+
4
+ export const SET_DATA = '@chhsiao1981/use-thunk/SET_DATA'
5
+ export const setData = <S extends State>(myID: string, data: S): BaseAction => ({
6
+ myID,
7
+ type: SET_DATA,
8
+ data,
9
+ })
10
+
11
+ export const reduceSetData = <S extends State>(state: ClassState<S>, action: BaseAction): ClassState<S> => {
12
+ const { myID, data } = action
13
+
14
+ const me = state.nodes[myID]
15
+ if (!me) return state
16
+
17
+ const newMyState = Object.assign({}, me.state, data)
18
+ const newMe = Object.assign({}, me, { state: newMyState })
19
+ const newNodes = Object.assign({}, state.nodes, { [myID]: newMe })
20
+ const newState = Object.assign({}, state, { nodes: newNodes })
21
+
22
+ return newState
23
+ }
package/src/setRoot.ts ADDED
@@ -0,0 +1,14 @@
1
+ import type { BaseAction } from './action'
2
+ import type { ClassState, State } from './stateTypes'
3
+
4
+ export const SET_ROOT = '@chhsiao1981/use-thunk/SET_ROOT'
5
+ export const setRoot = (myID: string): BaseAction => ({
6
+ myID,
7
+ type: SET_ROOT,
8
+ })
9
+
10
+ export const reduceSetRoot = <S extends State>(state: ClassState<S>, action: BaseAction): ClassState<S> => {
11
+ const { myID } = action
12
+
13
+ return Object.assign({}, state, { root: myID })
14
+ }
@@ -0,0 +1,62 @@
1
+ export enum StateType {
2
+ // SHARED = 'shared',
3
+ LOCAL = 'local',
4
+ }
5
+
6
+ export enum Relation {
7
+ CHILDREN = '_children',
8
+ LINKS = '_links',
9
+ }
10
+
11
+ export const PARENT = '_parent'
12
+
13
+ //State
14
+ export interface State {
15
+ [key: string]: unknown
16
+ }
17
+
18
+ // NodeState
19
+ export type NodeState<S extends State> = {
20
+ id: string
21
+ state: S
22
+ [Relation.CHILDREN]?: NodeStateRelationMap | null
23
+ [PARENT]?: NodeMeta | null
24
+ [Relation.LINKS]?: NodeStateRelationMap | null
25
+ }
26
+
27
+ // NodeStateRelation
28
+ type NodeStateRelationMap = {
29
+ [relationClass: string]: NodeStateRelation
30
+ }
31
+
32
+ type NodeStateRelation = {
33
+ list: string[]
34
+ // @ts-expect-error do can be any type.
35
+ do: DispatchFuncMap
36
+ }
37
+
38
+ export type NodeStateMap<S extends State> = {
39
+ [key: string]: NodeState<S>
40
+ }
41
+
42
+ export type NodeStateMapByClass<S extends State> = {
43
+ [className: string]: NodeStateMap<S>
44
+ }
45
+
46
+ // ClassState
47
+ export type ClassState<S extends State> = {
48
+ myClass: string
49
+ root?: string | null
50
+ // XXX doMe is a hidden variable for ClassState
51
+ // used only for parents / children / links.
52
+ // doMe: DispatchFuncMap
53
+ nodes: NodeStateMap<S>
54
+ }
55
+
56
+ // Node
57
+ export type NodeMeta = {
58
+ id: string
59
+ theClass: string
60
+ // @ts-expect-error do can be any type.
61
+ do: DispatchFuncMap
62
+ }
package/src/states.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { ClassState, NodeState, State } from './stateTypes'
2
+
3
+ export const getRootNode = <S extends State>(state: ClassState<S>): NodeState<S> | null => {
4
+ const root = state.root
5
+ if (!root) {
6
+ return null
7
+ }
8
+ return state.nodes[root] || null
9
+ }
10
+
11
+ export const getRootID = <S extends State>(state: ClassState<S>): string => {
12
+ return state.root ?? ''
13
+ }
14
+
15
+ export const getRoot = <S extends State>(state: ClassState<S>): S | null => {
16
+ const root = state.root
17
+ if (!root) {
18
+ return null
19
+ }
20
+ const me = state.nodes[root]
21
+ if (!me) {
22
+ return null
23
+ }
24
+ return me.state
25
+ }
26
+
27
+ export const getNode = <S extends State>(state: ClassState<S>, myID?: string): NodeState<S> | null => {
28
+ if (!myID) {
29
+ return getRootNode(state)
30
+ }
31
+
32
+ return state.nodes[myID] || null
33
+ }
34
+
35
+ export const getState = <S extends State>(state: ClassState<S>, myID?: string): S | null => {
36
+ if (!myID) {
37
+ return getRoot(state)
38
+ }
39
+
40
+ const me = state.nodes[myID]
41
+ if (!me) {
42
+ return null
43
+ }
44
+ return me.state
45
+ }
package/src/thunk.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { ActionFunc } from './action'
2
+ import type { Reducer } from './reducer'
3
+ import type { State } from './stateTypes'
4
+
5
+ export interface ThunkModuleFunc<S extends State> {
6
+ [action: string]: ActionFunc<S>
7
+ }
8
+
9
+ // This is used as the parameter for useThunk.
10
+ export type ThunkModule<S extends State, T extends ThunkModuleFunc<S>> = {
11
+ myClass: string
12
+ default?: Reducer<S>
13
+ defaultState?: S
14
+ } & T
15
+
16
+ export type ThunkModuleToFunc<T> = Omit<T, 'myClass' | 'default' | 'defaultState'>