@gogocat/data-bind 1.12.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 (271) 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 +2756 -2530
  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/globalConfig.html +131 -0
  147. package/examples/js/afterRenderDemo.js +190 -0
  148. package/examples/js/appTodo.js +46 -46
  149. package/examples/js/attrBindingDemo.js +2 -2
  150. package/examples/js/dbMonApp.js +24 -26
  151. package/examples/js/dbMonAppReact.jsx +79 -0
  152. package/examples/js/dbMonAppReactive.js +28 -0
  153. package/examples/js/fiberDemo.js +4 -4
  154. package/examples/js/filtersDemo.js +8 -8
  155. package/examples/js/forOfDemo.js +7 -9
  156. package/examples/js/forOfDemoComplex.js +44 -17
  157. package/examples/js/form.js +14 -14
  158. package/examples/js/globalConfig.js +117 -0
  159. package/examples/js/ifBindingDemo.js +16 -16
  160. package/examples/js/reactiveDemo.js +119 -0
  161. package/examples/js/switchBindingDemo.js +8 -8
  162. package/examples/react-dbmonster/dist/bundle.js +43 -0
  163. package/examples/react-dbmonster/package-lock.json +537 -0
  164. package/examples/react-dbmonster/package.json +16 -0
  165. package/examples/react-dbmonster/src/index.jsx +80 -0
  166. package/examples/reactiveDemo.html +127 -0
  167. package/examples/refreshRateTest.html +75 -75
  168. package/index.html +841 -0
  169. package/package.json +31 -34
  170. package/rollup.config.js +79 -36
  171. package/src/{_escape.js → _escape.ts} +19 -17
  172. package/src/{applyBinding.js → applyBinding.ts} +27 -18
  173. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  174. package/src/{binder.js → binder.ts} +289 -181
  175. package/src/changeBinding.ts +93 -0
  176. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  177. package/src/config.ts +107 -0
  178. package/src/{createBindingOption.js → createBindingOption.ts} +39 -15
  179. package/src/createEventBinding.ts +88 -0
  180. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  181. package/src/{domWalker.js → domWalker.ts} +44 -30
  182. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  183. package/src/hoverBinding.ts +84 -0
  184. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  185. package/src/index.ts +53 -0
  186. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  187. package/src/{postProcess.js → postProcess.ts} +6 -4
  188. package/src/{pubSub.js → pubSub.ts} +24 -21
  189. package/src/reactiveProxy.ts +285 -0
  190. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +54 -32
  191. package/src/{renderIfBinding.js → renderIfBinding.ts} +41 -19
  192. package/src/{renderIteration.js → renderIteration.ts} +24 -8
  193. package/src/renderTemplate.ts +165 -0
  194. package/src/renderTemplatesBinding.ts +73 -0
  195. package/src/{showBinding.js → showBinding.ts} +4 -3
  196. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  197. package/src/{textBinding.js → textBinding.ts} +5 -4
  198. package/src/types.ts +124 -0
  199. package/src/util.ts +810 -0
  200. package/test/css/reporter.css +9 -9
  201. package/test/globals.d.ts +19 -0
  202. package/test/helpers/testHelper.js +46 -11
  203. package/test/mocks/featureAdsResult.json +65 -65
  204. package/test/mocks/searchResult.json +57 -57
  205. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  206. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  207. package/test/specs/blurBinding.spec.ts +60 -0
  208. package/test/specs/chainableUse.spec.ts +125 -0
  209. package/test/specs/clickBinding.spec.ts +194 -0
  210. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  211. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  212. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  213. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  214. package/test/specs/formBinding.spec.ts +272 -0
  215. package/test/specs/ifBinding.spec.ts +165 -0
  216. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  217. package/test/specs/reactiveProxy.spec.ts +465 -0
  218. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  219. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  220. package/test/specs/templateBinding.spec.ts +273 -0
  221. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  222. package/test/tsconfig.json +31 -0
  223. package/test-output.txt +200 -0
  224. package/test-reactive.html +224 -0
  225. package/tsconfig.json +28 -0
  226. package/vendors/lodash.custom.js +4577 -4577
  227. package/vendors/lodash.custom.min.js +45 -45
  228. package/vitest.config.js +27 -0
  229. package/.eslintrc.js +0 -1
  230. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  231. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  232. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  233. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  234. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  235. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  236. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  237. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  238. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  239. package/coverage/coverage.json +0 -1
  240. package/coverage/lcov/lcov-report/base.css +0 -213
  241. package/coverage/lcov/lcov-report/index.html +0 -93
  242. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  243. package/coverage/lcov/lcov-report/js/index.html +0 -93
  244. package/coverage/lcov/lcov-report/prettify.css +0 -1
  245. package/coverage/lcov/lcov-report/prettify.js +0 -1
  246. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  247. package/coverage/lcov/lcov-report/sorter.js +0 -158
  248. package/coverage/lcov/lcov.info +0 -1991
  249. package/eslintrc.json +0 -40
  250. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  251. package/examples/bootstrap/js/popper.min.js +0 -5
  252. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  253. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  254. package/gruntfile.js +0 -92
  255. package/gulpfile.js +0 -32
  256. package/src/applyBindingExport.js +0 -5
  257. package/src/changeBinding.js +0 -63
  258. package/src/config.js +0 -66
  259. package/src/createEventBinding.js +0 -46
  260. package/src/eventSystem.js +0 -46
  261. package/src/hoverBinding.js +0 -57
  262. package/src/index.js +0 -26
  263. package/src/renderTemplate.js +0 -128
  264. package/src/renderTemplatesBinding.js +0 -44
  265. package/src/util.js +0 -648
  266. package/test/specs/blurBinding.spec.js +0 -57
  267. package/test/specs/formBinding.spec.js +0 -316
  268. package/test/specs/ifBinding.spec.js +0 -169
  269. package/test/specs/templateBinding.spec.js +0 -117
  270. package/vendors/jasmine-jquery.js +0 -841
  271. package/vendors/jquery-3.2.1.min.js +0 -4
@@ -0,0 +1,165 @@
1
+ import {dataIndexAttr} from './config';
2
+ import {
3
+ createHtmlFragment,
4
+ emptyElement,
5
+ getViewModelPropValue,
6
+ parseStringToJson,
7
+ updateDomWithMinimalChanges,
8
+ } from './util';
9
+ import type {BindingCache, ViewModel, BindingAttrs, ElementCache, PlainObject} from './types';
10
+
11
+ let $domFragment: DocumentFragment | null = null;
12
+ let $templateRoot: HTMLElement | null = null;
13
+ let $templateRootPrepend = false;
14
+ let $templateRootAppend = false;
15
+ let nestTemplatesCount = 0;
16
+
17
+ /**
18
+ * getTemplateString
19
+ * @description get Template tag innerHTML string
20
+ * @param {string} id
21
+ * @return {string} rendered html string
22
+ */
23
+ const getTemplateString = (id: string): string => {
24
+ const templateElement = document.getElementById(id);
25
+
26
+ return templateElement ? templateElement.innerHTML : '';
27
+ };
28
+
29
+ /**
30
+ * renderTemplate
31
+ * @description
32
+ * get template setting from DOM attribute then call compileTemplate
33
+ * to render and append to target DOM
34
+ * @param {object} cache
35
+ * @param {object} viewModel
36
+ * @param {object} bindingAttrs
37
+ * @param {object} elementCache
38
+ */
39
+ const renderTemplate = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, elementCache: ElementCache): void => {
40
+ const settings = typeof cache.dataKey === 'string' ? parseStringToJson(cache.dataKey) : cache.dataKey as PlainObject;
41
+ let viewData: unknown = (settings as PlainObject).data;
42
+ const isAppend = (settings as PlainObject).append;
43
+ const isPrepend = (settings as PlainObject).prepend;
44
+ let $currentElement: DocumentFragment | HTMLElement;
45
+
46
+ cache.dataKey = settings as unknown as string;
47
+
48
+ viewData = (typeof viewData === 'undefined' || viewData === '$root') ?
49
+ viewModel :
50
+ getViewModelPropValue(viewModel, {
51
+ dataKey: (settings as PlainObject).data,
52
+ parameters: cache.parameters,
53
+ } as BindingCache);
54
+
55
+ if (!viewData) {
56
+ return;
57
+ }
58
+
59
+ const $element = cache.el;
60
+ const $indexAttr = $element.getAttribute(dataIndexAttr);
61
+ const $index = typeof viewModel.$index !== 'undefined' ? viewModel.$index : ($indexAttr ? parseInt($indexAttr, 10) : undefined);
62
+
63
+ if (typeof $index !== 'undefined' && viewData && typeof viewData === 'object') {
64
+ (viewData as ViewModel).$index = $index;
65
+ }
66
+
67
+ $domFragment = $domFragment || document.createDocumentFragment();
68
+
69
+ if (!$templateRoot) {
70
+ $templateRoot = $element;
71
+ // Store the prepend/append flags from the root template only
72
+ $templateRootPrepend = Boolean(isPrepend);
73
+ $templateRootAppend = Boolean(isAppend);
74
+ }
75
+
76
+ const htmlString = getTemplateString((settings as PlainObject).id as string);
77
+
78
+ const htmlFragment = createHtmlFragment(htmlString);
79
+
80
+ // Return early if htmlFragment is null (invalid template)
81
+ if (!htmlFragment) {
82
+ return;
83
+ }
84
+
85
+ // append rendered html
86
+ if (!$domFragment.childNodes.length) {
87
+ // domFragment should be empty in first run
88
+ $currentElement = $domFragment; // copy of $domFragment for later find nested template check
89
+ $domFragment.appendChild(htmlFragment);
90
+ } else {
91
+ // during recursive run keep append to current fragment
92
+ // For nested templates, use the original behavior (clear and append)
93
+ // because they may contain forOf bindings or other dynamic content
94
+ // that manages its own DOM structure
95
+ $currentElement = $element; // reset to current nested template element
96
+ if (!isAppend && !isPrepend) {
97
+ $currentElement = emptyElement($currentElement);
98
+ }
99
+ if (isPrepend) {
100
+ $currentElement.insertBefore(htmlFragment, $currentElement.firstChild);
101
+ } else {
102
+ $currentElement.appendChild(htmlFragment);
103
+ }
104
+ }
105
+
106
+ // check if there are nested template then recurisive render them
107
+ const $nestedTemplates = $currentElement.querySelectorAll(`[${ bindingAttrs.tmp }]`);
108
+
109
+ const nestedTemplatesLength = $nestedTemplates.length;
110
+
111
+ if (nestedTemplatesLength) {
112
+ nestTemplatesCount += nestedTemplatesLength;
113
+
114
+ for (let i=0; i < nestedTemplatesLength; i+=1) {
115
+ const thisTemplateCache = {
116
+ el: $nestedTemplates[i] as HTMLElement,
117
+ dataKey: $nestedTemplates[i].getAttribute(bindingAttrs.tmp),
118
+ } as BindingCache;
119
+ elementCache[bindingAttrs.tmp].push(thisTemplateCache);
120
+ // recursive template render
121
+ renderTemplate(thisTemplateCache, viewModel, bindingAttrs, elementCache);
122
+ nestTemplatesCount -= 1;
123
+ }
124
+ }
125
+
126
+ // no more nested tempalted to render, start to append $domFragment into $templateRoot
127
+ if (nestTemplatesCount === 0) {
128
+ // append to DOM once
129
+ // Use the prepend/append flags from the root template, not the current nested template
130
+ if (!$templateRootAppend && !$templateRootPrepend) {
131
+ // Check if this is a re-render by looking for a marker attribute
132
+ // This is more reliable than checking childNodes.length because templates
133
+ // may have placeholder content
134
+ const isRerender = $templateRoot.hasAttribute('data-template-rendered');
135
+
136
+ if (isRerender) {
137
+ // Re-render: Use minimal DOM updates to preserve unchanged elements
138
+ // This is faster and preserves DOM state (focus, scroll, animations)
139
+ updateDomWithMinimalChanges($templateRoot, $domFragment);
140
+ } else {
141
+ // Initial render: Clear any placeholder content and render fresh
142
+ $templateRoot = emptyElement($templateRoot);
143
+ $templateRoot.appendChild($domFragment);
144
+ // Mark this template as rendered for future re-renders
145
+ $templateRoot.setAttribute('data-template-rendered', 'true');
146
+ }
147
+ } else {
148
+ // For prepend/append modes, use the original behavior
149
+ if ($templateRootPrepend) {
150
+ $templateRoot.insertBefore($domFragment, $templateRoot.firstChild);
151
+ } else {
152
+ $templateRoot.appendChild($domFragment);
153
+ }
154
+ }
155
+ // clear cached fragment and flags
156
+ $domFragment = $templateRoot = null;
157
+ $templateRootPrepend = $templateRootAppend = false;
158
+ // trigger callback if provided
159
+ if (viewModel.afterTemplateRender && typeof viewModel.afterTemplateRender === 'function') {
160
+ (viewModel.afterTemplateRender as Function)(viewData);
161
+ }
162
+ }
163
+ };
164
+
165
+ export default renderTemplate;
@@ -0,0 +1,73 @@
1
+ import {bindingUpdateConditions} from './config';
2
+ import * as applyBindingModule from './applyBinding';
3
+ import createBindingOption from './createBindingOption';
4
+ import renderTemplate from './renderTemplate';
5
+ import type {ElementCache, ViewModel, BindingAttrs, BindingCache} from './types';
6
+ import type {BindingOption} from './createBindingOption';
7
+
8
+ interface BinderContext {
9
+ updateElementCache: (opt: {
10
+ allCache?: boolean;
11
+ templateCache?: boolean;
12
+ elementCache?: ElementCache;
13
+ isRenderedTemplates?: boolean;
14
+ }) => void;
15
+ }
16
+
17
+ const renderTemplatesBinding = ({
18
+ ctx,
19
+ elementCache,
20
+ updateOption,
21
+ bindingAttrs,
22
+ viewModel,
23
+ }: {
24
+ ctx: BinderContext;
25
+ elementCache: ElementCache;
26
+ updateOption: BindingOption;
27
+ bindingAttrs: BindingAttrs;
28
+ viewModel: ViewModel;
29
+ }): boolean => {
30
+ if (!elementCache || !bindingAttrs) {
31
+ return false;
32
+ }
33
+ // render and apply binding to template(s) and forOf DOM
34
+ if (elementCache[bindingAttrs.tmp] && elementCache[bindingAttrs.tmp].length) {
35
+ // when re-render call with {templateBinding: true}
36
+ // template and nested templates
37
+ if (updateOption.templateBinding) {
38
+ // overwrite updateOption with 'init' bindingUpdateConditions
39
+ updateOption = createBindingOption(bindingUpdateConditions.init);
40
+
41
+ // forEach is correct here - nested templates are added to array but rendered recursively
42
+ // We don't want the loop to re-render templates that were already rendered via recursion
43
+ elementCache[bindingAttrs.tmp].forEach(($element: unknown) => {
44
+ renderTemplate($element as BindingCache, viewModel, bindingAttrs, elementCache);
45
+ });
46
+ // update cache after all template(s) rendered
47
+ ctx.updateElementCache({
48
+ templateCache: true,
49
+ elementCache,
50
+ isRenderedTemplates: true,
51
+ });
52
+ }
53
+ // enforce render even element is not in DOM tree
54
+ updateOption.forceRender = true;
55
+
56
+ // apply bindings to rendered templates element
57
+ // Use namespace import to access the function at runtime,
58
+ // which breaks the circular dependency during module initialization
59
+ // Use for loop to handle templates added during rendering
60
+ for (let i = 0; i < elementCache[bindingAttrs.tmp].length; i++) {
61
+ applyBindingModule.default({
62
+ ctx,
63
+ elementCache: elementCache[bindingAttrs.tmp][i].bindingCache as ElementCache,
64
+ updateOption,
65
+ bindingAttrs,
66
+ viewModel,
67
+ });
68
+ }
69
+ }
70
+ return true;
71
+ };
72
+
73
+ export default renderTemplatesBinding;
@@ -1,4 +1,5 @@
1
1
  import {getViewModelPropValue} from './util';
2
+ import type {BindingCache, ViewModel, BindingAttrs} from './types';
2
3
 
3
4
  /**
4
5
  * showBinding
@@ -9,9 +10,9 @@ import {getViewModelPropValue} from './util';
9
10
  * @param {object} viewModel
10
11
  * @param {object} bindingAttrs
11
12
  */
12
- const showBinding = (cache, viewModel, bindingAttrs) => {
13
+ const showBinding = (cache: BindingCache, viewModel: ViewModel, _bindingAttrs: BindingAttrs, _forceRender?: boolean): void => {
13
14
  const dataKey = cache.dataKey;
14
- let currentInlineSytle = {};
15
+ let currentInlineSytle: CSSStyleDeclaration | Record<string, never> = {};
15
16
  let currentInlineDisplaySytle = '';
16
17
  let shouldShow = true;
17
18
 
@@ -42,7 +43,7 @@ const showBinding = (cache, viewModel, bindingAttrs) => {
42
43
  }
43
44
  }
44
45
 
45
- shouldShow = getViewModelPropValue(viewModel, cache);
46
+ shouldShow = getViewModelPropValue(viewModel, cache) as boolean;
46
47
 
47
48
  // treat undefined || null as false.
48
49
  // eg if property doesn't exsits in viewModel, it will treat as false to hide element
@@ -1,6 +1,8 @@
1
1
  import {getViewModelPropValue} from './util';
2
2
  import {createClonedElementCache, wrapCommentAround} from './commentWrapper';
3
3
  import {renderIfBinding, removeIfBinding} from './renderIfBinding';
4
+ import type {BindingCache, ViewModel, BindingAttrs, CaseData} from './types';
5
+
4
6
  /**
5
7
  * switch-Binding
6
8
  * @description
@@ -10,7 +12,7 @@ import {renderIfBinding, removeIfBinding} from './renderIfBinding';
10
12
  * @param {object} viewModel
11
13
  * @param {object} bindingAttrs
12
14
  */
13
- const switchBinding = (cache, viewModel, bindingAttrs) => {
15
+ const switchBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, _forceRender?: boolean): void => {
14
16
  const dataKey = cache.dataKey;
15
17
 
16
18
  if (!dataKey) {
@@ -35,11 +37,12 @@ const switchBinding = (cache, viewModel, bindingAttrs) => {
35
37
  }
36
38
  cache.cases = [];
37
39
  for (let i = 0, elementLength = childrenElements.length; i < elementLength; i += 1) {
38
- let caseData = null;
39
- if (childrenElements[i].hasAttribute(bindingAttrs.case)) {
40
- caseData = createCaseData(childrenElements[i], bindingAttrs.case);
41
- } else if (childrenElements[i].hasAttribute(bindingAttrs.default)) {
42
- caseData = createCaseData(childrenElements[i], bindingAttrs.default);
40
+ let caseData: CaseData | null = null;
41
+ const childElement = childrenElements[i] as HTMLElement;
42
+ if (childElement.hasAttribute(bindingAttrs.case)) {
43
+ caseData = createCaseData(childElement, bindingAttrs.case);
44
+ } else if (childElement.hasAttribute(bindingAttrs.default)) {
45
+ caseData = createCaseData(childElement, bindingAttrs.default);
43
46
  caseData.isDefault = true;
44
47
  }
45
48
  // create fragment by clone node
@@ -62,7 +65,7 @@ const switchBinding = (cache, viewModel, bindingAttrs) => {
62
65
  let hasMatch = false;
63
66
  // do switch operation - reuse if binding logic
64
67
  for (let j = 0, casesLength = cache.cases.length; j < casesLength; j += 1) {
65
- let newCaseValue;
68
+ let newCaseValue: unknown;
66
69
  if (cache.cases[j].dataKey) {
67
70
  // set back to dataKey if nothing found in viewModel
68
71
  newCaseValue = getViewModelPropValue(viewModel, cache.cases[j]) || cache.cases[j].dataKey;
@@ -73,8 +76,8 @@ const switchBinding = (cache, viewModel, bindingAttrs) => {
73
76
  // render element
74
77
  renderIfBinding({
75
78
  bindingData: cache.cases[j],
76
- viewModel: viewModel,
77
- bindingAttrs: bindingAttrs,
79
+ viewModel,
80
+ bindingAttrs,
78
81
  });
79
82
 
80
83
  // remove other elements
@@ -89,8 +92,8 @@ const switchBinding = (cache, viewModel, bindingAttrs) => {
89
92
  }
90
93
  };
91
94
 
92
- function removeUnmatchCases(cases, matchedIndex) {
93
- cases.forEach((caseData, index) => {
95
+ const removeUnmatchCases = (cases: CaseData[], matchedIndex?: number): void => {
96
+ cases.forEach((caseData: CaseData, index: number) => {
94
97
  if (index !== matchedIndex || typeof matchedIndex === 'undefined') {
95
98
  removeIfBinding(caseData);
96
99
  // remove cache.IterationBindingCache to prevent memory leak
@@ -100,15 +103,15 @@ function removeUnmatchCases(cases, matchedIndex) {
100
103
  }
101
104
  }
102
105
  });
103
- }
106
+ };
104
107
 
105
- function createCaseData(node, attrName) {
106
- const caseData = {
108
+ const createCaseData = (node: HTMLElement, attrName: string): CaseData => {
109
+ const caseData: CaseData = {
107
110
  el: node,
108
111
  dataKey: node.getAttribute(attrName),
109
112
  type: attrName,
110
113
  };
111
114
  return caseData;
112
- }
115
+ };
113
116
 
114
117
  export default switchBinding;
@@ -1,4 +1,5 @@
1
1
  import {getViewModelPropValue} from './util';
2
+ import type {BindingCache, ViewModel, BindingAttrs} from './types';
2
3
 
3
4
  /**
4
5
  * textBinding
@@ -9,12 +10,12 @@ import {getViewModelPropValue} from './util';
9
10
  * @param {object} bindingAttrs
10
11
  * @param {boolean} forceRender
11
12
  */
12
- const textBinding = (cache, viewModel, bindingAttrs, forceRender) => {
13
+ const textBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, forceRender: boolean): void => {
13
14
  const dataKey = cache.dataKey;
14
- const APP = viewModel.APP || viewModel.$root.APP;
15
+ const APP = viewModel.APP || viewModel.$root?.APP;
15
16
 
16
17
  // NOTE: this doesn't work for for-of, if and switch bindings because element was not in DOM
17
- if (!dataKey || (!forceRender && !APP.$rootElement.contains(cache.el))) {
18
+ if (!dataKey || (!forceRender && !(APP?.$rootElement as HTMLElement)?.contains(cache.el))) {
18
19
  return;
19
20
  }
20
21
 
@@ -23,7 +24,7 @@ const textBinding = (cache, viewModel, bindingAttrs, forceRender) => {
23
24
 
24
25
  if (typeof newValue !== 'undefined' && typeof newValue !== 'object' && newValue !== null) {
25
26
  if (newValue !== oldValue) {
26
- cache.el.textContent = newValue;
27
+ cache.el.textContent = String(newValue);
27
28
  }
28
29
  }
29
30
  };
package/src/types.ts ADDED
@@ -0,0 +1,124 @@
1
+ // Common types used across the application
2
+
3
+ // Generic unknown value type - safer than any
4
+ export type UnknownValue = unknown;
5
+
6
+ export interface PlainObject {
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ export interface ViewModel {
11
+ [key: string]: unknown;
12
+ APP?: {
13
+ render?: (opt?: UpdateOption) => void | Promise<void>;
14
+ postProcessQueue?: Array<() => void>;
15
+ [key: string]: unknown;
16
+ };
17
+ $root?: ViewModel;
18
+ $data?: unknown;
19
+ $index?: number;
20
+ }
21
+
22
+ export interface ElementData {
23
+ viewModelPropValue?: unknown;
24
+ displayStyle?: string | null;
25
+ computedStyle?: string | null;
26
+ [key: string]: unknown;
27
+ }
28
+
29
+ export interface BindingCache {
30
+ el: HTMLElement;
31
+ dataKey?: string;
32
+ parameters?: unknown[];
33
+ filters?: string[];
34
+ isOnce?: boolean;
35
+ elementData?: ElementData;
36
+ bindingCache?: unknown;
37
+ type?: string;
38
+ fragment?: DocumentFragment;
39
+ hasIterationBindingCache?: boolean;
40
+ iterationBindingCache?: unknown;
41
+ parentElement?: HTMLElement | null;
42
+ previousNonTemplateElement?: Node | null;
43
+ nextNonTemplateElement?: Node | null;
44
+ iterator?: {
45
+ alias?: string;
46
+ dataKey?: string;
47
+ };
48
+ cases?: CaseData[];
49
+ [key: string]: unknown;
50
+ }
51
+
52
+ export interface CaseData {
53
+ el: HTMLElement;
54
+ dataKey?: string;
55
+ type: string;
56
+ isDefault?: boolean;
57
+ fragment?: DocumentFragment;
58
+ hasIterationBindingCache?: boolean;
59
+ iterationBindingCache?: unknown;
60
+ [key: string]: unknown;
61
+ }
62
+
63
+ export interface ElementCache {
64
+ [key: string]: BindingCache[];
65
+ }
66
+
67
+ export interface UpdateOption {
68
+ forceRender?: boolean;
69
+ attrBinding?: boolean;
70
+ cssBinding?: boolean;
71
+ textBinding?: boolean;
72
+ modelBinding?: boolean;
73
+ showBinding?: boolean;
74
+ ifBinding?: boolean;
75
+ switchBinding?: boolean;
76
+ forOfBinding?: boolean;
77
+ changeBinding?: boolean;
78
+ submitBinding?: boolean;
79
+ clickBinding?: boolean;
80
+ dblclickBinding?: boolean;
81
+ blurBinding?: boolean;
82
+ focusBinding?: boolean;
83
+ hoverBinding?: boolean;
84
+ inputBinding?: boolean;
85
+ [key: string]: unknown;
86
+ }
87
+
88
+ export interface DeferredObj<T = unknown> {
89
+ promise: Promise<T>;
90
+ resolve: (value?: T) => void;
91
+ reject: (reason?: unknown) => void;
92
+ }
93
+
94
+ export interface WrapMap {
95
+ [key: string]: [string, string, string];
96
+ }
97
+
98
+ export interface BindingAttrs {
99
+ [key: string]: string;
100
+ attr: string;
101
+ css: string;
102
+ text: string;
103
+ model: string;
104
+ show: string;
105
+ if: string;
106
+ switch: string;
107
+ case: string;
108
+ default: string;
109
+ forOf: string;
110
+ change: string;
111
+ submit: string;
112
+ click: string;
113
+ dblclick: string;
114
+ blur: string;
115
+ focus: string;
116
+ hover: string;
117
+ input: string;
118
+ tmp: string;
119
+ }
120
+
121
+ export interface BinderOptions {
122
+ reactive?: boolean;
123
+ trackChanges?: boolean;
124
+ }