@cratis/arc.react.mvvm 18.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 (238) hide show
  1. package/Bindings.ts +18 -0
  2. package/IHandleParams.ts +14 -0
  3. package/IHandleProps.ts +14 -0
  4. package/IHandleQueryParams.ts +14 -0
  5. package/IViewModelDetached.ts +12 -0
  6. package/MVVMContext.tsx +27 -0
  7. package/MobxOptions.ts +20 -0
  8. package/README.md +65 -0
  9. package/WellKnownBindings.ts +11 -0
  10. package/browser/ILocalStorage.ts +11 -0
  11. package/browser/INavigation.ts +9 -0
  12. package/browser/Navigation.ts +29 -0
  13. package/browser/index.ts +6 -0
  14. package/dialogs/BusyIndicator.ts +24 -0
  15. package/dialogs/DialogMediator.tsx +51 -0
  16. package/dialogs/DialogMediatorHandler.ts +67 -0
  17. package/dialogs/DialogRegistration.ts +25 -0
  18. package/dialogs/Dialogs.ts +58 -0
  19. package/dialogs/IDialogMediatorHandler.ts +40 -0
  20. package/dialogs/IDialogs.ts +37 -0
  21. package/dialogs/index.ts +12 -0
  22. package/dialogs/useDialog.tsx +34 -0
  23. package/dist/cjs/Bindings.d.ts +4 -0
  24. package/dist/cjs/Bindings.d.ts.map +1 -0
  25. package/dist/cjs/Bindings.js +21 -0
  26. package/dist/cjs/Bindings.js.map +1 -0
  27. package/dist/cjs/IHandleParams.d.ts +4 -0
  28. package/dist/cjs/IHandleParams.d.ts.map +1 -0
  29. package/dist/cjs/IHandleProps.d.ts +4 -0
  30. package/dist/cjs/IHandleProps.d.ts.map +1 -0
  31. package/dist/cjs/IHandleQueryParams.d.ts +4 -0
  32. package/dist/cjs/IHandleQueryParams.d.ts.map +1 -0
  33. package/dist/cjs/IViewModelDetached.d.ts +4 -0
  34. package/dist/cjs/IViewModelDetached.d.ts.map +1 -0
  35. package/dist/cjs/MVVMContext.d.ts +9 -0
  36. package/dist/cjs/MVVMContext.d.ts.map +1 -0
  37. package/dist/cjs/MVVMContext.js +21 -0
  38. package/dist/cjs/MVVMContext.js.map +1 -0
  39. package/dist/cjs/MobxOptions.d.ts +12 -0
  40. package/dist/cjs/MobxOptions.d.ts.map +1 -0
  41. package/dist/cjs/WellKnownBindings.d.ts +6 -0
  42. package/dist/cjs/WellKnownBindings.d.ts.map +1 -0
  43. package/dist/cjs/WellKnownBindings.js +10 -0
  44. package/dist/cjs/WellKnownBindings.js.map +1 -0
  45. package/dist/cjs/browser/ILocalStorage.d.ts +9 -0
  46. package/dist/cjs/browser/ILocalStorage.d.ts.map +1 -0
  47. package/dist/cjs/browser/ILocalStorage.js +7 -0
  48. package/dist/cjs/browser/ILocalStorage.js.map +1 -0
  49. package/dist/cjs/browser/INavigation.d.ts +5 -0
  50. package/dist/cjs/browser/INavigation.d.ts.map +1 -0
  51. package/dist/cjs/browser/INavigation.js +7 -0
  52. package/dist/cjs/browser/INavigation.js.map +1 -0
  53. package/dist/cjs/browser/Navigation.d.ts +7 -0
  54. package/dist/cjs/browser/Navigation.d.ts.map +1 -0
  55. package/dist/cjs/browser/Navigation.js +25 -0
  56. package/dist/cjs/browser/Navigation.js.map +1 -0
  57. package/dist/cjs/browser/index.d.ts +4 -0
  58. package/dist/cjs/browser/index.d.ts.map +1 -0
  59. package/dist/cjs/browser/index.js +12 -0
  60. package/dist/cjs/browser/index.js.map +1 -0
  61. package/dist/cjs/dialogs/BusyIndicator.d.ts +7 -0
  62. package/dist/cjs/dialogs/BusyIndicator.d.ts.map +1 -0
  63. package/dist/cjs/dialogs/BusyIndicator.js +16 -0
  64. package/dist/cjs/dialogs/BusyIndicator.js.map +1 -0
  65. package/dist/cjs/dialogs/DialogMediator.d.ts +11 -0
  66. package/dist/cjs/dialogs/DialogMediator.d.ts.map +1 -0
  67. package/dist/cjs/dialogs/DialogMediator.js +17 -0
  68. package/dist/cjs/dialogs/DialogMediator.js.map +1 -0
  69. package/dist/cjs/dialogs/DialogMediatorHandler.d.ts +14 -0
  70. package/dist/cjs/dialogs/DialogMediatorHandler.d.ts.map +1 -0
  71. package/dist/cjs/dialogs/DialogMediatorHandler.js +46 -0
  72. package/dist/cjs/dialogs/DialogMediatorHandler.js.map +1 -0
  73. package/dist/cjs/dialogs/DialogRegistration.d.ts +8 -0
  74. package/dist/cjs/dialogs/DialogRegistration.d.ts.map +1 -0
  75. package/dist/cjs/dialogs/DialogRegistration.js +13 -0
  76. package/dist/cjs/dialogs/DialogRegistration.js.map +1 -0
  77. package/dist/cjs/dialogs/Dialogs.d.ts +13 -0
  78. package/dist/cjs/dialogs/Dialogs.d.ts.map +1 -0
  79. package/dist/cjs/dialogs/Dialogs.js +40 -0
  80. package/dist/cjs/dialogs/Dialogs.js.map +1 -0
  81. package/dist/cjs/dialogs/IDialogMediatorHandler.d.ts +10 -0
  82. package/dist/cjs/dialogs/IDialogMediatorHandler.d.ts.map +1 -0
  83. package/dist/cjs/dialogs/IDialogMediatorHandler.js +7 -0
  84. package/dist/cjs/dialogs/IDialogMediatorHandler.js.map +1 -0
  85. package/dist/cjs/dialogs/IDialogs.d.ts +9 -0
  86. package/dist/cjs/dialogs/IDialogs.d.ts.map +1 -0
  87. package/dist/cjs/dialogs/IDialogs.js +7 -0
  88. package/dist/cjs/dialogs/IDialogs.js.map +1 -0
  89. package/dist/cjs/dialogs/index.d.ts +10 -0
  90. package/dist/cjs/dialogs/index.d.ts.map +1 -0
  91. package/dist/cjs/dialogs/index.js +24 -0
  92. package/dist/cjs/dialogs/index.js.map +1 -0
  93. package/dist/cjs/dialogs/useDialog.d.ts +5 -0
  94. package/dist/cjs/dialogs/useDialog.d.ts.map +1 -0
  95. package/dist/cjs/dialogs/useDialog.js +23 -0
  96. package/dist/cjs/dialogs/useDialog.js.map +1 -0
  97. package/dist/cjs/index.d.ts +14 -0
  98. package/dist/cjs/index.d.ts.map +1 -0
  99. package/dist/cjs/index.js +22 -0
  100. package/dist/cjs/index.js.map +1 -0
  101. package/dist/cjs/messaging/IMessenger.d.ts +8 -0
  102. package/dist/cjs/messaging/IMessenger.d.ts.map +1 -0
  103. package/dist/cjs/messaging/IMessenger.js +7 -0
  104. package/dist/cjs/messaging/IMessenger.js.map +1 -0
  105. package/dist/cjs/messaging/Message.d.ts +7 -0
  106. package/dist/cjs/messaging/Message.d.ts.map +1 -0
  107. package/dist/cjs/messaging/Message.js +13 -0
  108. package/dist/cjs/messaging/Message.js.map +1 -0
  109. package/dist/cjs/messaging/Messenger.d.ts +9 -0
  110. package/dist/cjs/messaging/Messenger.d.ts.map +1 -0
  111. package/dist/cjs/messaging/Messenger.js +20 -0
  112. package/dist/cjs/messaging/Messenger.js.map +1 -0
  113. package/dist/cjs/messaging/index.d.ts +4 -0
  114. package/dist/cjs/messaging/index.d.ts.map +1 -0
  115. package/dist/cjs/messaging/index.js +12 -0
  116. package/dist/cjs/messaging/index.js.map +1 -0
  117. package/dist/cjs/withViewModel.d.ts +8 -0
  118. package/dist/cjs/withViewModel.d.ts.map +1 -0
  119. package/dist/cjs/withViewModel.js +125 -0
  120. package/dist/cjs/withViewModel.js.map +1 -0
  121. package/dist/esm/Bindings.d.ts +4 -0
  122. package/dist/esm/Bindings.d.ts.map +1 -0
  123. package/dist/esm/Bindings.js +19 -0
  124. package/dist/esm/Bindings.js.map +1 -0
  125. package/dist/esm/IHandleParams.d.ts +4 -0
  126. package/dist/esm/IHandleParams.d.ts.map +1 -0
  127. package/dist/esm/IHandleParams.js +2 -0
  128. package/dist/esm/IHandleParams.js.map +1 -0
  129. package/dist/esm/IHandleProps.d.ts +4 -0
  130. package/dist/esm/IHandleProps.d.ts.map +1 -0
  131. package/dist/esm/IHandleProps.js +2 -0
  132. package/dist/esm/IHandleProps.js.map +1 -0
  133. package/dist/esm/IHandleQueryParams.d.ts +4 -0
  134. package/dist/esm/IHandleQueryParams.d.ts.map +1 -0
  135. package/dist/esm/IHandleQueryParams.js +2 -0
  136. package/dist/esm/IHandleQueryParams.js.map +1 -0
  137. package/dist/esm/IViewModelDetached.d.ts +4 -0
  138. package/dist/esm/IViewModelDetached.d.ts.map +1 -0
  139. package/dist/esm/IViewModelDetached.js +2 -0
  140. package/dist/esm/IViewModelDetached.js.map +1 -0
  141. package/dist/esm/MVVMContext.d.ts +9 -0
  142. package/dist/esm/MVVMContext.d.ts.map +1 -0
  143. package/dist/esm/MVVMContext.js +18 -0
  144. package/dist/esm/MVVMContext.js.map +1 -0
  145. package/dist/esm/MobxOptions.d.ts +12 -0
  146. package/dist/esm/MobxOptions.d.ts.map +1 -0
  147. package/dist/esm/MobxOptions.js +2 -0
  148. package/dist/esm/MobxOptions.js.map +1 -0
  149. package/dist/esm/WellKnownBindings.d.ts +6 -0
  150. package/dist/esm/WellKnownBindings.d.ts.map +1 -0
  151. package/dist/esm/WellKnownBindings.js +8 -0
  152. package/dist/esm/WellKnownBindings.js.map +1 -0
  153. package/dist/esm/browser/ILocalStorage.d.ts +9 -0
  154. package/dist/esm/browser/ILocalStorage.d.ts.map +1 -0
  155. package/dist/esm/browser/ILocalStorage.js +5 -0
  156. package/dist/esm/browser/ILocalStorage.js.map +1 -0
  157. package/dist/esm/browser/INavigation.d.ts +5 -0
  158. package/dist/esm/browser/INavigation.d.ts.map +1 -0
  159. package/dist/esm/browser/INavigation.js +5 -0
  160. package/dist/esm/browser/INavigation.js.map +1 -0
  161. package/dist/esm/browser/Navigation.d.ts +7 -0
  162. package/dist/esm/browser/Navigation.d.ts.map +1 -0
  163. package/dist/esm/browser/Navigation.js +23 -0
  164. package/dist/esm/browser/Navigation.js.map +1 -0
  165. package/dist/esm/browser/index.d.ts +4 -0
  166. package/dist/esm/browser/index.d.ts.map +1 -0
  167. package/dist/esm/browser/index.js +4 -0
  168. package/dist/esm/browser/index.js.map +1 -0
  169. package/dist/esm/dialogs/BusyIndicator.d.ts +7 -0
  170. package/dist/esm/dialogs/BusyIndicator.d.ts.map +1 -0
  171. package/dist/esm/dialogs/BusyIndicator.js +14 -0
  172. package/dist/esm/dialogs/BusyIndicator.js.map +1 -0
  173. package/dist/esm/dialogs/DialogMediator.d.ts +11 -0
  174. package/dist/esm/dialogs/DialogMediator.d.ts.map +1 -0
  175. package/dist/esm/dialogs/DialogMediator.js +13 -0
  176. package/dist/esm/dialogs/DialogMediator.js.map +1 -0
  177. package/dist/esm/dialogs/DialogMediatorHandler.d.ts +14 -0
  178. package/dist/esm/dialogs/DialogMediatorHandler.d.ts.map +1 -0
  179. package/dist/esm/dialogs/DialogMediatorHandler.js +44 -0
  180. package/dist/esm/dialogs/DialogMediatorHandler.js.map +1 -0
  181. package/dist/esm/dialogs/DialogRegistration.d.ts +8 -0
  182. package/dist/esm/dialogs/DialogRegistration.d.ts.map +1 -0
  183. package/dist/esm/dialogs/DialogRegistration.js +11 -0
  184. package/dist/esm/dialogs/DialogRegistration.js.map +1 -0
  185. package/dist/esm/dialogs/Dialogs.d.ts +13 -0
  186. package/dist/esm/dialogs/Dialogs.d.ts.map +1 -0
  187. package/dist/esm/dialogs/Dialogs.js +38 -0
  188. package/dist/esm/dialogs/Dialogs.js.map +1 -0
  189. package/dist/esm/dialogs/IDialogMediatorHandler.d.ts +10 -0
  190. package/dist/esm/dialogs/IDialogMediatorHandler.d.ts.map +1 -0
  191. package/dist/esm/dialogs/IDialogMediatorHandler.js +5 -0
  192. package/dist/esm/dialogs/IDialogMediatorHandler.js.map +1 -0
  193. package/dist/esm/dialogs/IDialogs.d.ts +9 -0
  194. package/dist/esm/dialogs/IDialogs.d.ts.map +1 -0
  195. package/dist/esm/dialogs/IDialogs.js +5 -0
  196. package/dist/esm/dialogs/IDialogs.js.map +1 -0
  197. package/dist/esm/dialogs/index.d.ts +10 -0
  198. package/dist/esm/dialogs/index.d.ts.map +1 -0
  199. package/dist/esm/dialogs/index.js +9 -0
  200. package/dist/esm/dialogs/index.js.map +1 -0
  201. package/dist/esm/dialogs/useDialog.d.ts +5 -0
  202. package/dist/esm/dialogs/useDialog.d.ts.map +1 -0
  203. package/dist/esm/dialogs/useDialog.js +21 -0
  204. package/dist/esm/dialogs/useDialog.js.map +1 -0
  205. package/dist/esm/index.d.ts +14 -0
  206. package/dist/esm/index.d.ts.map +1 -0
  207. package/dist/esm/index.js +12 -0
  208. package/dist/esm/index.js.map +1 -0
  209. package/dist/esm/messaging/IMessenger.d.ts +8 -0
  210. package/dist/esm/messaging/IMessenger.d.ts.map +1 -0
  211. package/dist/esm/messaging/IMessenger.js +5 -0
  212. package/dist/esm/messaging/IMessenger.js.map +1 -0
  213. package/dist/esm/messaging/Message.d.ts +7 -0
  214. package/dist/esm/messaging/Message.d.ts.map +1 -0
  215. package/dist/esm/messaging/Message.js +11 -0
  216. package/dist/esm/messaging/Message.js.map +1 -0
  217. package/dist/esm/messaging/Messenger.d.ts +9 -0
  218. package/dist/esm/messaging/Messenger.d.ts.map +1 -0
  219. package/dist/esm/messaging/Messenger.js +18 -0
  220. package/dist/esm/messaging/Messenger.js.map +1 -0
  221. package/dist/esm/messaging/index.d.ts +4 -0
  222. package/dist/esm/messaging/index.d.ts.map +1 -0
  223. package/dist/esm/messaging/index.js +4 -0
  224. package/dist/esm/messaging/index.js.map +1 -0
  225. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  226. package/dist/esm/withViewModel.d.ts +8 -0
  227. package/dist/esm/withViewModel.d.ts.map +1 -0
  228. package/dist/esm/withViewModel.js +123 -0
  229. package/dist/esm/withViewModel.js.map +1 -0
  230. package/global.d.ts +11 -0
  231. package/index.ts +21 -0
  232. package/messaging/IMessenger.ts +28 -0
  233. package/messaging/Message.ts +18 -0
  234. package/messaging/Messenger.ts +26 -0
  235. package/messaging/for_Messenger/when_publishing_message_with_subscriber.ts +27 -0
  236. package/messaging/index.ts +6 -0
  237. package/package.json +71 -0
  238. package/withViewModel.tsx +181 -0
@@ -0,0 +1,123 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { container } from 'tsyringe';
3
+ import { useContext, useState, useRef, useEffect } from 'react';
4
+ import { Observer } from 'mobx-react';
5
+ import { makeAutoObservable } from 'mobx';
6
+ import { useParams, useSearchParams } from 'react-router-dom';
7
+ import { DialogComponentsContext, useDialogContext, DialogContextContent } from '@cratis/arc.react/dialogs';
8
+ import { useDialogMediator, DialogMediator } from './dialogs/DialogMediator.js';
9
+ import { DialogMediatorHandler } from './dialogs/DialogMediatorHandler.js';
10
+ import { Dialogs } from './dialogs/Dialogs.js';
11
+ import { IDialogs } from './dialogs/IDialogs.js';
12
+ import { ArcContext } from '@cratis/arc.react';
13
+ import { WellKnownBindings } from './WellKnownBindings.js';
14
+ import { deepEqual } from '@cratis/arc';
15
+ import { ObservableQueryFor, QueryFor } from '@cratis/arc/queries';
16
+ import { Command } from '@cratis/arc/commands';
17
+
18
+ function disposeViewModel(viewModel) {
19
+ const vmWithDetach = viewModel;
20
+ if (typeof (vmWithDetach.detached) == 'function') {
21
+ vmWithDetach.detached();
22
+ }
23
+ if (viewModel.__childContainer) {
24
+ const container = viewModel.__childContainer;
25
+ container.dispose();
26
+ }
27
+ }
28
+ function handleProps(viewModel, params) {
29
+ const vmWithHandleParams = viewModel;
30
+ if (typeof (vmWithHandleParams.handleProps) == 'function') {
31
+ vmWithHandleParams.handleProps(params);
32
+ }
33
+ }
34
+ function handleParams(viewModel, params) {
35
+ const vmWithHandleParams = viewModel;
36
+ if (typeof (vmWithHandleParams.handleParams) == 'function') {
37
+ vmWithHandleParams.handleParams(params);
38
+ }
39
+ }
40
+ function handleQueryParams(viewModel, queryParams) {
41
+ const vmWithHandleParams = viewModel;
42
+ if (typeof (vmWithHandleParams.handleQueryParams) == 'function') {
43
+ vmWithHandleParams.handleQueryParams(queryParams);
44
+ }
45
+ }
46
+ function withViewModel(viewModelType, targetComponent) {
47
+ const renderComponent = (props) => {
48
+ const applicationContext = useContext(ArcContext);
49
+ const dialogComponentsContext = useContext(DialogComponentsContext);
50
+ const params = useParams();
51
+ const [currentProps, setCurrentProps] = useState(props);
52
+ const [previousParams, setPreviousParams] = useState(params);
53
+ const [queryParams] = useSearchParams();
54
+ const [previousQueryParams, setPreviousQueryParams] = useState(queryParams);
55
+ const queryParamsObject = Object.fromEntries(queryParams.entries());
56
+ const dialogMediatorContext = useRef(null);
57
+ const currentViewModel = useRef(null);
58
+ const [, setInitialRender] = useState(true);
59
+ const parentDialogMediator = useDialogMediator();
60
+ const dialogContext = useDialogContext();
61
+ useEffect(() => {
62
+ if (currentViewModel.current !== null) {
63
+ return () => {
64
+ disposeViewModel(currentViewModel.current);
65
+ };
66
+ }
67
+ dialogMediatorContext.current = new DialogMediatorHandler(parentDialogMediator);
68
+ const child = container.createChildContainer();
69
+ child.registerInstance(WellKnownBindings.props, props);
70
+ child.registerInstance(WellKnownBindings.params, params);
71
+ child.registerInstance(WellKnownBindings.queryParams, queryParamsObject);
72
+ child.registerInstance(DialogContextContent, dialogContext);
73
+ const originalResolve = child.resolve;
74
+ child.resolve = function (type) {
75
+ const instance = originalResolve.apply(child, arguments);
76
+ if (type.prototype instanceof ObservableQueryFor ||
77
+ type.prototype instanceof QueryFor ||
78
+ type.prototype instanceof Command) {
79
+ const query = instance;
80
+ query.setMicroservice(applicationContext.microservice);
81
+ query.setApiBasePath(applicationContext.apiBasePath ?? '');
82
+ query.setOrigin(applicationContext.origin ?? '');
83
+ }
84
+ return instance;
85
+ };
86
+ const dialogService = new Dialogs(dialogMediatorContext.current, dialogComponentsContext);
87
+ child.registerInstance(IDialogs, dialogService);
88
+ const viewModel = child.resolve(viewModelType);
89
+ makeAutoObservable(viewModel);
90
+ viewModel.__childContainer = child;
91
+ currentViewModel.current = viewModel;
92
+ setInitialRender(false);
93
+ handleProps(viewModel, props);
94
+ handleParams(viewModel, params);
95
+ handleQueryParams(viewModel, queryParamsObject);
96
+ return () => {
97
+ if (applicationContext.development === false) {
98
+ disposeViewModel(viewModel);
99
+ }
100
+ };
101
+ }, []);
102
+ if (currentViewModel.current === null)
103
+ return null;
104
+ if (!deepEqual(currentProps, props)) {
105
+ setCurrentProps(props);
106
+ handleProps(currentViewModel.current, props);
107
+ }
108
+ if (!deepEqual(params, previousParams)) {
109
+ setPreviousParams(params);
110
+ handleParams(currentViewModel.current, params);
111
+ }
112
+ if (!deepEqual(queryParams, previousQueryParams)) {
113
+ setPreviousQueryParams(queryParams);
114
+ handleQueryParams(currentViewModel.current, queryParams);
115
+ }
116
+ const component = () => targetComponent({ viewModel: currentViewModel.current, props });
117
+ return (jsx(DialogMediator, { handler: dialogMediatorContext.current, children: jsx(Observer, { children: component }) }));
118
+ };
119
+ return renderComponent;
120
+ }
121
+
122
+ export { withViewModel };
123
+ //# sourceMappingURL=withViewModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withViewModel.js","sources":["../../withViewModel.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { container, DependencyContainer } from 'tsyringe';\nimport { Constructor } from '@cratis/fundamentals';\nimport { FunctionComponent, ReactElement, useContext, useEffect, useRef, useState } from 'react';\nimport { Observer } from 'mobx-react';\nimport { makeAutoObservable } from 'mobx';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport {\n DialogMediator,\n DialogMediatorHandler,\n Dialogs,\n IDialogMediatorHandler,\n IDialogs,\n useDialogMediator\n} from './dialogs';\nimport { IViewModelDetached } from './IViewModelDetached';\nimport { ArcContext } from '@cratis/arc.react';\nimport { WellKnownBindings } from \"./WellKnownBindings\";\nimport { deepEqual } from '@cratis/arc';\nimport { IHandleParams } from 'IHandleParams';\nimport { IHandleQueryParams } from 'IHandleQueryParams';\nimport { IHandleProps } from 'IHandleProps';\nimport { ObservableQueryFor, QueryFor } from '@cratis/arc/queries';\nimport { Command } from '@cratis/arc/commands';\nimport { ICanBeConfigured } from '@cratis/arc/ICanBeConfigured';\nimport { DialogComponentsContext, DialogContextContent, IDialogComponents, useDialogContext } from '@cratis/arc.react/dialogs';\n\ninterface IViewModel extends IViewModelDetached {\n __childContainer: DependencyContainer;\n}\n\nfunction disposeViewModel(viewModel: IViewModel) {\n const vmWithDetach = (viewModel as IViewModelDetached);\n if (typeof (vmWithDetach.detached) == 'function') {\n vmWithDetach.detached();\n }\n\n if (viewModel.__childContainer) {\n const container = viewModel.__childContainer as DependencyContainer;\n container.dispose();\n }\n}\n\nfunction handleProps(viewModel: IViewModel, params: object) {\n const vmWithHandleParams = (viewModel as unknown as IHandleProps);\n if (typeof (vmWithHandleParams.handleProps) == 'function') {\n vmWithHandleParams.handleProps(params);\n }\n}\n\nfunction handleParams(viewModel: IViewModel, params: object) {\n const vmWithHandleParams = (viewModel as unknown as IHandleParams);\n if (typeof (vmWithHandleParams.handleParams) == 'function') {\n vmWithHandleParams.handleParams(params);\n }\n}\n\nfunction handleQueryParams(viewModel: IViewModel, queryParams: object) {\n const vmWithHandleParams = (viewModel as unknown as IHandleQueryParams);\n if (typeof (vmWithHandleParams.handleQueryParams) == 'function') {\n vmWithHandleParams.handleQueryParams(queryParams);\n }\n}\n\n/**\n * Represents the view context that is passed to the view.\n */\nexport interface IViewContext<T, TProps = object> {\n viewModel: T,\n props: TProps,\n}\n\n/**\n * Use a view model with a component.\n * @param {Constructor} viewModelType View model type to use.\n * @param {FunctionComponent} targetComponent The target component to render.\n * @returns \n */\nexport function withViewModel<TViewModel extends object, TProps extends object = object>(\n viewModelType: Constructor<TViewModel>,\n targetComponent: FunctionComponent<IViewContext<TViewModel, TProps>>) {\n\n const renderComponent = (props: TProps) => {\n const applicationContext = useContext(ArcContext);\n const dialogComponentsContext = useContext<IDialogComponents>(DialogComponentsContext);\n const params = useParams();\n const [currentProps, setCurrentProps] = useState(props);\n const [previousParams, setPreviousParams] = useState(params);\n const [queryParams] = useSearchParams();\n const [previousQueryParams, setPreviousQueryParams] = useState(queryParams);\n const queryParamsObject = Object.fromEntries(queryParams.entries());\n const dialogMediatorContext = useRef<IDialogMediatorHandler | null>(null);\n const currentViewModel = useRef<TViewModel | null>(null);\n const [, setInitialRender] = useState(true);\n const parentDialogMediator = useDialogMediator();\n const dialogContext = useDialogContext();\n\n useEffect(() => {\n if (currentViewModel.current !== null) {\n return () => {\n disposeViewModel(currentViewModel.current as IViewModel);\n };\n }\n\n dialogMediatorContext.current = new DialogMediatorHandler(parentDialogMediator);\n\n const child = container.createChildContainer();\n child.registerInstance(WellKnownBindings.props, props);\n child.registerInstance(WellKnownBindings.params, params);\n child.registerInstance(WellKnownBindings.queryParams, queryParamsObject);\n child.registerInstance(DialogContextContent, dialogContext as unknown);\n\n const originalResolve = child.resolve;\n\n child.resolve = function <T>(type: Constructor<T>) {\n // eslint-disable-next-line prefer-rest-params\n const instance = originalResolve.apply(child, arguments as never);\n\n if (type.prototype instanceof ObservableQueryFor ||\n type.prototype instanceof QueryFor ||\n type.prototype instanceof Command) {\n const query = instance as ICanBeConfigured;\n query.setMicroservice(applicationContext.microservice);\n query.setApiBasePath(applicationContext.apiBasePath ?? '');\n query.setOrigin(applicationContext.origin ?? '');\n }\n\n return instance;\n } as never;\n\n const dialogService = new Dialogs(dialogMediatorContext.current!, dialogComponentsContext);\n child.registerInstance<IDialogs>(IDialogs as Constructor<IDialogs>, dialogService);\n const viewModel = child.resolve<TViewModel>(viewModelType) as IViewModel;\n makeAutoObservable(viewModel);\n viewModel.__childContainer = child;\n currentViewModel.current = viewModel as TViewModel;\n\n setInitialRender(false);\n handleProps(viewModel, props);\n handleParams(viewModel, params);\n handleQueryParams(viewModel, queryParamsObject);\n\n return () => {\n if (applicationContext.development === false) {\n disposeViewModel(viewModel);\n }\n };\n }, []);\n\n if (currentViewModel.current === null) return null;\n\n if (!deepEqual(currentProps, props)) {\n setCurrentProps(props);\n handleProps(currentViewModel.current as IViewModel, props);\n }\n\n if (!deepEqual(params, previousParams)) {\n setPreviousParams(params);\n handleParams(currentViewModel.current as IViewModel, params);\n }\n\n if (!deepEqual(queryParams, previousQueryParams)) {\n setPreviousQueryParams(queryParams);\n handleQueryParams(currentViewModel.current as IViewModel, queryParams);\n }\n\n const component = () => targetComponent({ viewModel: currentViewModel.current!, props }) as ReactElement<object, string>;\n\n return (\n <DialogMediator handler={dialogMediatorContext.current!}>\n <Observer>\n {component}\n </Observer>\n </DialogMediator>\n );\n };\n\n return renderComponent;\n}\n"],"names":["_jsx"],"mappings":"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"}
package/global.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import * as React from 'react';
5
+
6
+ declare global {
7
+ namespace JSX {
8
+ interface Element extends React.ReactElement {}
9
+ // Add additional JSX-related overrides if necessary.
10
+ }
11
+ }
package/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import 'reflect-metadata';
5
+ import * as browser from './browser';
6
+ import * as messaging from './messaging';
7
+ import * as dialogs from './dialogs';
8
+ export * from './Bindings';
9
+ export * from './MVVMContext';
10
+ export * from './withViewModel';
11
+ export * from './IViewModelDetached';
12
+ export * from './WellKnownBindings';
13
+ export * from './IHandleProps';
14
+ export * from './IHandleParams';
15
+ export * from './IHandleQueryParams';
16
+
17
+ export {
18
+ browser,
19
+ messaging,
20
+ dialogs
21
+ };
@@ -0,0 +1,28 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Subscription } from 'rxjs';
5
+ import { Constructor } from '@cratis/fundamentals';
6
+
7
+ /**
8
+ * Represents a message handler.
9
+ */
10
+ export type MessageHandler<T> = (message: T) => void;
11
+
12
+ /**
13
+ * Defines a system for publishing and subscribing to messages.
14
+ */
15
+ export abstract class IMessenger {
16
+
17
+ /**
18
+ * Publish a message.
19
+ * @param {*} message Message to publish.
20
+ */
21
+ abstract publish<TMessage extends object>(message: TMessage): void;
22
+
23
+ /**
24
+ * Subscribe to a specific message type.
25
+ * @param {MessageHandler} callback Callback that gets called when message arrives.
26
+ */
27
+ abstract subscribe<TMessage extends object>(type: Constructor<TMessage>, callback: MessageHandler<TMessage>): Subscription;
28
+ }
@@ -0,0 +1,18 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Constructor } from '@cratis/fundamentals';
5
+
6
+ /**
7
+ * Represents a message published on the {@link Messenger}.
8
+ */
9
+ export class Message {
10
+
11
+ /**
12
+ * Initializes a new instance of {@link Message}.
13
+ * @param {Constructor} type Type of the content in the message.
14
+ * @param {*} content The actual content.
15
+ */
16
+ constructor(readonly type: Constructor, readonly content: object) {
17
+ }
18
+ }
@@ -0,0 +1,26 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Constructor } from '@cratis/fundamentals';
5
+ import { IMessenger } from './IMessenger';
6
+ import { filter, Subject, Subscription } from 'rxjs';
7
+ import { Message } from './Message';
8
+
9
+ /**
10
+ * Represents an implementation of {@link IMessenger}.
11
+ */
12
+ export class Messenger extends IMessenger {
13
+ private _messages: Subject<Message> = new Subject<Message>();
14
+
15
+ /** @inheritdoc */
16
+ publish<TMessage extends object>(message: TMessage): void {
17
+ this._messages.next(new Message(message.constructor as Constructor, message));
18
+ }
19
+
20
+ /** @inheritdoc */
21
+ subscribe<TMessage extends object>(type: Constructor<TMessage>, callback: (message: TMessage) => void): Subscription {
22
+ const observable = this._messages.pipe(filter(m => m.type === type));
23
+ const subscription = observable.subscribe(m => callback(m.content as TMessage));
24
+ return subscription;
25
+ }
26
+ }
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Messenger } from '../Messenger';
5
+
6
+ class MessageToSend {
7
+ constructor(readonly something: string) { }
8
+ }
9
+
10
+ describe('when publishing message with subscriber', () => {
11
+ let messenger: Messenger;
12
+ let message: MessageToSend;
13
+ let callbackCalled = false;
14
+
15
+ beforeEach(() => {
16
+ messenger = new Messenger();
17
+ messenger.subscribe(MessageToSend, (m: MessageToSend) => {
18
+ callbackCalled = true;
19
+ message = m;
20
+ });
21
+ messenger.publish(new MessageToSend('forty two'));
22
+ });
23
+
24
+ it('should call the callback', () => callbackCalled.should.be.true);
25
+ it('should pass the message to the callback', () => message.should.not.be.undefined);
26
+ it('should pass the correct content to the callback', () => message.something.should.equal('forty two'));
27
+ });
@@ -0,0 +1,6 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ export * from './Message';
5
+ export * from './IMessenger';
6
+ export * from './Messenger';
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@cratis/arc.react.mvvm",
3
+ "version": "18.0.0",
4
+ "description": "",
5
+ "author": "Cratis",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cratis/arc.git"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "**/*.ts",
17
+ "**/*.tsx"
18
+ ],
19
+ "type": "module",
20
+ "main": "dist/cjs/index.js",
21
+ "module": "dist/esm/index.js",
22
+ "types": "dist/esm/index.d.ts",
23
+ "exports": {
24
+ "./package.json": "./package.json",
25
+ ".": {
26
+ "types": "./dist/esm/index.d.ts",
27
+ "require": "./dist/cjs/index.js",
28
+ "import": "./dist/esm/index.js"
29
+ },
30
+ "./browser": {
31
+ "types": "./dist/esm/browser/index.d.ts",
32
+ "import": "./dist/esm/browser/index.js",
33
+ "require": "./dist/cjs/browser/index.js"
34
+ },
35
+ "./messaging": {
36
+ "types": "./dist/esm/messaging/index.d.ts",
37
+ "import": "./dist/esm/messaging/index.js",
38
+ "require": "./dist/cjs/messaging/index.js"
39
+ },
40
+ "./dialogs": {
41
+ "types": "./dist/esm/dialogs/index.d.ts",
42
+ "require": "./dist/cjs/dialogs/index.js",
43
+ "import": "./dist/esm/dialogs/index.js"
44
+ }
45
+ },
46
+ "scripts": {
47
+ "prepare": "yarn g:build",
48
+ "clean": "yarn g:clean",
49
+ "build": "yarn g:build",
50
+ "lint": "yarn g:lint",
51
+ "lint:ci": "yarn g:lint:ci",
52
+ "test": "yarn g:test",
53
+ "ci": "yarn g:ci",
54
+ "up": "yarn g:up"
55
+ },
56
+ "dependencies": {
57
+ "@cratis/arc": "18.0.0",
58
+ "@cratis/arc.react": "18.0.0",
59
+ "@cratis/fundamentals": "^7.2.3",
60
+ "mobx": "^6.15.0",
61
+ "mobx-react": "^9.2.1",
62
+ "reflect-metadata": "^0.2.2",
63
+ "rxjs": "^7.8.2",
64
+ "tsyringe": "^4.10.0"
65
+ },
66
+ "peerDependencies": {
67
+ "react": "^18.0.0 || ^19.0.0",
68
+ "react-dom": "^18.0.0 || ^19.0.0",
69
+ "react-router-dom": "^7.0.0"
70
+ }
71
+ }
@@ -0,0 +1,181 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { container, DependencyContainer } from 'tsyringe';
5
+ import { Constructor } from '@cratis/fundamentals';
6
+ import { FunctionComponent, ReactElement, useContext, useEffect, useRef, useState } from 'react';
7
+ import { Observer } from 'mobx-react';
8
+ import { makeAutoObservable } from 'mobx';
9
+ import { useParams, useSearchParams } from 'react-router-dom';
10
+ import {
11
+ DialogMediator,
12
+ DialogMediatorHandler,
13
+ Dialogs,
14
+ IDialogMediatorHandler,
15
+ IDialogs,
16
+ useDialogMediator
17
+ } from './dialogs';
18
+ import { IViewModelDetached } from './IViewModelDetached';
19
+ import { ArcContext } from '@cratis/arc.react';
20
+ import { WellKnownBindings } from "./WellKnownBindings";
21
+ import { deepEqual } from '@cratis/arc';
22
+ import { IHandleParams } from 'IHandleParams';
23
+ import { IHandleQueryParams } from 'IHandleQueryParams';
24
+ import { IHandleProps } from 'IHandleProps';
25
+ import { ObservableQueryFor, QueryFor } from '@cratis/arc/queries';
26
+ import { Command } from '@cratis/arc/commands';
27
+ import { ICanBeConfigured } from '@cratis/arc/ICanBeConfigured';
28
+ import { DialogComponentsContext, DialogContextContent, IDialogComponents, useDialogContext } from '@cratis/arc.react/dialogs';
29
+
30
+ interface IViewModel extends IViewModelDetached {
31
+ __childContainer: DependencyContainer;
32
+ }
33
+
34
+ function disposeViewModel(viewModel: IViewModel) {
35
+ const vmWithDetach = (viewModel as IViewModelDetached);
36
+ if (typeof (vmWithDetach.detached) == 'function') {
37
+ vmWithDetach.detached();
38
+ }
39
+
40
+ if (viewModel.__childContainer) {
41
+ const container = viewModel.__childContainer as DependencyContainer;
42
+ container.dispose();
43
+ }
44
+ }
45
+
46
+ function handleProps(viewModel: IViewModel, params: object) {
47
+ const vmWithHandleParams = (viewModel as unknown as IHandleProps);
48
+ if (typeof (vmWithHandleParams.handleProps) == 'function') {
49
+ vmWithHandleParams.handleProps(params);
50
+ }
51
+ }
52
+
53
+ function handleParams(viewModel: IViewModel, params: object) {
54
+ const vmWithHandleParams = (viewModel as unknown as IHandleParams);
55
+ if (typeof (vmWithHandleParams.handleParams) == 'function') {
56
+ vmWithHandleParams.handleParams(params);
57
+ }
58
+ }
59
+
60
+ function handleQueryParams(viewModel: IViewModel, queryParams: object) {
61
+ const vmWithHandleParams = (viewModel as unknown as IHandleQueryParams);
62
+ if (typeof (vmWithHandleParams.handleQueryParams) == 'function') {
63
+ vmWithHandleParams.handleQueryParams(queryParams);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Represents the view context that is passed to the view.
69
+ */
70
+ export interface IViewContext<T, TProps = object> {
71
+ viewModel: T,
72
+ props: TProps,
73
+ }
74
+
75
+ /**
76
+ * Use a view model with a component.
77
+ * @param {Constructor} viewModelType View model type to use.
78
+ * @param {FunctionComponent} targetComponent The target component to render.
79
+ * @returns
80
+ */
81
+ export function withViewModel<TViewModel extends object, TProps extends object = object>(
82
+ viewModelType: Constructor<TViewModel>,
83
+ targetComponent: FunctionComponent<IViewContext<TViewModel, TProps>>) {
84
+
85
+ const renderComponent = (props: TProps) => {
86
+ const applicationContext = useContext(ArcContext);
87
+ const dialogComponentsContext = useContext<IDialogComponents>(DialogComponentsContext);
88
+ const params = useParams();
89
+ const [currentProps, setCurrentProps] = useState(props);
90
+ const [previousParams, setPreviousParams] = useState(params);
91
+ const [queryParams] = useSearchParams();
92
+ const [previousQueryParams, setPreviousQueryParams] = useState(queryParams);
93
+ const queryParamsObject = Object.fromEntries(queryParams.entries());
94
+ const dialogMediatorContext = useRef<IDialogMediatorHandler | null>(null);
95
+ const currentViewModel = useRef<TViewModel | null>(null);
96
+ const [, setInitialRender] = useState(true);
97
+ const parentDialogMediator = useDialogMediator();
98
+ const dialogContext = useDialogContext();
99
+
100
+ useEffect(() => {
101
+ if (currentViewModel.current !== null) {
102
+ return () => {
103
+ disposeViewModel(currentViewModel.current as IViewModel);
104
+ };
105
+ }
106
+
107
+ dialogMediatorContext.current = new DialogMediatorHandler(parentDialogMediator);
108
+
109
+ const child = container.createChildContainer();
110
+ child.registerInstance(WellKnownBindings.props, props);
111
+ child.registerInstance(WellKnownBindings.params, params);
112
+ child.registerInstance(WellKnownBindings.queryParams, queryParamsObject);
113
+ child.registerInstance(DialogContextContent, dialogContext as unknown);
114
+
115
+ const originalResolve = child.resolve;
116
+
117
+ child.resolve = function <T>(type: Constructor<T>) {
118
+ // eslint-disable-next-line prefer-rest-params
119
+ const instance = originalResolve.apply(child, arguments as never);
120
+
121
+ if (type.prototype instanceof ObservableQueryFor ||
122
+ type.prototype instanceof QueryFor ||
123
+ type.prototype instanceof Command) {
124
+ const query = instance as ICanBeConfigured;
125
+ query.setMicroservice(applicationContext.microservice);
126
+ query.setApiBasePath(applicationContext.apiBasePath ?? '');
127
+ query.setOrigin(applicationContext.origin ?? '');
128
+ }
129
+
130
+ return instance;
131
+ } as never;
132
+
133
+ const dialogService = new Dialogs(dialogMediatorContext.current!, dialogComponentsContext);
134
+ child.registerInstance<IDialogs>(IDialogs as Constructor<IDialogs>, dialogService);
135
+ const viewModel = child.resolve<TViewModel>(viewModelType) as IViewModel;
136
+ makeAutoObservable(viewModel);
137
+ viewModel.__childContainer = child;
138
+ currentViewModel.current = viewModel as TViewModel;
139
+
140
+ setInitialRender(false);
141
+ handleProps(viewModel, props);
142
+ handleParams(viewModel, params);
143
+ handleQueryParams(viewModel, queryParamsObject);
144
+
145
+ return () => {
146
+ if (applicationContext.development === false) {
147
+ disposeViewModel(viewModel);
148
+ }
149
+ };
150
+ }, []);
151
+
152
+ if (currentViewModel.current === null) return null;
153
+
154
+ if (!deepEqual(currentProps, props)) {
155
+ setCurrentProps(props);
156
+ handleProps(currentViewModel.current as IViewModel, props);
157
+ }
158
+
159
+ if (!deepEqual(params, previousParams)) {
160
+ setPreviousParams(params);
161
+ handleParams(currentViewModel.current as IViewModel, params);
162
+ }
163
+
164
+ if (!deepEqual(queryParams, previousQueryParams)) {
165
+ setPreviousQueryParams(queryParams);
166
+ handleQueryParams(currentViewModel.current as IViewModel, queryParams);
167
+ }
168
+
169
+ const component = () => targetComponent({ viewModel: currentViewModel.current!, props }) as ReactElement<object, string>;
170
+
171
+ return (
172
+ <DialogMediator handler={dialogMediatorContext.current!}>
173
+ <Observer>
174
+ {component}
175
+ </Observer>
176
+ </DialogMediator>
177
+ );
178
+ };
179
+
180
+ return renderComponent;
181
+ }