react-better-model 0.2.2 → 1.0.0-beta

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 (99) hide show
  1. package/AGENTS.md +43 -0
  2. package/LICENSE +21 -4
  3. package/lib/ModelBase.d.ts +15 -0
  4. package/lib/ModelBase.d.ts.map +1 -0
  5. package/lib/ModelBase.js +91 -0
  6. package/lib/ModelBase.js.map +1 -0
  7. package/lib/ModelWithHooks.d.ts +9 -0
  8. package/lib/ModelWithHooks.d.ts.map +1 -0
  9. package/lib/ModelWithHooks.js +108 -0
  10. package/lib/ModelWithHooks.js.map +1 -0
  11. package/lib/common-types.d.ts +9 -0
  12. package/lib/common-types.d.ts.map +1 -0
  13. package/lib/{types.js → common-types.js} +1 -1
  14. package/lib/common-types.js.map +1 -0
  15. package/lib/create-model.d.ts +18 -0
  16. package/lib/create-model.d.ts.map +1 -0
  17. package/lib/create-model.js +78 -0
  18. package/lib/create-model.js.map +1 -0
  19. package/lib/create-model.test.d.ts +2 -0
  20. package/lib/create-model.test.d.ts.map +1 -0
  21. package/lib/create-model.test.js +80 -0
  22. package/lib/create-model.test.js.map +1 -0
  23. package/lib/index.d.ts +4 -8
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +22 -9
  26. package/lib/index.js.map +1 -1
  27. package/lib/src/ModelBase.d.ts +16 -0
  28. package/lib/src/ModelBase.d.ts.map +1 -0
  29. package/lib/src/ModelBase.js +97 -0
  30. package/lib/src/ModelBase.js.map +1 -0
  31. package/lib/src/ModelWithHooks.d.ts +8 -0
  32. package/lib/src/ModelWithHooks.d.ts.map +1 -0
  33. package/lib/src/ModelWithHooks.js +57 -0
  34. package/lib/src/ModelWithHooks.js.map +1 -0
  35. package/lib/src/common-types.d.ts +9 -0
  36. package/lib/src/common-types.d.ts.map +1 -0
  37. package/lib/src/common-types.js +3 -0
  38. package/lib/src/common-types.js.map +1 -0
  39. package/lib/src/create-model.d.ts +18 -0
  40. package/lib/src/create-model.d.ts.map +1 -0
  41. package/lib/src/create-model.js +78 -0
  42. package/lib/src/create-model.js.map +1 -0
  43. package/lib/src/index.d.ts +5 -0
  44. package/lib/src/index.d.ts.map +1 -0
  45. package/lib/src/index.js +34 -0
  46. package/lib/src/index.js.map +1 -0
  47. package/lib/src/utils.d.ts +4 -0
  48. package/lib/src/utils.d.ts.map +1 -0
  49. package/lib/src/utils.js +36 -0
  50. package/lib/src/utils.js.map +1 -0
  51. package/lib/tests/ModelBase.test.d.ts +2 -0
  52. package/lib/tests/ModelBase.test.d.ts.map +1 -0
  53. package/lib/tests/ModelBase.test.js +73 -0
  54. package/lib/tests/ModelBase.test.js.map +1 -0
  55. package/lib/tests/ModelWithHooks.test.d.ts +2 -0
  56. package/lib/tests/ModelWithHooks.test.d.ts.map +1 -0
  57. package/lib/tests/ModelWithHooks.test.js +108 -0
  58. package/lib/tests/ModelWithHooks.test.js.map +1 -0
  59. package/lib/tests/create-model.test.d.ts +2 -0
  60. package/lib/tests/create-model.test.d.ts.map +1 -0
  61. package/lib/tests/create-model.test.js +104 -0
  62. package/lib/tests/create-model.test.js.map +1 -0
  63. package/lib/tests/utils.test.d.ts +2 -0
  64. package/lib/tests/utils.test.d.ts.map +1 -0
  65. package/lib/tests/utils.test.js +30 -0
  66. package/lib/tests/utils.test.js.map +1 -0
  67. package/package.json +13 -11
  68. package/readme.md +212 -105
  69. package/lib/Model.d.ts +0 -23
  70. package/lib/Model.d.ts.map +0 -1
  71. package/lib/Model.js +0 -108
  72. package/lib/Model.js.map +0 -1
  73. package/lib/hooks/event-hooks.d.ts +0 -6
  74. package/lib/hooks/event-hooks.d.ts.map +0 -1
  75. package/lib/hooks/event-hooks.js +0 -19
  76. package/lib/hooks/event-hooks.js.map +0 -1
  77. package/lib/hooks/model-hooks.d.ts +0 -4
  78. package/lib/hooks/model-hooks.d.ts.map +0 -1
  79. package/lib/hooks/model-hooks.js +0 -9
  80. package/lib/hooks/model-hooks.js.map +0 -1
  81. package/lib/hooks/state-hooks.d.ts +0 -5
  82. package/lib/hooks/state-hooks.d.ts.map +0 -1
  83. package/lib/hooks/state-hooks.js +0 -19
  84. package/lib/hooks/state-hooks.js.map +0 -1
  85. package/lib/types.d.ts +0 -13
  86. package/lib/types.d.ts.map +0 -1
  87. package/lib/types.js.map +0 -1
  88. package/lib/utils/compose-providers.d.ts +0 -3
  89. package/lib/utils/compose-providers.d.ts.map +0 -1
  90. package/lib/utils/compose-providers.js +0 -19
  91. package/lib/utils/compose-providers.js.map +0 -1
  92. package/lib/utils/create-model.d.ts +0 -21
  93. package/lib/utils/create-model.d.ts.map +0 -1
  94. package/lib/utils/create-model.js +0 -84
  95. package/lib/utils/create-model.js.map +0 -1
  96. package/lib/utils/js-event-types.d.ts +0 -6
  97. package/lib/utils/js-event-types.d.ts.map +0 -1
  98. package/lib/utils/js-event-types.js +0 -9
  99. package/lib/utils/js-event-types.js.map +0 -1
package/readme.md CHANGED
@@ -1,125 +1,232 @@
1
- React Better Model
2
- ===
3
- Easy way to share state and events between components and services.
1
+ # React Better Model
4
2
 
5
- Installation:
6
- ===
3
+ Tiny, class-based models for React with a ruthless focus on simplicity: define a class, call `createModel`, and use a handful of hooks. Everything stays TypeScript-friendly and lets you extend behavior through your own model methods and events.
4
+
5
+ - API reference: see [Docs/API.md](Docs/API.md)
6
+ - Example app: Todo demo at https://github.com/ingvardm/react-better-model-todo-example
7
+ - TL;DR: see [Docs/TLDR.md](Docs/TLDR.md)
8
+
9
+ ## Installation
7
10
  ```bash
8
11
  npm i react-better-model
9
12
  ```
10
- or
11
13
  ```bash
12
14
  yarn add react-better-model
13
15
  ```
16
+ ```bash
17
+ pnpm add react-better-model
18
+ ```
14
19
 
15
- Usage
16
- ===
17
- Minimal working example
20
+ ## Minimal example
18
21
  ```ts
19
- import { useCallback } from "react"
20
- import { createModel, Model } from "react-better-model"
22
+ // DiceModel.ts
23
+ import { Model, createModel } from 'react-better-model'
24
+
25
+ class DiceModel extends Model {
26
+ constructor() {
27
+ super({ lastRoll: 1 })
28
+ }
29
+
30
+ roll = () => {
31
+ const next = 1 + Math.floor(Math.random() * 6)
32
+ this.setState({ lastRoll: next })
33
+ }
34
+ }
35
+
36
+ export const {
37
+ Provider: DiceProvider,
38
+ useModel: useDice,
39
+ } = createModel(DiceModel)
40
+ ```
21
41
 
22
- // initial state
23
- const helloWorldModelInitialState = {
24
- count: 0,
42
+ ```tsx
43
+ // RollButton.tsx
44
+ import React from 'react'
45
+ import { useDice } from './DiceModel'
46
+
47
+ export function RollButton() {
48
+ const model = useDice()
49
+
50
+ return (
51
+ <button onClick={model.roll}>
52
+ Roll the die
53
+ </button>
54
+ )
25
55
  }
56
+ ```
26
57
 
27
- // model state type (usefull later)
28
- type HelloWorldModelState = typeof helloWorldModelInitialState
58
+ ```tsx
59
+ // RollResult.tsx
60
+ import React from 'react'
61
+ import { useDice } from './DiceModel'
29
62
 
30
- // model event types (optional, if you want to use events)
31
- type HelloWorldModelEvents = {
32
- click: number
63
+ export function RollResult() {
64
+ const model = useDice()
65
+ const [value] = model.useState('lastRoll')
66
+ return <p>Last roll: {value}</p>
33
67
  }
68
+ ```
69
+
70
+ ```tsx
71
+ // root component
72
+ import React from 'react'
73
+ import { createRoot } from 'react-dom/client'
74
+ import { RollButton } from './RollButton'
75
+ import { RollResult } from './RollResult'
76
+ import { DiceProvider } from './DiceModel'
77
+
78
+ createRoot(document.getElementById('root')!).render(
79
+ <DiceProvider>
80
+ <RollButton />
81
+ <RollResult />
82
+ </DiceProvider>
83
+ )
84
+ ```
34
85
 
35
- // create your model class
36
- class HelloWorldModelClass extends Model<HelloWorldModelState, HelloWorldModelEvents> {
37
- constructor(state = helloWorldModelInitialState){
38
- super(state)
39
- }
86
+ ## State access and updates
87
+ - `useState('key')`: narrow subscription to one top-level key. Returns `[value, setValue]`; `setValue` skips updates when the value is unchanged.
88
+ - `useMapper(state => derived)`: derive values from one or many keys. The mapper is inspected to track which keys it reads; re-runs only when those change.
89
+ - `setState(patch)`: merge-style update for multiple keys; use inside model class methods to express actions.
90
+ - `reduce(draft => patch)`: compute the next state from a copy of the current state in one place.
91
+ - Class methods are your actions: declare methods on the model that call `setState`/`reduce` and call them from components (or events) to keep mutation logic centralized.
92
+
93
+ ```ts
94
+ // inside a component that has access to the model
95
+ const [count, setCount] = model.useState('count') // read + update a single key
96
+
97
+ const doubled = model.useMapper((s) => s.count * 2) // derive from count; runs only when count changes
98
+
99
+ // inside the model class
100
+ increment = () => this.setState({ count: this.state.count + 1 }) // merge update
101
+
102
+ bumpBoth = () => this.setState({ count: this.state.count + 1, bonus: (this.state as any).bonus + 1 }) // multi-key patch
103
+
104
+ boost = () => this.reduce((state) => ({ count: state.count + 10 })) // compute next state from a draft
105
+ ```
106
+
107
+ ## Provider control
108
+ `createModel` gives you `useModelInstance` to spin up a model inside the provider scope and keep a stable reference without extra wiring.
109
+
110
+ ```tsx
111
+ // App.tsx
112
+ import React from 'react'
113
+ import { DiceProvider, useModelInstance } from './DiceModel'
114
+
115
+ function AppBody() {
116
+ // Build the instance once; you can pass initial state if needed.
117
+ const instance = useModelInstance({ lastRoll: 3 })
118
+
119
+ return (
120
+ <DiceProvider value={instance}>
121
+ {/* children that call useDice() */}
122
+ </DiceProvider>
123
+ )
40
124
  }
125
+ ```
126
+ Use this when you want to control the model from the root (e.g., wire logging, external events) while still letting descendants consume it via `useModel`.
127
+
128
+ ## Events
129
+ Declare your events on the model type, dispatch them from class methods, and subscribe/dispatch from components with `useEvent`.
41
130
 
42
- // create object that will contain hooks and refferences
43
- // that you can use in your code
44
- const HWM = createModel(HelloWorldModelClass)
45
-
46
- // component with button that listens to and sets 'count' value
47
- const ButtonAdd = HWM.withModel(({ model, ...props }) => {
48
- const [count, setCount] = model.useState('count')
49
-
50
- const onClick = useCallback(() => {
51
- setCount(count + 1)
52
- }, [count])
53
-
54
- // this is just an example of listening and setting value.
55
- // a better way (without listening) would be:
56
- // const onClick = useCallback(() => {
57
- // model.reduce((state) => {
58
- // return {
59
- // count: state.count + 1
60
- // }
61
- // })
62
- // }, [])
63
-
64
- return <button onClick={onClick}>increment count</button>
65
- })
66
-
67
- // Button that uses an event dispatcher
68
- const ButtonClickEvent = HWM.withModel(({ model, ...props }) => {
69
- const [count] = model.useState('count')
70
-
71
- const dispatchClearEvent = model.useEvent('click')
72
-
73
- const clear = useCallback(() => {
74
- dispatchClearEvent(count)
75
- }, [count])
76
-
77
- return <button onClick={clear}>click to dispatch 'click' event</button>
78
- })
79
-
80
- // label with the current count value
81
- const CountLabel = HWM.withModel(({ model, ...props }) => {
82
- const [count] = model.useState('count')
83
-
84
- return <p>current count is: {count}</p>
85
- })
86
-
87
- // convinience component to handle model events in one place
88
- const EventHandler = HWM.withModel(({ model, ...props }) => {
89
- const handleClickEvent = useCallback((x: HelloWorldModelEvents['click']) => {
90
- alert(`clicked ${x} times`)
91
- }, [])
92
-
93
- model.useEvent('click', handleClickEvent)
94
-
95
- return null
96
- })
97
-
98
- // the widget itself wrapped in HelloWorldModel Provider
99
- // any consumers (HWM.withModel(...)) rendered here
100
- // are inside the same context.
101
- // they can still use other models via useState hook
102
- // created with createModel()
103
- export const HelloWorldWidget = HWM.withProvider(({
104
- model,
105
- ...props
106
- }) => {
107
- return <>
108
- <EventHandler/> {/* Event handling component */}
109
- <CountLabel /> {/* Label that displays updated count */}
110
- <ButtonAdd/> {/* Button that increases the count */}
111
- <ButtonClickEvent/> {/* Button that dispatches 'click' event */}
112
- </>
113
- })
131
+ ```ts
132
+ // ChatModel.ts
133
+ import { Model, createModel } from 'react-better-model'
134
+
135
+ type ChatEvents = { 'message:new': { id: string; text: string } }
136
+
137
+ class ChatModel extends Model<ChatEvents> {
138
+ constructor() {
139
+ super({ messages: [] as { id: string; text: string }[] })
140
+ }
141
+
142
+ addMessage = (text: string) => {
143
+ const msg = { id: '1234', text }
144
+ this.setState({ messages: [...this.state.messages, msg] })
145
+ this.dispatch('message:new', msg)
146
+ }
147
+ }
148
+
149
+ export const { Provider: ChatProvider, useModel: useChat } = createModel(ChatModel)
114
150
  ```
115
151
 
116
- Documentation
117
- ===
118
- [Model creation](model_creation.md)<br>
119
- [Global models](global_model.md)<br>
120
- [State](state.md)<br>
121
- [Events](events.md)<br>
152
+ ```tsx
153
+ // NewMessageForm.tsx (dispatch only)
154
+ import React, { useState } from 'react'
155
+ import { useChat } from './ChatModel'
156
+
157
+ export function NewMessageForm() {
158
+ const chat = useChat()
159
+ const send = chat.useEvent('message:new') // dispatcher only
160
+ const [text, setText] = useState('')
161
+
162
+ return (
163
+ <form onSubmit={(e) => { e.preventDefault(); chat.addMessage(text); send({ id: 'ui', text }); setText('') }}>
164
+ <input value={text} onChange={(e) => setText(e.target.value)} />
165
+ <button type="submit">Send</button>
166
+ </form>
167
+ )
168
+ }
169
+ ```
170
+
171
+ ```tsx
172
+ // MessageLogger.tsx (subscribe + optional dispatch)
173
+ import React, { useEffect } from 'react'
174
+ import { useChat } from './ChatModel'
175
+
176
+ export function MessageLogger() {
177
+ const chat = useChat()
178
+ const emit = chat.useEvent('message:new', (msg) => {
179
+ console.log('New message', msg)
180
+ })
122
181
 
123
- Example
124
- ===
125
- [Example project](https://github.com/ingvardm/react-model-hooks-example)
182
+ // emit can still dispatch if you need to trigger elsewhere:
183
+ useEffect(() => {
184
+ emit({ id: 'system', text: 'logger mounted' })
185
+ }, [emit])
186
+
187
+ return null
188
+ }
189
+ ```
190
+
191
+ ## Global models (optional)
192
+ Some state is truly global (e.g., auth/session). You can instantiate a model once and export it without a Provider.
193
+
194
+ ```ts
195
+ // authModel.ts
196
+ import { Model } from 'react-better-model'
197
+
198
+ class AuthModel extends Model {
199
+ constructor() {
200
+ super({ user: null as null | { id: string; name: string } })
201
+ }
202
+
203
+ setUser = (user: AuthModel['state']['user']) => this.setState({ user })
204
+ }
205
+
206
+ export const authModel = new AuthModel()
207
+ ```
208
+
209
+ ```tsx
210
+ // Nav.tsx
211
+ import React from 'react'
212
+ import { authModel } from './authModel'
213
+
214
+ export function Nav() {
215
+ const [user] = authModel.useState('user')
216
+ return <div>{user ? `Hi, ${user.name}` : 'Sign in'}</div>
217
+ }
218
+ ```
219
+ You still get typed hooks, but you skip a Provider when the model truly lives at app scope.
220
+
221
+ ## Patterns & pitfalls
222
+ - Prefer immutable top-level updates: replace keys instead of mutating nested structures so subscriptions fire. e.g., `setState({ todos: todos.map(...) })`.
223
+ - Keep derived data out of state: compute with `useMapper` or on the fly in model methods.
224
+ - Class methods are your API surface: call them from components instead of mutating state directly.
225
+ - Scope models with Providers: make multiple instances for different parts of the tree instead of sharing global singletons.
226
+ - Event names should be literal and typed (e.g., `'clear-todos'`), and payloads should be minimal.
227
+
228
+ ## Developing this library
229
+ - Install dependancies: `pnpm install`
230
+ - Build: `pnpm run build` runs TypeScript and emits to `lib/`.
231
+ - Tests: `pnpm run test` (ts-jest, jsdom).
232
+ - Docs live in `Docs/` and the root README; keep API changes reflected there.
package/lib/Model.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import { Sub, Subs } from './types';
2
- export declare class Model<S = {}, E = {}> {
3
- state: S;
4
- events: E;
5
- protected initialState: S;
6
- protected subs: Subs<S>;
7
- protected listeners: Subs<E>;
8
- protected updateSingleSubscriber: <K extends keyof S>(k: K, v: S[K]) => void;
9
- protected updateSubscribers: (delta: Partial<S>) => void;
10
- protected updateEventListeners: <K extends keyof E>(k: K, data?: (E[K] extends undefined ? never : E[K]) | undefined) => void;
11
- constructor(state: S, events?: E);
12
- onStateChange: <K extends keyof S>(k: K, cb: Sub<S>) => () => void;
13
- setState: (delta: Partial<S>) => void;
14
- setValue: <K extends keyof S>(key: K, value: S[K]) => void;
15
- reduce: (reducer: (state: S) => Partial<S>) => void;
16
- reset: () => void;
17
- onEvent: <K extends keyof E>(k: K, cb: Sub<E>) => () => void;
18
- dispatch: <K extends keyof E>(key: K, data?: (E[K] extends undefined ? never : E[K]) | undefined) => void;
19
- awaitEvent: <K extends keyof E>(key: K) => Promise<E[K]>;
20
- useState: <K extends keyof S>(key: K) => [this["state"][K], (data: this["state"][K]) => void];
21
- useEvent: <K extends keyof E>(ns: K, cb?: Sub<E, K> | undefined) => (data?: (E[K] extends undefined ? never : E[K]) | undefined) => void;
22
- }
23
- //# sourceMappingURL=Model.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../src/Model.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEnC,qBAAa,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE;IA0BxB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IA1BjB,SAAS,CAAC,YAAY,IAAA;IACtB,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAY;IACnC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAY;IAGxC,SAAS,CAAC,sBAAsB,6CAE/B;IAED,SAAS,CAAC,iBAAiB,UAAW,QAAQ,CAAC,CAAC,UAQ/C;IAED,SAAS,CAAC,oBAAoB,gGAE7B;gBAIO,KAAK,EAAE,CAAC,EACR,MAAM,GAAE,CAAW;IAM3B,aAAa,gCAAiC,IAAI,CAAC,CAAC,gBAanD;IAED,QAAQ,UAAW,QAAQ,CAAC,CAAC,UAO5B;IAED,QAAQ,mDAIP;IAED,MAAM,oBAAqB,CAAC,KAAK,QAAQ,CAAC,CAAC,UAI1C;IAED,KAAK,aAEJ;IAID,OAAO,gCAAiC,IAAI,CAAC,CAAC,gBAa7C;IAED,QAAQ,kGAEP;IAED,UAAU,+CAOT;IAID,QAAQ,sFAEP;IAED,QAAQ,iIAEP;CAED"}
package/lib/Model.js DELETED
@@ -1,108 +0,0 @@
1
- "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.Model = void 0;
15
- var event_hooks_1 = require("./hooks/event-hooks");
16
- var state_hooks_1 = require("./hooks/state-hooks");
17
- var Model = /** @class */ (function () {
18
- //#endregion updaters
19
- function Model(state, events) {
20
- var _this = this;
21
- if (events === void 0) { events = {}; }
22
- this.state = state;
23
- this.events = events;
24
- this.subs = new Map();
25
- this.listeners = new Map();
26
- //#region updaters
27
- this.updateSingleSubscriber = function (k, v) {
28
- var _a;
29
- (_a = _this.subs.get(k)) === null || _a === void 0 ? void 0 : _a.forEach(function (cb) { return cb(v); });
30
- };
31
- this.updateSubscribers = function (delta) {
32
- var keyVals = Object.entries(delta);
33
- for (var _i = 0, _a = keyVals; _i < _a.length; _i++) {
34
- var _b = _a[_i], key = _b[0], val = _b[1];
35
- if (_this.subs.has(key)) {
36
- _this.updateSingleSubscriber(key, val);
37
- }
38
- }
39
- };
40
- this.updateEventListeners = function (k, data) {
41
- var _a;
42
- (_a = _this.listeners.get(k)) === null || _a === void 0 ? void 0 : _a.forEach(function (cb) { return cb(data); });
43
- };
44
- //#region state
45
- this.onStateChange = function (k, cb) {
46
- var keySubs = _this.subs.get(k);
47
- if (!keySubs) {
48
- keySubs = new Set();
49
- _this.subs.set(k, keySubs);
50
- }
51
- keySubs.add(cb);
52
- return function () {
53
- keySubs.delete(cb);
54
- };
55
- };
56
- this.setState = function (delta) {
57
- _this.state = __assign(__assign({}, _this.state), delta);
58
- _this.updateSubscribers(delta);
59
- };
60
- this.setValue = function (key, value) {
61
- _this.state[key] = value;
62
- _this.updateSingleSubscriber(key, value);
63
- };
64
- this.reduce = function (reducer) {
65
- var nextState = reducer(_this.state);
66
- _this.setState(nextState);
67
- };
68
- this.reset = function () {
69
- _this.setState(_this.initialState);
70
- };
71
- //#endregion state
72
- //#region events
73
- this.onEvent = function (k, cb) {
74
- var nsListeners = _this.listeners.get(k);
75
- if (!nsListeners) {
76
- nsListeners = new Set();
77
- _this.listeners.set(k, nsListeners);
78
- }
79
- nsListeners.add(cb);
80
- return function () {
81
- nsListeners.delete(cb);
82
- };
83
- };
84
- this.dispatch = function (key, data) {
85
- _this.updateEventListeners(key, data);
86
- };
87
- this.awaitEvent = function (key) {
88
- return new Promise(function (resolve) {
89
- var remove = _this.onEvent(key, function (data) {
90
- remove();
91
- resolve(data);
92
- });
93
- });
94
- };
95
- //#endregion events
96
- //#region instance hooks
97
- this.useState = function (key) {
98
- return (0, state_hooks_1.useModelInstanceState)(_this, key);
99
- };
100
- this.useEvent = function (ns, cb) {
101
- return (0, event_hooks_1.useModelInstanceEvent)(_this, ns, cb);
102
- };
103
- this.initialState = __assign({}, state);
104
- }
105
- return Model;
106
- }());
107
- exports.Model = Model;
108
- //# sourceMappingURL=Model.js.map
package/lib/Model.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"Model.js","sourceRoot":"","sources":["../src/Model.ts"],"names":[],"mappings":"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"}
@@ -1,6 +0,0 @@
1
- import { Context } from 'react';
2
- import { Model } from '../Model';
3
- import { Sub } from '../types';
4
- export declare function useModelInstanceEvent<K extends keyof M['events'], D extends M['events'], M extends Model<M['state'], M['events']>>(viewModel: M, ns: K, cb?: Sub<D, K>): (data?: (D[K] extends undefined ? never : D[K]) | undefined) => void;
5
- export declare function useModelCtxEvent<K extends keyof M['events'], D extends M['events'], M extends Model<M['state'], M['events']>>(ctx: Context<M>, ns: K, cb?: (eventData: D[K]) => void): (data?: (D[K] extends undefined ? never : D[K]) | undefined) => void;
6
- //# sourceMappingURL=event-hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-hooks.d.ts","sourceRoot":"","sources":["../../src/hooks/event-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAyB,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EACjI,SAAS,EAAE,CAAC,EACZ,EAAE,EAAE,CAAC,EACL,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,wEASd;AAED,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC5H,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EACf,EAAE,EAAE,CAAC,EACL,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,wEAK9B"}
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useModelCtxEvent = exports.useModelInstanceEvent = void 0;
4
- var react_1 = require("react");
5
- function useModelInstanceEvent(viewModel, ns, cb) {
6
- (0, react_1.useEffect)(function () {
7
- if (cb) {
8
- return viewModel.onEvent(ns, cb);
9
- }
10
- }, [cb]);
11
- return function (data) { return viewModel.dispatch(ns, data); };
12
- }
13
- exports.useModelInstanceEvent = useModelInstanceEvent;
14
- function useModelCtxEvent(ctx, ns, cb) {
15
- var viewModel = (0, react_1.useContext)(ctx);
16
- return useModelInstanceEvent(viewModel, ns, cb);
17
- }
18
- exports.useModelCtxEvent = useModelCtxEvent;
19
- //# sourceMappingURL=event-hooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-hooks.js","sourceRoot":"","sources":["../../src/hooks/event-hooks.ts"],"names":[],"mappings":";;;AAAA,+BAAsD;AAKtD,SAAgB,qBAAqB,CACpC,SAAY,EACZ,EAAK,EACL,EAAc;IAEd,IAAA,iBAAS,EAAC;QACT,IAAI,EAAE,EAAE;YACP,OAAO,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,EAAsB,CAAC,CAAA;SACpD;IACF,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAER,OAAO,UAAC,IAA4C,IAAK,OAAA,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAK,CAAC,EAA7B,CAA6B,CAAA;AACvF,CAAC;AAZD,sDAYC;AAED,SAAgB,gBAAgB,CAC/B,GAAe,EACf,EAAK,EACL,EAA8B;IAE9B,IAAM,SAAS,GAAG,IAAA,kBAAU,EAAC,GAAG,CAAC,CAAA;IAEjC,OAAO,qBAAqB,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;AAChD,CAAC;AARD,4CAQC"}
@@ -1,4 +0,0 @@
1
- import { Context } from 'react';
2
- import { Model } from '../Model';
3
- export declare function useModelCtx<M extends Model<M['state'], M['events']>>(ctx: Context<M>): M;
4
- //# sourceMappingURL=model-hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-hooks.d.ts","sourceRoot":"","sources":["../../src/hooks/model-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAc,MAAM,OAAO,CAAA;AAE3C,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAEhC,wBAAgB,WAAW,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EACnE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,KAGf"}
@@ -1,9 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useModelCtx = void 0;
4
- var react_1 = require("react");
5
- function useModelCtx(ctx) {
6
- return (0, react_1.useContext)(ctx);
7
- }
8
- exports.useModelCtx = useModelCtx;
9
- //# sourceMappingURL=model-hooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-hooks.js","sourceRoot":"","sources":["../../src/hooks/model-hooks.ts"],"names":[],"mappings":";;;AAAA,+BAA2C;AAI3C,SAAgB,WAAW,CAC1B,GAAe;IAEf,OAAO,IAAA,kBAAU,EAAC,GAAG,CAAC,CAAA;AACvB,CAAC;AAJD,kCAIC"}
@@ -1,5 +0,0 @@
1
- import { Context } from 'react';
2
- import { Model } from '../Model';
3
- export declare function useModelInstanceState<K extends keyof M['state'], M extends Model<M['state'], M['events']>>(viewModel: M, key: K): [M['state'][K], (data: M['state'][K]) => void];
4
- export declare function useModelCtxState<K extends keyof M['state'], M extends Model<M['state'], M['events']>>(ctx: Context<M>, key: K): [M["state"][K], (data: M["state"][K]) => void];
5
- //# sourceMappingURL=state-hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-hooks.d.ts","sourceRoot":"","sources":["../../src/hooks/state-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,OAAO,EAKP,MAAM,OAAO,CAAA;AAEd,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAGhC,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EACzG,SAAS,EAAE,CAAC,EACZ,GAAG,EAAE,CAAC,GACJ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAUhD;AAED,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EACpG,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EACf,GAAG,EAAE,CAAC,kDAKN"}
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useModelCtxState = exports.useModelInstanceState = void 0;
4
- var react_1 = require("react");
5
- function useModelInstanceState(viewModel, key) {
6
- var _a = (0, react_1.useState)(viewModel.state[key]), value = _a[0], setValue = _a[1];
7
- (0, react_1.useEffect)(function () { return viewModel.onStateChange(key, setValue); }, []);
8
- var setter = (0, react_1.useCallback)(function (v) {
9
- viewModel.setValue(key, v);
10
- }, [key]);
11
- return [value, setter];
12
- }
13
- exports.useModelInstanceState = useModelInstanceState;
14
- function useModelCtxState(ctx, key) {
15
- var viewModel = (0, react_1.useContext)(ctx);
16
- return useModelInstanceState(viewModel, key);
17
- }
18
- exports.useModelCtxState = useModelCtxState;
19
- //# sourceMappingURL=state-hooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-hooks.js","sourceRoot":"","sources":["../../src/hooks/state-hooks.ts"],"names":[],"mappings":";;;AAAA,+BAMc;AAKd,SAAgB,qBAAqB,CACpC,SAAY,EACZ,GAAM;IAEA,IAAA,KAAoB,IAAA,gBAAQ,EAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAjD,KAAK,QAAA,EAAE,QAAQ,QAAkC,CAAA;IAExD,IAAA,iBAAS,EAAC,cAAM,OAAA,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,QAA2B,CAAC,EAAzD,CAAyD,EAAE,EAAE,CAAC,CAAA;IAE9E,IAAM,MAAM,GAAG,IAAA,mBAAW,EAAC,UAAC,CAAC;QAC5B,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAC3B,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IAET,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;AACvB,CAAC;AAbD,sDAaC;AAED,SAAgB,gBAAgB,CAC/B,GAAe,EACf,GAAM;IAEN,IAAM,SAAS,GAAG,IAAA,kBAAU,EAAC,GAAG,CAAC,CAAA;IAEjC,OAAO,qBAAqB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;AAC7C,CAAC;AAPD,4CAOC"}
package/lib/types.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import { ProviderProps } from 'react';
2
- import { Model } from './Model';
3
- export declare type Sub<S, K extends keyof S = keyof S> = (v: S[K]) => void;
4
- export declare type Subs<S> = Map<keyof S, Set<Sub<S>>>;
5
- export declare type CreateModelOptions = {
6
- globalInstance?: Model;
7
- createGlobalInstance?: boolean;
8
- };
9
- export declare type ModelProviderProps<M extends Model> = Omit<ProviderProps<M>, 'value'> & {
10
- value?: M;
11
- initialState?: M['state'];
12
- };
13
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,oBAAY,GAAG,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;AAEnE,oBAAY,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/C,oBAAY,kBAAkB,GAAG;IAChC,cAAc,CAAC,EAAE,KAAK,CAAA;IACtB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC9B,CAAA;AAED,oBAAY,kBAAkB,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG;IACnF,KAAK,CAAC,EAAE,CAAC,CAAA;IACT,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;CACzB,CAAA"}
package/lib/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -1,3 +0,0 @@
1
- import { FC } from 'react';
2
- export declare function composeProviders(providers: FC[]): FC;
3
- //# sourceMappingURL=compose-providers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compose-providers.d.ts","sourceRoot":"","sources":["../../src/utils/compose-providers.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAEjC,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,CAWpD"}
@@ -1,19 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.composeProviders = void 0;
7
- var react_1 = __importDefault(require("react"));
8
- function composeProviders(providers) {
9
- return providers.reduce(function (Parent, Child) { return function (_a) {
10
- var children = _a.children;
11
- return react_1.default.createElement(Parent, null,
12
- react_1.default.createElement(Child, null, children));
13
- }; }, function (_a) {
14
- var children = _a.children;
15
- return react_1.default.createElement(react_1.default.Fragment, null, children);
16
- });
17
- }
18
- exports.composeProviders = composeProviders;
19
- //# sourceMappingURL=compose-providers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compose-providers.js","sourceRoot":"","sources":["../../src/utils/compose-providers.tsx"],"names":[],"mappings":";;;;;;AAAA,gDAAiC;AAEjC,SAAgB,gBAAgB,CAAC,SAAe;IAC/C,OAAO,SAAS,CAAC,MAAM,CACtB,UAAC,MAAU,EAAE,KAAS,IAAK,OAAA,UAAC,EAAY;YAAV,QAAQ,cAAA;QACrC,OAAO,8BAAC,MAAM;YACb,8BAAC,KAAK,QACH,QAAQ,CACH,CACA,CAAA;IACV,CAAC,EAN0B,CAM1B,EACD,UAAC,EAAY;YAAV,QAAQ,cAAA;QAAO,OAAA,8DAAI,QAAQ,CAAK;IAAjB,CAAiB,CACnC,CAAA;AACF,CAAC;AAXD,4CAWC"}