@gogocat/data-bind 1.11.0 → 2.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 (274) hide show
  1. package/.editorconfig +14 -14
  2. package/.vscode/launch.json +12 -12
  3. package/CONFIGURATION.md +294 -0
  4. package/REACTIVE_MODE.md +553 -0
  5. package/README.md +266 -829
  6. package/babel.config.json +30 -0
  7. package/dist/js/_escape.d.ts +14 -0
  8. package/dist/js/_escape.d.ts.map +1 -0
  9. package/dist/js/applyBinding.d.ts +11 -0
  10. package/dist/js/applyBinding.d.ts.map +1 -0
  11. package/dist/js/attrBinding.d.ts +12 -0
  12. package/dist/js/attrBinding.d.ts.map +1 -0
  13. package/dist/js/binder.d.ts +67 -0
  14. package/dist/js/binder.d.ts.map +1 -0
  15. package/dist/js/changeBinding.d.ts +19 -0
  16. package/dist/js/changeBinding.d.ts.map +1 -0
  17. package/dist/js/commentWrapper.d.ts +39 -0
  18. package/dist/js/commentWrapper.d.ts.map +1 -0
  19. package/dist/js/config.d.ts +55 -0
  20. package/dist/js/config.d.ts.map +1 -0
  21. package/dist/js/createBindingOption.d.ts +32 -0
  22. package/dist/js/createBindingOption.d.ts.map +1 -0
  23. package/dist/js/createEventBinding.d.ts +10 -0
  24. package/dist/js/createEventBinding.d.ts.map +1 -0
  25. package/dist/js/cssBinding.d.ts +15 -0
  26. package/dist/js/cssBinding.d.ts.map +1 -0
  27. package/dist/js/dataBind.js +2772 -2519
  28. package/dist/js/dataBind.min.js +8 -1
  29. package/dist/js/dataBind.min.js.map +1 -1
  30. package/dist/js/domWalker.d.ts +9 -0
  31. package/dist/js/domWalker.d.ts.map +1 -0
  32. package/dist/js/forOfBinding.d.ts +12 -0
  33. package/dist/js/forOfBinding.d.ts.map +1 -0
  34. package/dist/js/hoverBinding.d.ts +13 -0
  35. package/dist/js/hoverBinding.d.ts.map +1 -0
  36. package/dist/js/ifBinding.d.ts +12 -0
  37. package/dist/js/ifBinding.d.ts.map +1 -0
  38. package/dist/js/index.d.ts +10 -0
  39. package/dist/js/index.d.ts.map +1 -0
  40. package/dist/js/modelBinding.d.ts +12 -0
  41. package/dist/js/modelBinding.d.ts.map +1 -0
  42. package/dist/js/postProcess.d.ts +3 -0
  43. package/dist/js/postProcess.d.ts.map +1 -0
  44. package/dist/js/pubSub.d.ts +11 -0
  45. package/dist/js/pubSub.d.ts.map +1 -0
  46. package/dist/js/reactiveProxy.d.ts +28 -0
  47. package/dist/js/reactiveProxy.d.ts.map +1 -0
  48. package/dist/js/renderForOfBinding.d.ts +8 -0
  49. package/dist/js/renderForOfBinding.d.ts.map +1 -0
  50. package/dist/js/renderIfBinding.d.ts +22 -0
  51. package/dist/js/renderIfBinding.d.ts.map +1 -0
  52. package/dist/js/renderIteration.d.ts +16 -0
  53. package/dist/js/renderIteration.d.ts.map +1 -0
  54. package/dist/js/renderTemplate.d.ts +14 -0
  55. package/dist/js/renderTemplate.d.ts.map +1 -0
  56. package/dist/js/renderTemplatesBinding.d.ts +19 -0
  57. package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
  58. package/dist/js/showBinding.d.ts +13 -0
  59. package/dist/js/showBinding.d.ts.map +1 -0
  60. package/dist/js/switchBinding.d.ts +13 -0
  61. package/dist/js/switchBinding.d.ts.map +1 -0
  62. package/dist/js/textBinding.d.ts +13 -0
  63. package/dist/js/textBinding.d.ts.map +1 -0
  64. package/dist/js/types/_escape.d.ts +14 -0
  65. package/dist/js/types/_escape.d.ts.map +1 -0
  66. package/dist/js/types/applyBinding.d.ts +11 -0
  67. package/dist/js/types/applyBinding.d.ts.map +1 -0
  68. package/dist/js/types/attrBinding.d.ts +12 -0
  69. package/dist/js/types/attrBinding.d.ts.map +1 -0
  70. package/dist/js/types/binder.d.ts +67 -0
  71. package/dist/js/types/binder.d.ts.map +1 -0
  72. package/dist/js/types/changeBinding.d.ts +19 -0
  73. package/dist/js/types/changeBinding.d.ts.map +1 -0
  74. package/dist/js/types/commentWrapper.d.ts +39 -0
  75. package/dist/js/types/commentWrapper.d.ts.map +1 -0
  76. package/dist/js/types/config.d.ts +55 -0
  77. package/dist/js/types/config.d.ts.map +1 -0
  78. package/dist/js/types/createBindingOption.d.ts +32 -0
  79. package/dist/js/types/createBindingOption.d.ts.map +1 -0
  80. package/dist/js/types/createEventBinding.d.ts +10 -0
  81. package/dist/js/types/createEventBinding.d.ts.map +1 -0
  82. package/dist/js/types/cssBinding.d.ts +15 -0
  83. package/dist/js/types/cssBinding.d.ts.map +1 -0
  84. package/dist/js/types/domWalker.d.ts +9 -0
  85. package/dist/js/types/domWalker.d.ts.map +1 -0
  86. package/dist/js/types/forOfBinding.d.ts +12 -0
  87. package/dist/js/types/forOfBinding.d.ts.map +1 -0
  88. package/dist/js/types/hoverBinding.d.ts +13 -0
  89. package/dist/js/types/hoverBinding.d.ts.map +1 -0
  90. package/dist/js/types/ifBinding.d.ts +12 -0
  91. package/dist/js/types/ifBinding.d.ts.map +1 -0
  92. package/dist/js/types/index.d.ts +10 -0
  93. package/dist/js/types/index.d.ts.map +1 -0
  94. package/dist/js/types/modelBinding.d.ts +12 -0
  95. package/dist/js/types/modelBinding.d.ts.map +1 -0
  96. package/dist/js/types/postProcess.d.ts +3 -0
  97. package/dist/js/types/postProcess.d.ts.map +1 -0
  98. package/dist/js/types/pubSub.d.ts +11 -0
  99. package/dist/js/types/pubSub.d.ts.map +1 -0
  100. package/dist/js/types/reactiveProxy.d.ts +28 -0
  101. package/dist/js/types/reactiveProxy.d.ts.map +1 -0
  102. package/dist/js/types/renderForOfBinding.d.ts +8 -0
  103. package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
  104. package/dist/js/types/renderIfBinding.d.ts +22 -0
  105. package/dist/js/types/renderIfBinding.d.ts.map +1 -0
  106. package/dist/js/types/renderIteration.d.ts +16 -0
  107. package/dist/js/types/renderIteration.d.ts.map +1 -0
  108. package/dist/js/types/renderTemplate.d.ts +14 -0
  109. package/dist/js/types/renderTemplate.d.ts.map +1 -0
  110. package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
  111. package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
  112. package/dist/js/types/showBinding.d.ts +13 -0
  113. package/dist/js/types/showBinding.d.ts.map +1 -0
  114. package/dist/js/types/switchBinding.d.ts +13 -0
  115. package/dist/js/types/switchBinding.d.ts.map +1 -0
  116. package/dist/js/types/textBinding.d.ts +13 -0
  117. package/dist/js/types/textBinding.d.ts.map +1 -0
  118. package/dist/js/types/types.d.ts +111 -0
  119. package/dist/js/types/types.d.ts.map +1 -0
  120. package/dist/js/types/util.d.ts +119 -0
  121. package/dist/js/types/util.d.ts.map +1 -0
  122. package/dist/js/types.d.ts +111 -0
  123. package/dist/js/types.d.ts.map +1 -0
  124. package/dist/js/util.d.ts +119 -0
  125. package/dist/js/util.d.ts.map +1 -0
  126. package/eslint.config.js +124 -0
  127. package/examples/DBMONSTER_COMPARISON.md +123 -0
  128. package/examples/afterRenderDemo.html +119 -0
  129. package/examples/bootstrap/css/animate.css +1579 -1579
  130. package/examples/bootstrap/css/bootstrap.min.css +6 -6
  131. package/examples/bootstrap/css/homeservices.css +378 -390
  132. package/examples/bootstrap/css/open-iconic.css +511 -511
  133. package/examples/bootstrap/fonts/open-iconic.svg +543 -543
  134. package/examples/bootstrap/js/compMessageDialog.js +20 -19
  135. package/examples/bootstrap/js/compSearchBar.js +12 -19
  136. package/examples/bootstrap/js/compSearchResults.js +50 -46
  137. package/examples/bootstrap/js/featureAdsResult.json +65 -65
  138. package/examples/bootstrap/js/searchResult.json +57 -57
  139. package/examples/bootstrap.html +343 -332
  140. package/examples/css/baseTodo.css +141 -141
  141. package/examples/css/dbMonsterStyles.css +27 -27
  142. package/examples/css/indexTodo.css +374 -374
  143. package/examples/dbmonsterForOfReactive.html +40 -0
  144. package/examples/dbmonsterReact.html +19 -0
  145. package/examples/forOfBindingSimpleDebug.html +45 -0
  146. package/examples/form.html +20 -4
  147. package/examples/globalConfig.html +131 -0
  148. package/examples/js/afterRenderDemo.js +190 -0
  149. package/examples/js/appTodo.js +46 -46
  150. package/examples/js/attrBindingDemo.js +2 -2
  151. package/examples/js/dbMonApp.js +24 -26
  152. package/examples/js/dbMonAppReact.jsx +79 -0
  153. package/examples/js/dbMonAppReactive.js +28 -0
  154. package/examples/js/fiberDemo.js +4 -4
  155. package/examples/js/filtersDemo.js +8 -8
  156. package/examples/js/forOfDemo.js +7 -9
  157. package/examples/js/forOfDemoComplex.js +44 -17
  158. package/examples/js/form.js +44 -12
  159. package/examples/js/globalConfig.js +117 -0
  160. package/examples/js/ifBindingDemo.js +16 -16
  161. package/examples/js/reactiveDemo.js +119 -0
  162. package/examples/js/switchBindingDemo.js +8 -8
  163. package/examples/react-dbmonster/dist/bundle.js +43 -0
  164. package/examples/react-dbmonster/package-lock.json +537 -0
  165. package/examples/react-dbmonster/package.json +16 -0
  166. package/examples/react-dbmonster/src/index.jsx +80 -0
  167. package/examples/reactiveDemo.html +127 -0
  168. package/examples/refreshRateTest.html +75 -75
  169. package/index.html +841 -0
  170. package/package.json +31 -34
  171. package/rollup.config.js +79 -36
  172. package/src/{_escape.js → _escape.ts} +19 -17
  173. package/src/applyBinding.ts +179 -0
  174. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  175. package/src/binder.ts +289 -0
  176. package/src/changeBinding.ts +93 -0
  177. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  178. package/src/config.ts +107 -0
  179. package/src/createBindingOption.ts +91 -0
  180. package/src/createEventBinding.ts +88 -0
  181. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  182. package/src/{domWalker.js → domWalker.ts} +44 -30
  183. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  184. package/src/hoverBinding.ts +84 -0
  185. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  186. package/src/index.ts +53 -0
  187. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  188. package/src/postProcess.ts +22 -0
  189. package/src/{pubSub.js → pubSub.ts} +24 -15
  190. package/src/reactiveProxy.ts +285 -0
  191. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +55 -33
  192. package/src/{renderIfBinding.js → renderIfBinding.ts} +45 -20
  193. package/src/renderIteration.ts +53 -0
  194. package/src/renderTemplate.ts +165 -0
  195. package/src/renderTemplatesBinding.ts +73 -0
  196. package/src/{showBinding.js → showBinding.ts} +4 -3
  197. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  198. package/src/{textBinding.js → textBinding.ts} +5 -4
  199. package/src/types.ts +124 -0
  200. package/src/util.ts +810 -0
  201. package/test/css/reporter.css +9 -9
  202. package/test/fixtures/dataBindBootstrap.html +2 -2
  203. package/test/fixtures/formBindings.html +9 -1
  204. package/test/globals.d.ts +19 -0
  205. package/test/helpers/testHelper.js +46 -11
  206. package/test/mocks/featureAdsResult.json +65 -65
  207. package/test/mocks/searchResult.json +57 -57
  208. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  209. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  210. package/test/specs/blurBinding.spec.ts +60 -0
  211. package/test/specs/chainableUse.spec.ts +125 -0
  212. package/test/specs/clickBinding.spec.ts +194 -0
  213. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  214. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  215. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  216. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  217. package/test/specs/formBinding.spec.ts +272 -0
  218. package/test/specs/ifBinding.spec.ts +165 -0
  219. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  220. package/test/specs/reactiveProxy.spec.ts +465 -0
  221. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  222. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  223. package/test/specs/templateBinding.spec.ts +273 -0
  224. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  225. package/test/tsconfig.json +31 -0
  226. package/test-output.txt +200 -0
  227. package/test-reactive.html +224 -0
  228. package/tsconfig.json +28 -0
  229. package/vendors/lodash.custom.js +4577 -4577
  230. package/vendors/lodash.custom.min.js +45 -45
  231. package/vitest.config.js +27 -0
  232. package/.eslintrc.js +0 -1
  233. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  234. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  235. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  236. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  237. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  238. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  239. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  240. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  241. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  242. package/coverage/coverage.json +0 -1
  243. package/coverage/lcov/lcov-report/base.css +0 -213
  244. package/coverage/lcov/lcov-report/index.html +0 -93
  245. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  246. package/coverage/lcov/lcov-report/js/index.html +0 -93
  247. package/coverage/lcov/lcov-report/prettify.css +0 -1
  248. package/coverage/lcov/lcov-report/prettify.js +0 -1
  249. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  250. package/coverage/lcov/lcov-report/sorter.js +0 -158
  251. package/coverage/lcov/lcov.info +0 -1991
  252. package/eslintrc.json +0 -40
  253. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  254. package/examples/bootstrap/js/popper.min.js +0 -5
  255. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  256. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  257. package/gruntfile.js +0 -92
  258. package/gulpfile.js +0 -32
  259. package/src/binder.js +0 -422
  260. package/src/changeBinding.js +0 -57
  261. package/src/config.js +0 -65
  262. package/src/createBindingOption.js +0 -66
  263. package/src/createEventBinding.js +0 -46
  264. package/src/eventSystem.js +0 -46
  265. package/src/hoverBinding.js +0 -57
  266. package/src/index.js +0 -26
  267. package/src/renderTemplate.js +0 -128
  268. package/src/util.js +0 -648
  269. package/test/specs/blurBinding.spec.js +0 -57
  270. package/test/specs/formBinding.spec.js +0 -292
  271. package/test/specs/ifBinding.spec.js +0 -169
  272. package/test/specs/templateBinding.spec.js +0 -117
  273. package/vendors/jasmine-jquery.js +0 -841
  274. package/vendors/jquery-3.2.1.min.js +0 -4
@@ -1,4 +1,5 @@
1
1
  import {getViewModelValue} from './util';
2
+ import type {BindingCache, ViewModel, BindingAttrs} from './types';
2
3
 
3
4
  /**
4
5
  * modelBinding
@@ -8,23 +9,23 @@ import {getViewModelValue} from './util';
8
9
  * @param {object} bindingAttrs
9
10
  * @param {boolean} forceRender
10
11
  */
11
- const modelBinding = (cache, viewModel, bindingAttrs, forceRender) => {
12
+ const modelBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, forceRender: boolean): void => {
12
13
  const dataKey = cache.dataKey;
13
- let newValue = '';
14
- const APP = viewModel.APP || viewModel.$root.APP;
14
+ let newValue: unknown = '';
15
+ const APP = viewModel.APP || viewModel.$root?.APP;
15
16
 
16
- if (!dataKey || (!forceRender && !APP.$rootElement.contains(cache.el))) {
17
+ if (!dataKey || (!forceRender && !(APP?.$rootElement as HTMLElement)?.contains(cache.el))) {
17
18
  return;
18
19
  }
19
20
 
20
21
  newValue = getViewModelValue(viewModel, dataKey);
21
22
 
22
23
  if (typeof newValue !== 'undefined' && newValue !== null) {
23
- const $element = cache.el;
24
+ const $element = cache.el as HTMLInputElement;
24
25
  const isCheckbox = $element.type === 'checkbox';
25
26
  const isRadio = $element.type === 'radio';
26
27
  const inputName = $element.name;
27
- const $radioGroup = isRadio ? APP.$rootElement.querySelectorAll(`input[name="${inputName}"]`) : [];
28
+ const $radioGroup = isRadio ? (APP?.$rootElement as HTMLElement).querySelectorAll(`input[name="${inputName}"]`) : [];
28
29
  const oldValue = isCheckbox ? $element.checked : $element.value;
29
30
 
30
31
  // update element value
@@ -36,13 +37,14 @@ const modelBinding = (cache, viewModel, bindingAttrs, forceRender) => {
36
37
  const radioGroupLength = $radioGroup.length;
37
38
 
38
39
  for (i = 0; i < radioGroupLength; i += 1) {
39
- if ($radioGroup[i].value === newValue) {
40
- $radioGroup[i].checked = true;
40
+ const radioInput = $radioGroup[i] as HTMLInputElement;
41
+ if (radioInput.value === newValue) {
42
+ radioInput.checked = true;
41
43
  break;
42
44
  }
43
45
  }
44
46
  } else {
45
- $element.value = newValue;
47
+ $element.value = String(newValue);
46
48
  }
47
49
  }
48
50
  }
@@ -0,0 +1,22 @@
1
+ import {
2
+ each,
3
+ throwErrorMessage,
4
+ } from './util';
5
+
6
+ const postProcess = (tasks: Function[]): void => {
7
+ if (!tasks || !tasks.length) {
8
+ return;
9
+ }
10
+
11
+ each(tasks, (index: number, task: Function) => {
12
+ if (typeof task === 'function') {
13
+ try {
14
+ task();
15
+ } catch (err) {
16
+ throwErrorMessage(err, `Error postProcess: ${ String(task)}`);
17
+ }
18
+ }
19
+ });
20
+ };
21
+
22
+ export default postProcess;
@@ -10,47 +10,58 @@ import * as util from './util';
10
10
  };
11
11
  */
12
12
 
13
- const EVENTS = {};
13
+ interface Subscriber {
14
+ [compId: string]: Function | boolean | undefined;
15
+ isOnce?: boolean;
16
+ }
14
17
 
15
- const subscribeEvent = (instance = null, eventName = '', fn, isOnce = false) => {
16
- if (!instance || !instance.compId || !eventName || typeof fn !== 'function') {
18
+ interface Events {
19
+ [eventName: string]: Subscriber[];
20
+ }
21
+
22
+ const EVENTS: Events = {};
23
+
24
+ export const subscribeEvent = (instance: unknown = null, eventName: string = '', fn: Function, isOnce: boolean = false): void => {
25
+ if (!instance || typeof instance !== 'object' || !('compId' in instance) || !instance.compId || !eventName || typeof fn !== 'function') {
17
26
  return;
18
27
  }
19
28
 
20
- let subscriber;
29
+ let subscriber: Subscriber;
21
30
  let isSubscribed = false;
22
31
 
23
32
  eventName = eventName.replace(util.REGEX.WHITE_SPACES, '');
24
33
  EVENTS[eventName] = EVENTS[eventName] || [];
25
34
  // check if already subscribed and update callback fn
35
+ const instanceWithViewModel = instance as { compId: string | number; viewModel: unknown };
26
36
  isSubscribed = EVENTS[eventName].some((subscriber) => {
27
- if (subscriber[instance.compId]) {
28
- subscriber[instance.compId] = fn.bind(instance.viewModel);
37
+ if (subscriber[instanceWithViewModel.compId]) {
38
+ subscriber[instanceWithViewModel.compId] = fn.bind(instanceWithViewModel.viewModel);
29
39
  subscriber.isOnce = isOnce;
30
40
  return true;
31
41
  }
42
+ return false;
32
43
  });
33
44
  // push if not yet subscribe
34
45
  if (!isSubscribed) {
35
46
  subscriber = {};
36
- subscriber[instance.compId] = fn.bind(instance.viewModel);
47
+ subscriber[instanceWithViewModel.compId] = fn.bind(instanceWithViewModel.viewModel);
37
48
  subscriber.isOnce = isOnce;
38
49
  EVENTS[eventName].push(subscriber);
39
50
  }
40
51
  };
41
52
 
42
- const subscribeEventOnce = (instance = null, eventName = '', fn) => {
53
+ export const subscribeEventOnce = (instance: unknown = null, eventName: string = '', fn: Function): void => {
43
54
  subscribeEvent(instance, eventName, fn, true);
44
55
  };
45
56
 
46
- const unsubscribeEvent = (compId = '', eventName = '') => {
57
+ export const unsubscribeEvent = (compId: string | number = '', eventName: string = ''): void => {
47
58
  if (!compId || !eventName) {
48
59
  return;
49
60
  }
50
61
 
51
62
  let i = 0;
52
63
  let subscribersLength = 0;
53
- let subscriber;
64
+ let subscriber: Subscriber;
54
65
 
55
66
  eventName = eventName.replace(util.REGEX.WHITE_SPACES, '');
56
67
 
@@ -65,7 +76,7 @@ const unsubscribeEvent = (compId = '', eventName = '') => {
65
76
  }
66
77
  }
67
78
  // delete the event if no more subscriber
68
- if (!EVENTS[eventName].length) {
79
+ if (EVENTS[eventName] && !EVENTS[eventName].length) {
69
80
  delete EVENTS[eventName];
70
81
  }
71
82
  };
@@ -75,7 +86,7 @@ const unsubscribeEvent = (compId = '', eventName = '') => {
75
86
  * @description unsubscribe all event by compId. eg when a component removed
76
87
  * @param {string} compId
77
88
  */
78
- const unsubscribeAllEvent = (compId = '') => {
89
+ export const unsubscribeAllEvent = (compId: string | number = ''): void => {
79
90
  if (!compId) {
80
91
  return;
81
92
  }
@@ -84,7 +95,7 @@ const unsubscribeAllEvent = (compId = '') => {
84
95
  });
85
96
  };
86
97
 
87
- const publishEvent = (eventName = '', ...args) => {
98
+ export const publishEvent = (eventName: string = '', ...args: unknown[]): void => {
88
99
  if (!eventName || !EVENTS[eventName]) {
89
100
  return;
90
101
  }
@@ -103,5 +114,3 @@ const publishEvent = (eventName = '', ...args) => {
103
114
  });
104
115
  });
105
116
  };
106
-
107
- export {subscribeEvent, subscribeEventOnce, unsubscribeEvent, unsubscribeAllEvent, publishEvent};
@@ -0,0 +1,285 @@
1
+ import type {ViewModel} from './types';
2
+
3
+ interface ReactiveOptions {
4
+ onChange: () => void;
5
+ deep?: boolean;
6
+ trackChanges?: boolean;
7
+ }
8
+
9
+ interface ChangeTracker {
10
+ changedPaths: Set<string>;
11
+ }
12
+
13
+ // WeakMap to store proxy metadata
14
+ const PROXY_MARKER = Symbol('isReactiveProxy');
15
+ const ORIGINAL_TARGET = Symbol('originalTarget');
16
+
17
+ /**
18
+ * Check if an object is already a reactive proxy
19
+ */
20
+ function isReactiveProxy(obj: unknown): boolean {
21
+ return obj !== null && typeof obj === 'object' && (obj as Record<symbol, unknown>)[PROXY_MARKER] === true;
22
+ }
23
+
24
+ /**
25
+ * Get the original target from a proxy
26
+ */
27
+ function getOriginalTarget<T>(obj: T): T {
28
+ if (isReactiveProxy(obj)) {
29
+ return (obj as Record<symbol, unknown>)[ORIGINAL_TARGET] as T;
30
+ }
31
+ return obj;
32
+ }
33
+
34
+ /**
35
+ * Create a reactive proxy that automatically triggers onChange when properties are modified
36
+ * Supports deep proxying for nested objects and arrays
37
+ */
38
+ export function createReactiveProxy<T extends ViewModel>(
39
+ target: T,
40
+ options: ReactiveOptions,
41
+ path: string = '',
42
+ tracker?: ChangeTracker,
43
+ ): T {
44
+ const {onChange, deep = true, trackChanges = false} = options;
45
+
46
+ // Don't proxy non-objects
47
+ if (target === null || typeof target !== 'object') {
48
+ return target;
49
+ }
50
+
51
+ // Don't re-proxy already proxied objects
52
+ if (isReactiveProxy(target)) {
53
+ return target;
54
+ }
55
+
56
+ // Skip proxying special properties to avoid circular issues
57
+ const skipProps = ['APP', '$root', '__proto__', 'constructor'];
58
+ if (skipProps.includes(path)) {
59
+ return target;
60
+ }
61
+
62
+ // Track changes if enabled
63
+ const changeTracker = tracker || (trackChanges ? {changedPaths: new Set<string>()} : undefined);
64
+
65
+ // Store proxied nested objects to reuse same proxy
66
+ const proxiedChildren = new Map<string | symbol, unknown>();
67
+
68
+ const handler: ProxyHandler<T> = {
69
+ set(obj, prop, value) {
70
+ // Skip internal properties
71
+ if (typeof prop === 'symbol') {
72
+ (obj as Record<symbol, unknown>)[prop] = value;
73
+ return true;
74
+ }
75
+
76
+ const oldValue = obj[prop as keyof T];
77
+
78
+ // Only trigger if value actually changed
79
+ if (oldValue === value) {
80
+ return true;
81
+ }
82
+
83
+ // Set the new value
84
+ obj[prop as keyof T] = value;
85
+
86
+ // Clear cached proxy for this property since value changed
87
+ proxiedChildren.delete(prop);
88
+
89
+ // Track the changed path
90
+ if (changeTracker) {
91
+ const fullPath = path ? `${path}.${String(prop)}` : String(prop);
92
+ changeTracker.changedPaths.add(fullPath);
93
+ }
94
+
95
+ // Trigger onChange callback (debounced render)
96
+ onChange();
97
+
98
+ return true;
99
+ },
100
+
101
+ get(obj, prop) {
102
+ // Return proxy markers
103
+ if (prop === PROXY_MARKER) {
104
+ return true;
105
+ }
106
+ if (prop === ORIGINAL_TARGET) {
107
+ return obj;
108
+ }
109
+
110
+ const value = obj[prop as keyof T];
111
+
112
+ // Don't proxy functions, symbols, or special properties
113
+ if (
114
+ typeof value === 'function' ||
115
+ typeof prop === 'symbol' ||
116
+ skipProps.includes(String(prop))
117
+ ) {
118
+ return value;
119
+ }
120
+
121
+ // If deep proxying is enabled and value is an object, wrap it in proxy
122
+ if (deep && value !== null && typeof value === 'object') {
123
+ // Return cached proxy if exists
124
+ if (proxiedChildren.has(prop)) {
125
+ return proxiedChildren.get(prop);
126
+ }
127
+
128
+ const fullPath = path ? `${path}.${String(prop)}` : String(prop);
129
+ const proxied = Array.isArray(value)
130
+ ? createReactiveArray(value as unknown[], onChange, options, fullPath, changeTracker)
131
+ : createReactiveProxy(value as ViewModel, options, fullPath, changeTracker);
132
+
133
+ // Cache the proxy
134
+ proxiedChildren.set(prop, proxied);
135
+
136
+ return proxied;
137
+ }
138
+
139
+ return value;
140
+ },
141
+
142
+ deleteProperty(obj, prop) {
143
+ if (typeof prop === 'symbol') {
144
+ delete (obj as Record<symbol, unknown>)[prop];
145
+ return true;
146
+ }
147
+
148
+ delete obj[prop as keyof T];
149
+
150
+ // Clear cached proxy
151
+ proxiedChildren.delete(prop);
152
+
153
+ // Track deletion
154
+ if (changeTracker) {
155
+ const fullPath = path ? `${path}.${String(prop)}` : String(prop);
156
+ changeTracker.changedPaths.add(fullPath);
157
+ }
158
+
159
+ onChange();
160
+ return true;
161
+ },
162
+ };
163
+
164
+ return new Proxy(target, handler);
165
+ }
166
+
167
+ /**
168
+ * Special handling for arrays to intercept mutating methods
169
+ */
170
+ export function createReactiveArray<T extends unknown[]>(
171
+ target: T,
172
+ onChange: () => void,
173
+ options: ReactiveOptions,
174
+ path: string = '',
175
+ tracker?: ChangeTracker,
176
+ ): T {
177
+ const {deep = true} = options;
178
+
179
+ // Don't re-proxy already proxied arrays
180
+ if (isReactiveProxy(target)) {
181
+ return target;
182
+ }
183
+
184
+ const handler: ProxyHandler<T> = {
185
+ set(obj, prop, value) {
186
+ // Handle symbol properties
187
+ if (typeof prop === 'symbol') {
188
+ (obj as Record<symbol, unknown>)[prop] = value;
189
+ return true;
190
+ }
191
+
192
+ const oldValue = obj[prop as keyof T];
193
+
194
+ // Only trigger if value actually changed
195
+ if (oldValue === value) {
196
+ return true;
197
+ }
198
+
199
+ obj[prop as keyof T] = value;
200
+
201
+ // Track changes
202
+ if (tracker) {
203
+ const fullPath = path ? `${path}[${String(prop)}]` : `[${String(prop)}]`;
204
+ tracker.changedPaths.add(fullPath);
205
+ }
206
+
207
+ onChange();
208
+ return true;
209
+ },
210
+
211
+ get(obj, prop) {
212
+ // Return proxy markers
213
+ if (prop === PROXY_MARKER) {
214
+ return true;
215
+ }
216
+ if (prop === ORIGINAL_TARGET) {
217
+ return obj;
218
+ }
219
+
220
+ const value = obj[prop as keyof T];
221
+
222
+ // Intercept array mutating methods
223
+ if (typeof value === 'function') {
224
+ const mutatingMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse', 'fill'];
225
+ if (mutatingMethods.includes(String(prop))) {
226
+ return function (this: T, ...args: unknown[]) {
227
+ const result = (value as Function).apply(this, args);
228
+
229
+ // Track change
230
+ if (tracker) {
231
+ tracker.changedPaths.add(path || 'array');
232
+ }
233
+
234
+ onChange();
235
+ return result;
236
+ };
237
+ }
238
+ }
239
+
240
+ // Deep proxy array elements if they are objects
241
+ if (deep && value !== null && typeof value === 'object' && typeof prop !== 'symbol') {
242
+ const fullPath = path ? `${path}[${String(prop)}]` : `[${String(prop)}]`;
243
+ if (Array.isArray(value)) {
244
+ return createReactiveArray(value as unknown[], onChange, options, fullPath, tracker);
245
+ }
246
+ return createReactiveProxy(value as ViewModel, options, fullPath, tracker);
247
+ }
248
+
249
+ return value;
250
+ },
251
+
252
+ deleteProperty(obj, prop) {
253
+ if (typeof prop === 'symbol') {
254
+ delete (obj as Record<symbol, unknown>)[prop];
255
+ return true;
256
+ }
257
+
258
+ delete obj[prop as keyof T];
259
+
260
+ if (tracker) {
261
+ const fullPath = path ? `${path}[${String(prop)}]` : `[${String(prop)}]`;
262
+ tracker.changedPaths.add(fullPath);
263
+ }
264
+
265
+ onChange();
266
+ return true;
267
+ },
268
+ };
269
+
270
+ return new Proxy(target, handler);
271
+ }
272
+
273
+ /**
274
+ * Utility to get the original object from a reactive proxy
275
+ */
276
+ export function toRaw<T>(obj: T): T {
277
+ return getOriginalTarget(obj);
278
+ }
279
+
280
+ /**
281
+ * Check if Proxy is supported
282
+ */
283
+ export function isProxySupported(): boolean {
284
+ return typeof Proxy !== 'undefined';
285
+ }
@@ -1,4 +1,4 @@
1
- /* eslint-disable no-invalid-this */
1
+
2
2
  import {bindingAttrs as configBindingAttrs, bindingDataReference} from './config';
3
3
  import {
4
4
  getViewModelPropValue,
@@ -9,20 +9,27 @@ import {
9
9
  isEmptyObject,
10
10
  } from './util';
11
11
  import createBindingCache from './domWalker';
12
- import {renderIteration} from './binder';
12
+ import renderIteration from './renderIteration';
13
13
  import {
14
14
  wrapCommentAround,
15
15
  removeElemnetsByCommentWrap,
16
16
  insertRenderedElements,
17
17
  } from './commentWrapper';
18
+ import type {ViewModel, BindingCache, BindingAttrs, ElementCache} from './types';
18
19
 
19
- const renderForOfBinding = ({bindingData, viewModel, bindingAttrs}) => {
20
+ const renderForOfBinding = ({bindingData, viewModel, bindingAttrs}: {
21
+ bindingData: BindingCache;
22
+ viewModel: ViewModel;
23
+ bindingAttrs: BindingAttrs;
24
+ }): void => {
20
25
  if (!bindingData || !viewModel || !bindingAttrs) {
21
26
  return;
22
27
  }
23
- let keys;
24
- let iterationDataLength;
25
- const iterationData = getViewModelPropValue(viewModel, bindingData.iterator);
28
+ let keys: string[] | undefined;
29
+ let iterationDataLength: number;
30
+ // FIX: Use bindingData.iterator instead of bindingData to get the iteration data
31
+ // The iterator object has the dataKey pointing to the array/object to iterate over
32
+ const iterationData = getViewModelPropValue(viewModel, bindingData.iterator as BindingCache);
26
33
  let isRegenerate = false;
27
34
 
28
35
  // check iterationData and set iterationDataLength
@@ -57,19 +64,19 @@ const renderForOfBinding = ({bindingData, viewModel, bindingAttrs}) => {
57
64
  }
58
65
 
59
66
  if (!isRegenerate) {
60
- bindingData.iterationBindingCache.forEach(function(elementCache, i) {
67
+ (bindingData.iterationBindingCache as ElementCache[])?.forEach((elementCache: ElementCache, i: number) => {
61
68
  if (!isEmptyObject(elementCache)) {
62
69
  const iterationVm = createIterationViewModel({
63
- bindingData: bindingData,
64
- viewModel: viewModel,
65
- iterationData: iterationData,
66
- keys: keys,
70
+ bindingData,
71
+ viewModel,
72
+ iterationData,
73
+ keys,
67
74
  index: i,
68
75
  });
69
76
  renderIteration({
70
- elementCache: elementCache,
71
- iterationVm: iterationVm,
72
- bindingAttrs: bindingAttrs,
77
+ elementCache,
78
+ iterationVm,
79
+ bindingAttrs,
73
80
  isRegenerate: false,
74
81
  });
75
82
  }
@@ -97,27 +104,42 @@ const renderForOfBinding = ({bindingData, viewModel, bindingAttrs}) => {
97
104
  * @param {*} param0
98
105
  * @return {object} virtual viewModel
99
106
  */
100
- const createIterationViewModel = ({bindingData, viewModel, iterationData, keys, index}) => {
101
- const iterationVm = {};
102
- iterationVm[bindingData.iterator.alias] = keys ? iterationData[keys[index]] : iterationData[index];
107
+ const createIterationViewModel = ({bindingData, viewModel, iterationData, keys, index}: {
108
+ bindingData: BindingCache;
109
+ viewModel: ViewModel;
110
+ iterationData: unknown;
111
+ keys: string[] | undefined;
112
+ index: number;
113
+ }): ViewModel => {
114
+ const iterationVm: ViewModel = {};
115
+ const alias = bindingData.iterator?.alias;
116
+ if (alias) {
117
+ iterationVm[alias] = keys ? (iterationData as Record<string, unknown>)[keys[index]] : (iterationData as unknown[])[index];
118
+ }
103
119
  // populate common binding data reference
104
120
  iterationVm[bindingDataReference.rootDataKey] = viewModel.$root || viewModel;
105
- iterationVm[bindingDataReference.currentData] = iterationVm[bindingData.iterator.alias];
121
+ iterationVm[bindingDataReference.currentData] = alias ? iterationVm[alias] : undefined;
106
122
  iterationVm[bindingDataReference.currentIndex] = index;
107
123
  return iterationVm;
108
124
  };
109
125
 
110
- const generateForOfElements = (bindingData, viewModel, bindingAttrs, iterationData, keys) => {
126
+ const generateForOfElements = (
127
+ bindingData: BindingCache,
128
+ viewModel: ViewModel,
129
+ bindingAttrs: BindingAttrs,
130
+ iterationData: unknown,
131
+ keys: string[] | undefined,
132
+ ): DocumentFragment => {
111
133
  const fragment = document.createDocumentFragment();
112
- const iterationDataLength = bindingData.iterationSize;
113
- let clonedItem;
114
- let iterationVm;
115
- let iterationBindingCache;
134
+ const iterationDataLength = bindingData.iterationSize as number;
135
+ let clonedItem: HTMLElement;
136
+ let iterationVm: ViewModel;
137
+ let iterationBindingCache: ElementCache;
116
138
  let i = 0;
117
139
 
118
140
  // create or clear exisitng iterationBindingCache
119
141
  if (isArray(bindingData.iterationBindingCache)) {
120
- bindingData.iterationBindingCache.length = 0;
142
+ (bindingData.iterationBindingCache as ElementCache[]).length = 0;
121
143
  } else {
122
144
  bindingData.iterationBindingCache = [];
123
145
  }
@@ -129,25 +151,25 @@ const generateForOfElements = (bindingData, viewModel, bindingAttrs, iterationDa
129
151
  // create bindingCache per iteration
130
152
  iterationBindingCache = createBindingCache({
131
153
  rootNode: clonedItem,
132
- bindingAttrs: bindingAttrs,
154
+ bindingAttrs,
133
155
  });
134
156
 
135
- bindingData.iterationBindingCache.push(iterationBindingCache);
157
+ (bindingData.iterationBindingCache as ElementCache[]).push(iterationBindingCache);
136
158
 
137
159
  if (!isEmptyObject(iterationBindingCache)) {
138
160
  // create an iterationVm match iterator alias
139
161
  iterationVm = createIterationViewModel({
140
- bindingData: bindingData,
141
- viewModel: viewModel,
142
- iterationData: iterationData,
143
- keys: keys,
162
+ bindingData,
163
+ viewModel,
164
+ iterationData,
165
+ keys,
144
166
  index: i,
145
167
  });
146
168
 
147
169
  renderIteration({
148
- elementCache: bindingData.iterationBindingCache[i],
149
- iterationVm: iterationVm,
150
- bindingAttrs: bindingAttrs,
170
+ elementCache: (bindingData.iterationBindingCache as ElementCache[])[i],
171
+ iterationVm,
172
+ bindingAttrs,
151
173
  isRegenerate: true,
152
174
  });
153
175
  }