cx 26.4.0 → 26.4.1

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 (591) hide show
  1. package/.mocharc.json +5 -5
  2. package/LICENSE-THIRD-PARTY.md +91 -91
  3. package/LICENSE.md +7 -7
  4. package/README.md +46 -46
  5. package/build/util/innerTextTrim.js +1 -1
  6. package/build.js +133 -133
  7. package/dist/manifest.js +697 -697
  8. package/dist/util.js +1 -1
  9. package/package.json +101 -100
  10. package/src/charts/Bar.scss +32 -32
  11. package/src/charts/Bar.ts +114 -114
  12. package/src/charts/BarGraph.scss +31 -31
  13. package/src/charts/BarGraph.tsx +145 -145
  14. package/src/charts/BubbleGraph.scss +35 -35
  15. package/src/charts/BubbleGraph.tsx +165 -165
  16. package/src/charts/Chart.ts +110 -110
  17. package/src/charts/ColorMap.ts +150 -150
  18. package/src/charts/Column.scss +32 -32
  19. package/src/charts/Column.ts +124 -124
  20. package/src/charts/ColumnBarBase.tsx +309 -309
  21. package/src/charts/ColumnBarGraphBase.tsx +229 -229
  22. package/src/charts/ColumnGraph.scss +32 -32
  23. package/src/charts/ColumnGraph.tsx +153 -153
  24. package/src/charts/Grid.ts +5 -5
  25. package/src/charts/Gridlines.scss +27 -27
  26. package/src/charts/Gridlines.tsx +88 -88
  27. package/src/charts/Legend.scss +57 -57
  28. package/src/charts/Legend.tsx +314 -314
  29. package/src/charts/LegendEntry.scss +35 -35
  30. package/src/charts/LegendEntry.tsx +197 -197
  31. package/src/charts/LineGraph.scss +28 -28
  32. package/src/charts/LineGraph.tsx +455 -455
  33. package/src/charts/Marker.scss +46 -46
  34. package/src/charts/Marker.tsx +538 -538
  35. package/src/charts/MarkerLine.scss +23 -23
  36. package/src/charts/MarkerLine.tsx +233 -233
  37. package/src/charts/MouseTracker.tsx +115 -115
  38. package/src/charts/Pie.ts +8 -8
  39. package/src/charts/PieChart.scss +32 -32
  40. package/src/charts/PieChart.tsx +843 -843
  41. package/src/charts/PieLabel.tsx +124 -124
  42. package/src/charts/PieLabelsContainer.ts +68 -68
  43. package/src/charts/Range.scss +23 -23
  44. package/src/charts/Range.tsx +318 -318
  45. package/src/charts/RangeMarker.scss +17 -17
  46. package/src/charts/RangeMarker.tsx +250 -250
  47. package/src/charts/ScatterGraph.scss +27 -27
  48. package/src/charts/ScatterGraph.tsx +245 -245
  49. package/src/charts/Swimlane.scss +19 -19
  50. package/src/charts/Swimlane.tsx +195 -195
  51. package/src/charts/Swimlanes.scss +19 -19
  52. package/src/charts/Swimlanes.tsx +179 -179
  53. package/src/charts/axis/Axis.scss +24 -24
  54. package/src/charts/axis/Axis.tsx +444 -444
  55. package/src/charts/axis/CategoryAxis.scss +37 -37
  56. package/src/charts/axis/CategoryAxis.tsx +313 -313
  57. package/src/charts/axis/NumericAxis.scss +38 -38
  58. package/src/charts/axis/NumericAxis.tsx +437 -437
  59. package/src/charts/axis/Stack.ts +61 -61
  60. package/src/charts/axis/TimeAxis.scss +37 -37
  61. package/src/charts/axis/TimeAxis.tsx +834 -834
  62. package/src/charts/axis/index.scss +4 -4
  63. package/src/charts/axis/index.ts +3 -3
  64. package/src/charts/axis/variables.scss +5 -5
  65. package/src/charts/helpers/MinMaxFinder.ts +66 -66
  66. package/src/charts/helpers/PointReducer.ts +135 -135
  67. package/src/charts/helpers/SnapPointFinder.ts +136 -136
  68. package/src/charts/helpers/ValueAtFinder.ts +72 -72
  69. package/src/charts/helpers/index.ts +4 -4
  70. package/src/charts/index.scss +21 -21
  71. package/src/charts/index.ts +34 -34
  72. package/src/charts/palette.scss +77 -77
  73. package/src/charts/palette.variables.scss +23 -23
  74. package/src/charts/shapes.tsx +86 -86
  75. package/src/charts/variables.scss +53 -53
  76. package/src/core.d.ts +182 -182
  77. package/src/data/AggregateFunction.ts +171 -171
  78. package/src/data/ArrayElementView.spec.ts +88 -88
  79. package/src/data/ArrayElementView.ts +91 -91
  80. package/src/data/ArrayRef.ts +34 -34
  81. package/src/data/AugmentedViewBase.ts +88 -88
  82. package/src/data/Binding.spec.ts +69 -69
  83. package/src/data/Binding.ts +107 -107
  84. package/src/data/ExposedRecordView.ts +95 -95
  85. package/src/data/ExposedValueView.ts +89 -89
  86. package/src/data/Expression.spec.ts +229 -229
  87. package/src/data/Expression.ts +233 -233
  88. package/src/data/Grouper.spec.ts +57 -57
  89. package/src/data/Grouper.ts +158 -158
  90. package/src/data/NestedDataView.ts +43 -43
  91. package/src/data/ReadOnlyDataView.ts +39 -39
  92. package/src/data/Ref.spec.ts +79 -79
  93. package/src/data/Ref.ts +104 -104
  94. package/src/data/Selector.ts +10 -10
  95. package/src/data/Store.spec.ts +22 -22
  96. package/src/data/Store.ts +52 -52
  97. package/src/data/StoreProxy.ts +19 -19
  98. package/src/data/StoreRef.spec.ts +24 -24
  99. package/src/data/StoreRef.ts +66 -66
  100. package/src/data/StringTemplate.spec.ts +132 -132
  101. package/src/data/StringTemplate.ts +93 -93
  102. package/src/data/StructuredSelector.spec.ts +113 -113
  103. package/src/data/StructuredSelector.ts +146 -146
  104. package/src/data/SubscribableView.ts +63 -63
  105. package/src/data/View.spec.ts +60 -60
  106. package/src/data/View.ts +346 -346
  107. package/src/data/ZoomIntoPropertyView.spec.ts +64 -64
  108. package/src/data/ZoomIntoPropertyView.ts +45 -45
  109. package/src/data/comparer.spec.ts +60 -60
  110. package/src/data/comparer.ts +78 -78
  111. package/src/data/computable.spec.ts +87 -87
  112. package/src/data/computable.ts +69 -69
  113. package/src/data/createAccessorModelProxy.spec.tsx +157 -157
  114. package/src/data/createAccessorModelProxy.ts +66 -66
  115. package/src/data/createStructuredSelector.spec.ts +45 -45
  116. package/src/data/createStructuredSelector.ts +62 -62
  117. package/src/data/defaultCompare.ts +14 -14
  118. package/src/data/diff/diffArrays.ts +49 -49
  119. package/src/data/diff/diffs.spec.ts +49 -49
  120. package/src/data/diff/index.ts +1 -1
  121. package/src/data/enableFatArrowExpansion.ts +6 -6
  122. package/src/data/getAccessor.spec.ts +11 -11
  123. package/src/data/getAccessor.ts +74 -74
  124. package/src/data/getSelector.spec.ts +43 -43
  125. package/src/data/getSelector.ts +66 -66
  126. package/src/data/index.ts +30 -30
  127. package/src/data/isSelector.ts +26 -26
  128. package/src/data/ops/append.spec.ts +28 -28
  129. package/src/data/ops/append.ts +5 -5
  130. package/src/data/ops/filter.spec.ts +35 -35
  131. package/src/data/ops/filter.ts +9 -9
  132. package/src/data/ops/findTreeNode.spec.ts +23 -23
  133. package/src/data/ops/findTreeNode.ts +33 -33
  134. package/src/data/ops/findTreePath.ts +28 -28
  135. package/src/data/ops/index.ts +11 -11
  136. package/src/data/ops/insertElement.ts +3 -3
  137. package/src/data/ops/merge.spec.ts +27 -27
  138. package/src/data/ops/merge.ts +13 -13
  139. package/src/data/ops/moveElement.ts +21 -21
  140. package/src/data/ops/removeTreeNodes.spec.ts +37 -37
  141. package/src/data/ops/removeTreeNodes.ts +28 -28
  142. package/src/data/ops/updateArray.spec.ts +69 -69
  143. package/src/data/ops/updateArray.ts +31 -31
  144. package/src/data/ops/updateTree.spec.ts +54 -54
  145. package/src/data/ops/updateTree.ts +49 -49
  146. package/src/data/test-types.ts +7 -7
  147. package/src/hooks/createLocalStorageRef.ts +21 -21
  148. package/src/hooks/index.ts +8 -8
  149. package/src/hooks/invokeCallback.spec.tsx +59 -59
  150. package/src/hooks/invokeCallback.ts +8 -8
  151. package/src/hooks/resolveCallback.spec.tsx +71 -71
  152. package/src/hooks/resolveCallback.ts +16 -16
  153. package/src/hooks/store.spec.tsx +67 -67
  154. package/src/hooks/store.ts +46 -46
  155. package/src/hooks/useEffect.ts +14 -14
  156. package/src/hooks/useInterval.ts +8 -8
  157. package/src/hooks/useState.ts +20 -20
  158. package/src/hooks/useTrigger.spec.tsx +105 -105
  159. package/src/hooks/useTrigger.ts +26 -26
  160. package/src/index.scss +5 -5
  161. package/src/index.ts +7 -7
  162. package/src/jsx-dev-runtime.ts +4 -4
  163. package/src/jsx-runtime.spec.tsx +508 -508
  164. package/src/jsx-runtime.ts +88 -88
  165. package/src/locale/de-de.ts +76 -76
  166. package/src/locale/en-us.ts +75 -75
  167. package/src/locale/es-es.ts +76 -76
  168. package/src/locale/fr-fr.ts +76 -76
  169. package/src/locale/nl-nl.ts +76 -76
  170. package/src/locale/pt-pt.ts +76 -76
  171. package/src/locale/sr-latn-ba.ts +76 -76
  172. package/src/maps.scss +5 -5
  173. package/src/svg/BoundedObject.ts +101 -101
  174. package/src/svg/ClipRect.tsx +29 -29
  175. package/src/svg/Ellipse.tsx +76 -76
  176. package/src/svg/Line.tsx +73 -73
  177. package/src/svg/NonOverlappingRect.ts +26 -26
  178. package/src/svg/NonOverlappingRectGroup.ts +49 -49
  179. package/src/svg/Rectangle.tsx +98 -98
  180. package/src/svg/Svg.scss +28 -28
  181. package/src/svg/Svg.tsx +241 -241
  182. package/src/svg/Text.scss +19 -19
  183. package/src/svg/Text.tsx +190 -190
  184. package/src/svg/TextualBoundedObject.ts +35 -35
  185. package/src/svg/index.scss +10 -10
  186. package/src/svg/index.ts +17 -17
  187. package/src/svg/util/Rect.ts +105 -105
  188. package/src/ui/CSS.ts +87 -87
  189. package/src/ui/CSSHelper.ts +17 -17
  190. package/src/ui/Container.spec.ts +87 -87
  191. package/src/ui/Container.tsx +220 -220
  192. package/src/ui/ContentResolver.spec.tsx +585 -585
  193. package/src/ui/ContentResolver.ts +132 -132
  194. package/src/ui/Controller.spec.tsx +604 -604
  195. package/src/ui/Controller.ts +207 -207
  196. package/src/ui/Culture.ts +159 -159
  197. package/src/ui/Cx.spec.tsx +210 -210
  198. package/src/ui/Cx.tsx +386 -386
  199. package/src/ui/DataProxy.spec.tsx +337 -337
  200. package/src/ui/DetachedScope.tsx +159 -159
  201. package/src/ui/FocusManager.ts +171 -171
  202. package/src/ui/Format.ts +108 -108
  203. package/src/ui/HoverSync.tsx +221 -221
  204. package/src/ui/Instance.ts +963 -963
  205. package/src/ui/IsolatedScope.spec.tsx +62 -62
  206. package/src/ui/IsolatedScope.ts +52 -52
  207. package/src/ui/Localization.ts +70 -70
  208. package/src/ui/Prop.ts +141 -141
  209. package/src/ui/PureContainer.spec.tsx +232 -232
  210. package/src/ui/PureContainer.tsx +19 -19
  211. package/src/ui/RenderingContext.ts +99 -99
  212. package/src/ui/Repeater.spec.tsx +181 -181
  213. package/src/ui/Repeater.ts +194 -194
  214. package/src/ui/Rescope.spec.tsx +199 -199
  215. package/src/ui/ResizeManager.ts +30 -30
  216. package/src/ui/Restate.spec.tsx +422 -422
  217. package/src/ui/Restate.tsx +217 -217
  218. package/src/ui/StaticText.ts +11 -11
  219. package/src/ui/StructuredInstanceDataAccessor.ts +32 -32
  220. package/src/ui/Text.ts +52 -52
  221. package/src/ui/VDOM.ts +1 -1
  222. package/src/ui/Widget.spec.tsx +53 -53
  223. package/src/ui/Widget.tsx +359 -359
  224. package/src/ui/ZIndexManager.ts +11 -11
  225. package/src/ui/adapter/ArrayAdapter.spec.ts +55 -55
  226. package/src/ui/adapter/DataAdapter.ts +52 -52
  227. package/src/ui/adapter/GroupAdapter.spec.ts +42 -42
  228. package/src/ui/adapter/GroupAdapter.ts +244 -244
  229. package/src/ui/adapter/TreeAdapter.spec.ts +76 -76
  230. package/src/ui/adapter/TreeAdapter.ts +245 -245
  231. package/src/ui/adapter/index.ts +4 -4
  232. package/src/ui/app/History.ts +147 -147
  233. package/src/ui/app/Url.spec.ts +50 -50
  234. package/src/ui/app/Url.ts +102 -102
  235. package/src/ui/app/index.ts +5 -5
  236. package/src/ui/app/startAppLoop.tsx +82 -82
  237. package/src/ui/app/startHotAppLoop.ts +41 -41
  238. package/src/ui/batchUpdates.ts +72 -72
  239. package/src/ui/bind.ts +10 -10
  240. package/src/ui/createFunctionalComponent.spec.tsx +605 -605
  241. package/src/ui/createFunctionalComponent.ts +96 -96
  242. package/src/ui/expr.ts +47 -47
  243. package/src/ui/exprHelpers.spec.ts +412 -412
  244. package/src/ui/flattenProps.ts +21 -21
  245. package/src/ui/index.scss +2 -2
  246. package/src/ui/index.ts +51 -51
  247. package/src/ui/keyboardShortcuts.ts +50 -50
  248. package/src/ui/layout/Content.ts +30 -30
  249. package/src/ui/layout/ContentPlaceholder.spec.tsx +599 -599
  250. package/src/ui/layout/ContentPlaceholder.ts +133 -133
  251. package/src/ui/layout/FirstVisibleChildLayout.spec.tsx +207 -207
  252. package/src/ui/layout/FirstVisibleChildLayout.ts +60 -60
  253. package/src/ui/layout/LabelsLeftLayout.scss +45 -45
  254. package/src/ui/layout/LabelsLeftLayout.tsx +76 -76
  255. package/src/ui/layout/LabelsTopLayout.scss +73 -73
  256. package/src/ui/layout/LabelsTopLayout.tsx +156 -156
  257. package/src/ui/layout/UseParentLayout.ts +8 -8
  258. package/src/ui/layout/exploreChildren.ts +38 -38
  259. package/src/ui/layout/index.scss +2 -2
  260. package/src/ui/layout/index.ts +10 -10
  261. package/src/ui/layout/variables.scss +2 -2
  262. package/src/ui/selection/KeySelection.ts +153 -153
  263. package/src/ui/selection/PropertySelection.ts +87 -87
  264. package/src/ui/selection/Selection.ts +128 -128
  265. package/src/ui/selection/index.ts +3 -3
  266. package/src/ui/tpl.spec.ts +69 -69
  267. package/src/ui/tpl.ts +17 -17
  268. package/src/ui/variables.scss +1 -1
  269. package/src/util/Component.spec.ts +494 -494
  270. package/src/util/Component.ts +352 -352
  271. package/src/util/Console.ts +13 -13
  272. package/src/util/DOM.ts +88 -88
  273. package/src/util/Debug.ts +71 -71
  274. package/src/util/Format.spec.ts +69 -69
  275. package/src/util/Format.ts +267 -267
  276. package/src/util/GlobalCacheIdentifier.ts +11 -11
  277. package/src/util/KeyCode.ts +21 -21
  278. package/src/util/SubscriberList.ts +86 -86
  279. package/src/util/Timing.ts +58 -58
  280. package/src/util/TraversalStack.spec.ts +53 -53
  281. package/src/util/TraversalStack.ts +47 -47
  282. package/src/util/addEventListenerWithOptions.ts +41 -41
  283. package/src/util/browserSupportsPassiveEventHandlers.ts +20 -20
  284. package/src/util/calculateNaturalElementHeight.ts +22 -22
  285. package/src/util/call-once.scss +6 -6
  286. package/src/util/capitalize.ts +4 -4
  287. package/src/util/coalesce.ts +6 -6
  288. package/src/util/color/hslToRgb.ts +34 -34
  289. package/src/util/color/index.ts +4 -4
  290. package/src/util/color/parseColor.ts +173 -173
  291. package/src/util/color/rgbToHex.ts +14 -14
  292. package/src/util/color/rgbToHsl.ts +35 -35
  293. package/src/util/date/dateDiff.ts +9 -9
  294. package/src/util/date/diff.ts +13 -13
  295. package/src/util/date/encodeDate.ts +8 -8
  296. package/src/util/date/encodeDateWithTimezoneOffset.ts +18 -18
  297. package/src/util/date/index.ts +11 -11
  298. package/src/util/date/lowerBoundCheck.ts +13 -13
  299. package/src/util/date/maxDate.ts +14 -14
  300. package/src/util/date/minDate.ts +14 -14
  301. package/src/util/date/monthStart.ts +8 -8
  302. package/src/util/date/parseDateInvariant.ts +20 -20
  303. package/src/util/date/sameDate.ts +11 -11
  304. package/src/util/date/upperBoundCheck.spec.ts +30 -30
  305. package/src/util/date/upperBoundCheck.ts +13 -13
  306. package/src/util/date/zeroTime.ts +9 -9
  307. package/src/util/debounce.ts +26 -26
  308. package/src/util/dummyCallback.ts +1 -1
  309. package/src/util/escapeSpecialRegexCharacters.ts +8 -8
  310. package/src/util/eventCallbacks.ts +12 -12
  311. package/src/util/expandFatArrows.ts +118 -118
  312. package/src/util/findScrollableParent.ts +15 -15
  313. package/src/util/getActiveElement.ts +4 -4
  314. package/src/util/getParentFrameBoundingClientRect.ts +13 -13
  315. package/src/util/getScrollerBoundingClientRect.ts +12 -12
  316. package/src/util/getSearchQueryPredicate.spec.ts +40 -40
  317. package/src/util/getSearchQueryPredicate.ts +58 -58
  318. package/src/util/getTopLevelBoundingClientRect.ts +7 -7
  319. package/src/util/getVendorPrefix.ts +38 -38
  320. package/src/util/hasKey.ts +18 -18
  321. package/src/util/index.scss +13 -13
  322. package/src/util/index.ts +55 -55
  323. package/src/util/innerTextTrim.ts +10 -10
  324. package/src/util/isArray.ts +3 -3
  325. package/src/util/isDataRecord.ts +5 -5
  326. package/src/util/isDefined.ts +3 -3
  327. package/src/util/isDigit.ts +8 -8
  328. package/src/util/isFunction.ts +3 -3
  329. package/src/util/isNonEmptyArray.ts +3 -3
  330. package/src/util/isNumber.ts +3 -3
  331. package/src/util/isObject.ts +3 -3
  332. package/src/util/isPromise.ts +6 -6
  333. package/src/util/isString.ts +3 -3
  334. package/src/util/isTextInputElement.ts +2 -2
  335. package/src/util/isTouchDevice.ts +7 -7
  336. package/src/util/isTouchEvent.ts +64 -64
  337. package/src/util/isUndefined.ts +3 -3
  338. package/src/util/isValidIdentifierName.spec.ts +33 -33
  339. package/src/util/isValidIdentifierName.ts +5 -5
  340. package/src/util/maps.scss +1 -1
  341. package/src/util/onIdleCallback.ts +10 -10
  342. package/src/util/parseStyle.ts +29 -29
  343. package/src/util/quote.ts +6 -6
  344. package/src/util/reverseSlice.ts +9 -9
  345. package/src/util/routeAppend.spec.ts +19 -19
  346. package/src/util/routeAppend.ts +15 -15
  347. package/src/util/scrollElementIntoView.ts +40 -40
  348. package/src/util/scss/add-rules.scss +40 -40
  349. package/src/util/scss/besm.scss +15 -15
  350. package/src/util/scss/calc.scss +137 -137
  351. package/src/util/scss/call-once.scss +12 -12
  352. package/src/util/scss/clockwise.scss +49 -49
  353. package/src/util/scss/colors.scss +9 -9
  354. package/src/util/scss/deep-get.scss +11 -11
  355. package/src/util/scss/deep-merge.scss +21 -21
  356. package/src/util/scss/defaults.scss +24 -24
  357. package/src/util/scss/divide.scss +3 -3
  358. package/src/util/scss/elements.scss +78 -78
  359. package/src/util/scss/global.scss +15 -15
  360. package/src/util/scss/include.scss +69 -69
  361. package/src/util/scss/index.scss +1 -1
  362. package/src/util/scss/maps.scss +2 -2
  363. package/src/util/scss/pad-size.scss +9 -9
  364. package/src/util/scss/padding.scss +6 -6
  365. package/src/util/scss/screen-size.scss +5 -5
  366. package/src/util/scss/variables.scss +6 -6
  367. package/src/util/shallowEquals.ts +43 -43
  368. package/src/util/test/createTestRenderer.tsx +19 -19
  369. package/src/util/throttle.ts +21 -21
  370. package/src/util/validatedDebounce.ts +23 -23
  371. package/src/util/variables.scss +1 -1
  372. package/src/variables.scss +5 -5
  373. package/src/widgets/AccessorBindings.spec.tsx +90 -90
  374. package/src/widgets/Button.maps.scss +103 -103
  375. package/src/widgets/Button.scss +143 -143
  376. package/src/widgets/Button.tsx +181 -181
  377. package/src/widgets/Button.variables.scss +21 -21
  378. package/src/widgets/CxCredit.scss +41 -41
  379. package/src/widgets/CxCredit.tsx +46 -46
  380. package/src/widgets/DocumentTitle.ts +95 -95
  381. package/src/widgets/FlexBox.scss +153 -153
  382. package/src/widgets/FlexBox.tsx +168 -168
  383. package/src/widgets/Heading.scss +46 -46
  384. package/src/widgets/Heading.ts +42 -42
  385. package/src/widgets/HighlightedSearchText.scss +27 -27
  386. package/src/widgets/HighlightedSearchText.tsx +54 -54
  387. package/src/widgets/HtmlElement.spec.helpers.tsx +108 -108
  388. package/src/widgets/HtmlElement.spec.tsx +117 -117
  389. package/src/widgets/HtmlElement.tsx +468 -468
  390. package/src/widgets/Icon.scss +28 -28
  391. package/src/widgets/Icon.ts +64 -64
  392. package/src/widgets/List.scss +100 -100
  393. package/src/widgets/List.tsx +792 -792
  394. package/src/widgets/ProgressBar.scss +64 -64
  395. package/src/widgets/ProgressBar.tsx +66 -66
  396. package/src/widgets/ReactElementWrapper.spec.tsx +452 -452
  397. package/src/widgets/ReactElementWrapper.tsx +108 -108
  398. package/src/widgets/Resizer.scss +46 -46
  399. package/src/widgets/Resizer.tsx +200 -200
  400. package/src/widgets/Section.scss +53 -53
  401. package/src/widgets/Section.tsx +187 -187
  402. package/src/widgets/animations.scss +13 -13
  403. package/src/widgets/autoFocus.ts +9 -9
  404. package/src/widgets/box.scss +47 -47
  405. package/src/widgets/cx.ts +63 -63
  406. package/src/widgets/drag-drop/DragClone.scss +44 -44
  407. package/src/widgets/drag-drop/DragHandle.scss +25 -25
  408. package/src/widgets/drag-drop/DragHandle.tsx +47 -47
  409. package/src/widgets/drag-drop/DragSource.scss +33 -33
  410. package/src/widgets/drag-drop/DragSource.tsx +237 -237
  411. package/src/widgets/drag-drop/DropZone.scss +83 -83
  412. package/src/widgets/drag-drop/DropZone.tsx +353 -353
  413. package/src/widgets/drag-drop/index.scss +4 -4
  414. package/src/widgets/drag-drop/index.ts +4 -4
  415. package/src/widgets/drag-drop/maps.scss +7 -7
  416. package/src/widgets/drag-drop/ops.tsx +422 -422
  417. package/src/widgets/drag-drop/variables.scss +17 -17
  418. package/src/widgets/enableAllInternalDependencies.ts +11 -11
  419. package/src/widgets/form/Calendar.maps.scss +54 -54
  420. package/src/widgets/form/Calendar.scss +237 -237
  421. package/src/widgets/form/Calendar.tsx +772 -772
  422. package/src/widgets/form/Calendar.variables.scss +21 -21
  423. package/src/widgets/form/Checkbox.maps.scss +34 -34
  424. package/src/widgets/form/Checkbox.scss +140 -140
  425. package/src/widgets/form/Checkbox.tsx +340 -340
  426. package/src/widgets/form/Checkbox.variables.scss +8 -8
  427. package/src/widgets/form/ColorField.tsx +551 -551
  428. package/src/widgets/form/ColorPicker.maps.scss +21 -21
  429. package/src/widgets/form/ColorPicker.scss +302 -302
  430. package/src/widgets/form/ColorPicker.tsx +606 -606
  431. package/src/widgets/form/ColorPicker.variables.scss +9 -9
  432. package/src/widgets/form/DateField.ts +21 -21
  433. package/src/widgets/form/DateTimeField.tsx +814 -814
  434. package/src/widgets/form/DateTimePicker.scss +57 -57
  435. package/src/widgets/form/DateTimePicker.tsx +429 -429
  436. package/src/widgets/form/Field.maps.scss +122 -122
  437. package/src/widgets/form/Field.scss +200 -200
  438. package/src/widgets/form/Field.tsx +715 -715
  439. package/src/widgets/form/Field.variables.scss +46 -46
  440. package/src/widgets/form/FieldGroup.ts +10 -10
  441. package/src/widgets/form/FieldIcon.ts +61 -61
  442. package/src/widgets/form/HelpText.scss +27 -27
  443. package/src/widgets/form/HelpText.ts +15 -15
  444. package/src/widgets/form/Label.scss +45 -45
  445. package/src/widgets/form/Label.tsx +117 -117
  446. package/src/widgets/form/LabeledContainer.ts +77 -77
  447. package/src/widgets/form/LookupField.scss +227 -227
  448. package/src/widgets/form/LookupField.spec.tsx +93 -93
  449. package/src/widgets/form/LookupField.tsx +1689 -1689
  450. package/src/widgets/form/MonthField.tsx +748 -748
  451. package/src/widgets/form/MonthPicker.maps.scss +50 -50
  452. package/src/widgets/form/MonthPicker.scss +134 -134
  453. package/src/widgets/form/MonthPicker.tsx +1061 -1061
  454. package/src/widgets/form/MonthPicker.variables.scss +24 -24
  455. package/src/widgets/form/NumberField.tsx +653 -653
  456. package/src/widgets/form/Radio.maps.scss +36 -36
  457. package/src/widgets/form/Radio.scss +133 -133
  458. package/src/widgets/form/Radio.tsx +249 -249
  459. package/src/widgets/form/Radio.variables.scss +7 -7
  460. package/src/widgets/form/Select.tsx +327 -327
  461. package/src/widgets/form/Slider.scss +130 -130
  462. package/src/widgets/form/Slider.tsx +473 -473
  463. package/src/widgets/form/Switch.scss +152 -152
  464. package/src/widgets/form/Switch.tsx +176 -176
  465. package/src/widgets/form/TextArea.scss +58 -58
  466. package/src/widgets/form/TextArea.tsx +229 -229
  467. package/src/widgets/form/TextField.tsx +453 -453
  468. package/src/widgets/form/TimeField.ts +10 -10
  469. package/src/widgets/form/TimeList.tsx +105 -105
  470. package/src/widgets/form/UploadButton.scss +57 -57
  471. package/src/widgets/form/UploadButton.tsx +387 -387
  472. package/src/widgets/form/ValidationError.scss +27 -27
  473. package/src/widgets/form/ValidationError.tsx +81 -81
  474. package/src/widgets/form/ValidationGroup.spec.tsx +147 -147
  475. package/src/widgets/form/ValidationGroup.ts +141 -141
  476. package/src/widgets/form/Validator.ts +60 -60
  477. package/src/widgets/form/Wheel.scss +164 -164
  478. package/src/widgets/form/Wheel.tsx +309 -309
  479. package/src/widgets/form/index.scss +22 -22
  480. package/src/widgets/form/index.ts +28 -28
  481. package/src/widgets/form/maps.scss +81 -81
  482. package/src/widgets/form/variables.scss +114 -114
  483. package/src/widgets/grid/Grid.scss +657 -657
  484. package/src/widgets/grid/Grid.spec.ts +42 -42
  485. package/src/widgets/grid/Grid.tsx +4269 -4269
  486. package/src/widgets/grid/GridCell.ts +143 -143
  487. package/src/widgets/grid/GridCellEditor.tsx +58 -58
  488. package/src/widgets/grid/GridRow.ts +302 -302
  489. package/src/widgets/grid/GridRowLine.ts +49 -49
  490. package/src/widgets/grid/Pagination.scss +124 -124
  491. package/src/widgets/grid/Pagination.tsx +134 -134
  492. package/src/widgets/grid/TreeNode.scss +107 -107
  493. package/src/widgets/grid/TreeNode.tsx +165 -165
  494. package/src/widgets/grid/createGridCellEditor.tsx +18 -18
  495. package/src/widgets/grid/index.scss +3 -3
  496. package/src/widgets/grid/index.ts +16 -16
  497. package/src/widgets/grid/maps.scss +110 -110
  498. package/src/widgets/grid/variables.scss +47 -47
  499. package/src/widgets/icons/arrow-down.svg +3 -3
  500. package/src/widgets/icons/arrow-right.svg +2 -2
  501. package/src/widgets/icons/base.svg +104 -104
  502. package/src/widgets/icons/calendar-old.svg +169 -169
  503. package/src/widgets/icons/calendar.svg +187 -187
  504. package/src/widgets/icons/calendar.tsx +22 -22
  505. package/src/widgets/icons/check.tsx +14 -14
  506. package/src/widgets/icons/clear.svg +74 -74
  507. package/src/widgets/icons/clear.tsx +16 -16
  508. package/src/widgets/icons/close.svg +74 -74
  509. package/src/widgets/icons/close.tsx +20 -20
  510. package/src/widgets/icons/cx.tsx +39 -39
  511. package/src/widgets/icons/drop-down.tsx +16 -16
  512. package/src/widgets/icons/dropdown-arrow.svg +61 -61
  513. package/src/widgets/icons/file.svg +4 -4
  514. package/src/widgets/icons/file.tsx +14 -14
  515. package/src/widgets/icons/folder-open.svg +5 -5
  516. package/src/widgets/icons/folder-open.tsx +16 -16
  517. package/src/widgets/icons/folder.svg +58 -58
  518. package/src/widgets/icons/folder.tsx +13 -13
  519. package/src/widgets/icons/forward.svg +67 -67
  520. package/src/widgets/icons/forward.tsx +26 -26
  521. package/src/widgets/icons/index.ts +14 -14
  522. package/src/widgets/icons/loading.svg +4 -4
  523. package/src/widgets/icons/loading.tsx +28 -28
  524. package/src/widgets/icons/menu.tsx +18 -18
  525. package/src/widgets/icons/pixel-picker.tsx +18 -18
  526. package/src/widgets/icons/registry.ts +56 -56
  527. package/src/widgets/icons/search.svg +107 -107
  528. package/src/widgets/icons/search.tsx +14 -14
  529. package/src/widgets/icons/sort-asc.svg +3 -3
  530. package/src/widgets/icons/sort-asc.tsx +15 -15
  531. package/src/widgets/icons/square.tsx +22 -22
  532. package/src/widgets/index.d.ts +55 -55
  533. package/src/widgets/index.scss +16 -16
  534. package/src/widgets/index.ts +63 -63
  535. package/src/widgets/lists.scss +42 -42
  536. package/src/widgets/maps.scss +139 -139
  537. package/src/widgets/nav/Link.scss +33 -33
  538. package/src/widgets/nav/Link.ts +11 -11
  539. package/src/widgets/nav/LinkButton.ts +176 -176
  540. package/src/widgets/nav/Menu.scss +82 -82
  541. package/src/widgets/nav/Menu.tsx +497 -497
  542. package/src/widgets/nav/Menu.variables.scss +13 -13
  543. package/src/widgets/nav/MenuItem.scss +150 -150
  544. package/src/widgets/nav/MenuSpacer.ts +18 -18
  545. package/src/widgets/nav/RedirectRoute.ts +49 -49
  546. package/src/widgets/nav/Route.spec.tsx +24 -24
  547. package/src/widgets/nav/Route.ts +142 -142
  548. package/src/widgets/nav/Scroller.scss +157 -157
  549. package/src/widgets/nav/Scroller.tsx +252 -252
  550. package/src/widgets/nav/Submenu.ts +6 -6
  551. package/src/widgets/nav/Tab.maps.scss +78 -78
  552. package/src/widgets/nav/Tab.scss +88 -88
  553. package/src/widgets/nav/Tab.ts +122 -122
  554. package/src/widgets/nav/Tab.variables.scss +15 -15
  555. package/src/widgets/nav/cover.scss +24 -24
  556. package/src/widgets/nav/index.scss +6 -6
  557. package/src/widgets/nav/index.ts +10 -10
  558. package/src/widgets/nav/maps.scss +32 -32
  559. package/src/widgets/nav/variables.scss +19 -19
  560. package/src/widgets/overlay/ContextMenu.ts +45 -45
  561. package/src/widgets/overlay/Dropdown.scss +217 -217
  562. package/src/widgets/overlay/Dropdown.tsx +937 -937
  563. package/src/widgets/overlay/FlyweightTooltipTracker.ts +91 -91
  564. package/src/widgets/overlay/MsgBox.tsx +155 -155
  565. package/src/widgets/overlay/Overlay.scss +88 -88
  566. package/src/widgets/overlay/Overlay.tsx +1028 -1028
  567. package/src/widgets/overlay/Toast.scss +172 -172
  568. package/src/widgets/overlay/Toast.ts +121 -121
  569. package/src/widgets/overlay/Tooltip.scss +108 -108
  570. package/src/widgets/overlay/Tooltip.tsx +460 -460
  571. package/src/widgets/overlay/Window.maps.scss +51 -51
  572. package/src/widgets/overlay/Window.scss +129 -129
  573. package/src/widgets/overlay/Window.tsx +320 -320
  574. package/src/widgets/overlay/Window.variables.scss +21 -21
  575. package/src/widgets/overlay/alerts.ts +46 -46
  576. package/src/widgets/overlay/captureMouse.scss +13 -13
  577. package/src/widgets/overlay/captureMouse.ts +195 -195
  578. package/src/widgets/overlay/createHotPromiseWindowFactory.ts +71 -71
  579. package/src/widgets/overlay/index.d.ts +11 -11
  580. package/src/widgets/overlay/index.scss +6 -6
  581. package/src/widgets/overlay/index.ts +11 -11
  582. package/src/widgets/overlay/maps.scss +44 -44
  583. package/src/widgets/overlay/tooltip-ops.ts +173 -173
  584. package/src/widgets/overlay/variables.scss +53 -53
  585. package/src/widgets/variables.scss +61 -61
  586. package/tsconfig.compile.json +4 -4
  587. package/tsconfig.json +35 -35
  588. package/tsconfig.mocha.json +14 -14
  589. package/build/widgets/form/Test.d.ts +0 -4
  590. package/build/widgets/form/Test.d.ts.map +0 -1
  591. package/build/widgets/form/Test.js +0 -5
@@ -1,963 +1,963 @@
1
- import { isAccessorChain } from "../data/createAccessorModelProxy";
2
- import { View } from "../data/View";
3
- import { debug, destroyFlag, processDataFlag, renderFlag } from "../util/Debug";
4
- import { GlobalCacheIdentifier } from "../util/GlobalCacheIdentifier";
5
- import { isArray } from "../util/isArray";
6
- import { isDefined } from "../util/isDefined";
7
- import { isFunction } from "../util/isFunction";
8
- import { isNonEmptyArray } from "../util/isNonEmptyArray";
9
- import { isObject } from "../util/isObject";
10
- import { isString } from "../util/isString";
11
- import { isUndefined } from "../util/isUndefined";
12
- import { throttle } from "../util/throttle";
13
- import { validatedDebounce } from "../util/validatedDebounce";
14
- import { batchUpdates } from "./batchUpdates";
15
- import { Controller } from "./Controller";
16
- import { RenderingContext } from "./RenderingContext";
17
- import type { Widget } from "./Widget";
18
-
19
- /**
20
- * Serializable value types that can be safely passed through the framework
21
- */
22
- export type SerializableValue =
23
- | string
24
- | number
25
- | boolean
26
- | object
27
- | null
28
- | undefined;
29
-
30
- /**
31
- * Core instance data structure used by widgets
32
- *
33
- * For custom widget data, extend this interface:
34
- * @example
35
- * interface MyWidgetData extends WidgetData {
36
- * customProp: string;
37
- * }
38
- */
39
- export interface WidgetData {
40
- visible?: boolean;
41
- disabled?: boolean;
42
- enabled?: boolean;
43
- text?: string;
44
- innerHtml?: string;
45
- attrs?: Record<string, unknown>;
46
- data?: Record<string, unknown>;
47
- classNames?: string;
48
- className?: string;
49
- class?: string;
50
- style?: Record<string, string | number> | string;
51
- stateMods?: Record<string, boolean | undefined>;
52
- mod?: Record<string, boolean | undefined>;
53
- pressed?: boolean;
54
- icon?: string | boolean;
55
- confirm?: string | ConfirmConfig;
56
- parentDisabled?: boolean;
57
- parentStrict?: boolean;
58
- error?: string;
59
- }
60
-
61
- /**
62
- * Confirmation dialog configuration
63
- */
64
- export interface ConfirmConfig {
65
- message: string;
66
- title?: string;
67
- yesText?: string;
68
- noText?: string;
69
- }
70
-
71
- /**
72
- * Parent options passed down from overlay/modal components
73
- *
74
- * For custom parent options, extend this interface:
75
- * @example
76
- * interface MyParentOptions extends ParentOptions {
77
- * customOption: string;
78
- * }
79
- */
80
- export interface ParentOptions {
81
- dismiss?: () => void;
82
- }
83
-
84
- /**
85
- * Props object used for rendering (passed to React.createElement)
86
- */
87
- export type RenderProps = Record<string, any>;
88
-
89
- /**
90
- * Result type for yes/no dialogs
91
- */
92
- export const enum YesNoResult {
93
- Yes = "yes",
94
- No = "no",
95
- }
96
-
97
- /**
98
- * Partial instance-like object used in some render methods
99
- * This is a hack to allow passing only the data property instead of full Instance
100
- */
101
- export interface PartialInstance {
102
- data: Record<string, any>;
103
- widget?: Widget;
104
- state?: Record<string, any>;
105
- }
106
-
107
- /**
108
- * Example widget-specific properties for dropdown widgets
109
- */
110
- export interface DropdownWidgetProps {
111
- lastDropdown?: Instance;
112
- dropdownOpen?: boolean;
113
- selectedIndex?: number;
114
- }
115
-
116
- /**
117
- * Example widget-specific properties for form field widgets
118
- */
119
- export interface FieldWidgetProps {
120
- inputElement?: HTMLInputElement;
121
- validationState?: "valid" | "invalid" | "pending";
122
- }
123
-
124
- /**
125
- * Type aliases for common widget instance types using intersection types
126
- */
127
- export type DropdownInstance = Instance & DropdownWidgetProps;
128
- export type FieldInstance = Instance & FieldWidgetProps;
129
-
130
- let instanceId = 1000;
131
-
132
- /**
133
- * Base Instance class
134
- */
135
- export class Instance<WidgetType extends Widget<any, any> = Widget<any, any>> {
136
- // Core properties
137
- declare public widget: WidgetType;
138
- declare public key: string;
139
- declare public id: string;
140
- declare public parent?: Instance;
141
- declare public parentStore: View;
142
- declare public store: View;
143
- declare public controller?: Controller;
144
-
145
- // Data and state
146
- declare public data: Record<string, any>;
147
- declare public rawData: Record<string, any>;
148
- declare public state?: Record<string, any>;
149
- declare public cached: Record<string, any>;
150
- declare public cacheList?: Record<string, any> | null;
151
-
152
- // Selectors
153
- public dataSelector?: (store: any) => Record<string, any>;
154
-
155
- // Lifecycle flags
156
- public initialized?: boolean;
157
- declare public visible?: boolean;
158
- declare public explored?: boolean;
159
- declare public prepared?: boolean;
160
- declare public shouldUpdate?: boolean;
161
- declare public childStateDirty?: boolean;
162
- declare public detached?: boolean;
163
-
164
- // Cleanup tracking
165
- declare public needsExploreCleanup?: boolean;
166
- declare public needsPrepare?: boolean;
167
- declare public needsCleanup?: boolean;
168
- declare public destroyTracked?: boolean;
169
- public destroySubscriptions?: Array<() => void> | null;
170
-
171
- // Child management
172
- declare public instanceCache?: InstanceCache | null;
173
- declare public children?: Instance[];
174
- public helpers?: Record<string, Instance>;
175
- public components?: Record<string, Instance>;
176
-
177
- // Rendering
178
- declare public vdom?: any;
179
- declare public contentVDOM?: any;
180
- declare public renderList?: any;
181
- declare public assignedRenderList?: any;
182
-
183
- // Layout
184
- declare public outerLayout?: Instance;
185
- declare public contentPlaceholder?: any;
186
- declare public parentOptions?: any;
187
-
188
- // Setters
189
- declare public setters?: Record<string, any>;
190
-
191
- // Other
192
- declare public record?: any;
193
- declare public mappedRecords?: any[];
194
-
195
- // List-specific
196
- declare public instances?: Instance[];
197
- declare public selected?: boolean;
198
-
199
- constructor(
200
- widget: WidgetType,
201
- key: string,
202
- parent?: Instance,
203
- parentStore?: any,
204
- ) {
205
- this.widget = widget;
206
- this.key = key;
207
- this.id = String(++instanceId);
208
- this.cached = {};
209
- this.parent = parent;
210
- this.parentStore = parentStore ?? parent?.store;
211
-
212
- if (this.parentStore == null)
213
- throw new Error("Cannot create instance without a parent store.");
214
- }
215
-
216
- public setParentStore(parentStore: any): void {
217
- this.parentStore = parentStore;
218
- this.widget.applyParentStore(this);
219
- }
220
-
221
- public init(context: RenderingContext): void {
222
- // widget is initialized when the first instance is initialized
223
- if (!this.widget.initialized) {
224
- this.widget.init();
225
- this.widget.initialized = true;
226
- }
227
-
228
- if (!this.dataSelector) {
229
- this.widget.selector!.init(this.parentStore);
230
- this.dataSelector = this.widget.selector!.createStoreSelector();
231
- }
232
-
233
- // init instance might change the store, so this must go before the controller initialization
234
- this.widget.initInstance(context, this);
235
-
236
- // initInstance can set the store, otherwise use parent store
237
- if (!this.store) this.store = this.parentStore;
238
- if (this.widget.onInit) this.widget.onInit(context, this);
239
-
240
- this.widget.initState(context, this);
241
-
242
- if (this.widget.controller)
243
- this.controller = Controller.create(this.widget.controller, {
244
- widget: this.widget,
245
- instance: this,
246
- store: this.store,
247
- });
248
-
249
- if (
250
- this.widget.exploreCleanup ||
251
- this.widget.outerLayout ||
252
- this.widget.isContent ||
253
- this.widget.controller ||
254
- this.widget.prepareCleanup
255
- )
256
- this.needsExploreCleanup = true;
257
- if (this.widget.prepare || this.widget.controller)
258
- this.needsPrepare = true;
259
- if (this.widget.cleanup || this.widget.controller)
260
- this.needsCleanup = true;
261
- this.initialized = true;
262
- }
263
-
264
- public checkVisible(context: RenderingContext): boolean {
265
- if (!this.initialized) this.init(context);
266
-
267
- const wasVisible = this.visible;
268
- this.rawData = this.dataSelector!(this.store);
269
- this.visible = this.widget.checkVisible(context, this, this.rawData);
270
- if (this.visible && !this.detached)
271
- this.parent!.instanceCache!.addChild(this);
272
- this.explored = false;
273
- this.prepared = false;
274
-
275
- if (!this.visible && wasVisible) this.destroy();
276
-
277
- return this.visible;
278
- }
279
-
280
- public scheduleExploreIfVisible(context: RenderingContext): boolean {
281
- if (this.checkVisible(context)) {
282
- context.exploreStack.push(this);
283
-
284
- if (this.needsExploreCleanup) context.exploreStack.push(this);
285
-
286
- return true;
287
- }
288
- return false;
289
- }
290
-
291
- public cache(key: string, value: any): boolean {
292
- const oldValue = this.cached[key];
293
- if (oldValue === value) return false;
294
-
295
- if (!this.cacheList) this.cacheList = {};
296
- this.cacheList[key] = value;
297
- return true;
298
- }
299
-
300
- public markShouldUpdate(context: RenderingContext): void {
301
- let ins: Instance | undefined = this;
302
- let renderList = this.renderList;
303
- renderList.markReverseIndex();
304
-
305
- //notify all parents that child state changed to bust up caching
306
- while (ins && !ins.shouldUpdate && ins.explored) {
307
- if (ins.renderList !== renderList) {
308
- renderList.reverse();
309
- renderList = ins.renderList;
310
- renderList.markReverseIndex();
311
- }
312
- ins.shouldUpdate = true;
313
- renderList.data.push(ins);
314
- ins = ins.widget.isContent
315
- ? ins.contentPlaceholder
316
- : ins.parent?.outerLayout === ins
317
- ? ins.parent?.parent
318
- : ins.parent;
319
- }
320
- renderList.reverse();
321
- }
322
-
323
- public explore(context: RenderingContext): void {
324
- if (!this.visible) throw new Error("Explore invisible!");
325
-
326
- if (this.explored) {
327
- if (this.widget.prepareCleanup) context.prepareList.push(this);
328
-
329
- if (this.widget.exploreCleanup)
330
- this.widget.exploreCleanup(context, this);
331
-
332
- if (this.parent?.outerLayout === this)
333
- context.popNamedValue("content", "body");
334
-
335
- if (this.widget.controller) context.pop("controller");
336
-
337
- return;
338
- }
339
-
340
- this.explored = true;
341
-
342
- if (this.needsPrepare) context.prepareList.push(this);
343
- else this.prepared = true;
344
-
345
- if (this.needsCleanup) context.cleanupList.push(this);
346
-
347
- if (this.instanceCache) this.instanceCache.mark();
348
-
349
- //controller may reconfigure the widget and need to go before shouldUpdate calculation
350
- this.parentOptions = context.parentOptions;
351
-
352
- if (!this.controller) {
353
- if (context.controller) this.controller = context.controller;
354
- else if (this.parent?.controller)
355
- this.controller = this.parent?.controller;
356
- }
357
-
358
- this.destroyTracked = false;
359
-
360
- if (this.controller) {
361
- if (this.widget.controller) {
362
- if (!this.controller.initialized) {
363
- this.controller.init(context);
364
- this.controller.initialized = true;
365
- }
366
- context.push("controller", this.controller);
367
- this.controller.explore(context);
368
- if (
369
- this.controller.onDestroy &&
370
- this.controller.widget == this.widget
371
- )
372
- this.trackDestroy();
373
- }
374
- }
375
-
376
- if (this.widget.onDestroy || isNonEmptyArray(this.destroySubscriptions))
377
- this.trackDestroy();
378
-
379
- this.renderList =
380
- this.assignedRenderList ||
381
- this.parent?.renderList ||
382
- context.getRootRenderList();
383
-
384
- let shouldUpdate =
385
- this.rawData !== this.cached.rawData ||
386
- this.state !== this.cached.state ||
387
- this.widget.version !== this.cached.widgetVersion ||
388
- this.cached.globalCacheIdentifier !== GlobalCacheIdentifier.get();
389
-
390
- if (shouldUpdate) {
391
- this.data = { ...this.rawData };
392
- this.widget.prepareData(context, this);
393
- debug(processDataFlag, this.widget);
394
- }
395
-
396
- //onExplore might set the outer layout
397
- if (this.widget.onExplore) this.widget.onExplore(context, this);
398
-
399
- if (this.parent?.outerLayout === this) {
400
- this.renderList = this.renderList.insertRight();
401
- context.pushNamedValue("content", "body", this.parent);
402
- }
403
-
404
- if (this.widget.outerLayout) {
405
- this.outerLayout = this.getChild(
406
- context,
407
- this.widget.outerLayout,
408
- null,
409
- this.store,
410
- );
411
- this.outerLayout.scheduleExploreIfVisible(context);
412
- this.renderList = this.renderList.insertLeft();
413
- }
414
-
415
- if (this.widget.isContent) {
416
- this.contentPlaceholder =
417
- context.contentPlaceholder &&
418
- context.contentPlaceholder[this.widget.putInto!];
419
- if (this.contentPlaceholder)
420
- context.contentPlaceholder[this.widget.putInto!](this);
421
- else {
422
- this.renderList = this.renderList.insertLeft();
423
- context.pushNamedValue("content", this.widget.putInto!, this);
424
- if (!context.contentList) context.contentList = {};
425
- let list = context.contentList[this.widget.putInto!];
426
- if (!list) list = context.contentList[this.widget.putInto!] = [];
427
- list.push(this);
428
- }
429
- }
430
-
431
- this.shouldUpdate = false;
432
- if (shouldUpdate || this.childStateDirty || !this.widget.memoize)
433
- this.markShouldUpdate(context);
434
-
435
- context.exploreStack.hop();
436
-
437
- if (this.widget.helpers) {
438
- this.helpers = {};
439
- for (let cmp in this.widget.helpers) {
440
- let helper = this.widget.helpers[cmp];
441
- if (helper) {
442
- let ins = this.getChild(context, helper);
443
- if (ins.scheduleExploreIfVisible(context))
444
- this.helpers[cmp] = ins;
445
- }
446
- }
447
- }
448
-
449
- //TODO: check do we need to pass data here?
450
- this.widget.explore(context, this, this.data);
451
- }
452
-
453
- public prepare(context: RenderingContext): void {
454
- if (!this.visible) throw new Error("Prepare invisible!");
455
-
456
- if (this.prepared) {
457
- if (this.widget.prepareCleanup)
458
- this.widget.prepareCleanup(context, this);
459
- return;
460
- }
461
-
462
- this.prepared = true;
463
- if (this.widget.prepare) this.widget.prepare(context, this);
464
-
465
- if (this.widget.controller && this.controller?.prepare)
466
- this.controller.prepare(context);
467
- }
468
-
469
- public render(
470
- context: RenderingContext,
471
- ): null | Record<string, React.ReactNode> | React.ReactNode[] {
472
- if (!this.visible) throw new Error("Render invisible!");
473
-
474
- if (this.shouldUpdate) {
475
- debug(renderFlag, this.widget, this.key);
476
- const vdom = renderResultFix(
477
- this.widget.render(context, this, this.key),
478
- );
479
- if (this.widget.isContent || this.outerLayout) this.contentVDOM = vdom;
480
- else this.vdom = vdom;
481
- }
482
-
483
- if (this.cacheList) {
484
- for (const key in this.cacheList) {
485
- this.cached[key] = this.cacheList[key];
486
- }
487
- }
488
-
489
- this.cacheList = null;
490
-
491
- this.cached.rawData = this.rawData;
492
- this.cached.data = this.data;
493
- this.cached.state = this.state;
494
- this.cached.widgetVersion = this.widget.version;
495
- this.cached.globalCacheIdentifier = GlobalCacheIdentifier.get();
496
- this.childStateDirty = false;
497
-
498
- if (this.instanceCache) this.instanceCache.sweep();
499
-
500
- if (this.parent?.outerLayout === this) {
501
- //if outer layouts are chained we need to find the originating element (last element with OL set)
502
- let parent = this.parent;
503
- while (parent.parent?.outerLayout == parent) parent = parent.parent;
504
- parent.vdom = this.vdom;
505
- }
506
-
507
- return this.vdom;
508
- }
509
-
510
- public cleanup(context: RenderingContext): void {
511
- if (this.widget.controller && this.controller?.cleanup)
512
- this.controller.cleanup(context);
513
-
514
- if (this.widget.cleanup) this.widget.cleanup(context, this);
515
- }
516
-
517
- private trackDestroy(): void {
518
- if (!this.destroyTracked) {
519
- this.destroyTracked = true;
520
- if (this.parent && !this.detached)
521
- this.parent.trackDestroyableChild(this);
522
- }
523
- }
524
-
525
- private trackDestroyableChild(child: Instance): void {
526
- this.instanceCache!.trackDestroy(child);
527
- this.trackDestroy();
528
- }
529
-
530
- public subscribeOnDestroy(callback: () => void): () => void {
531
- if (!this.destroySubscriptions) this.destroySubscriptions = [];
532
- this.destroySubscriptions.push(callback);
533
- this.trackDestroy();
534
- return () => {
535
- if (this.destroySubscriptions) {
536
- this.destroySubscriptions = this.destroySubscriptions.filter(
537
- (cb) => cb !== callback,
538
- );
539
- }
540
- };
541
- }
542
-
543
- public destroy(): void {
544
- if (this.instanceCache) {
545
- this.instanceCache.destroy();
546
- this.instanceCache = null;
547
- }
548
-
549
- if (this.destroySubscriptions) {
550
- this.destroySubscriptions.forEach((cb) => cb());
551
- this.destroySubscriptions = null;
552
- }
553
-
554
- if (this.destroyTracked) {
555
- debug(destroyFlag, this);
556
-
557
- if (this.widget.onDestroy) this.widget.onDestroy(this);
558
-
559
- if (
560
- this.widget.controller &&
561
- this.controller &&
562
- this.controller.onDestroy &&
563
- this.controller.widget == this.widget
564
- )
565
- this.controller.onDestroy();
566
-
567
- this.destroyTracked = false;
568
- }
569
- }
570
-
571
- public setState(state: Record<string, any>): void {
572
- let skip = !!this.state;
573
- if (this.state) {
574
- for (const k in state) {
575
- if (this.state[k] !== state[k]) {
576
- skip = false;
577
- break;
578
- }
579
- }
580
- }
581
-
582
- if (skip) return;
583
-
584
- this.state = Object.assign({}, this.state, state);
585
- let parent = this.parent;
586
- //notify all parents that child state change to bust up caching
587
- while (parent) {
588
- parent.childStateDirty = true;
589
- parent = parent.parent;
590
- }
591
- batchUpdates(() => {
592
- this.store.notify();
593
- });
594
- }
595
-
596
- public set(
597
- prop: string,
598
- value: any,
599
- options: { internal?: boolean; immediate?: boolean } = {},
600
- ): boolean {
601
- //skip re-rendering (used for reading state from uncontrolled components)
602
- if (options.internal && this.rawData) {
603
- this.rawData[prop] = value;
604
- this.data[prop] = value;
605
- }
606
-
607
- const setter = this.setters && this.setters[prop];
608
- if (setter) {
609
- if (options.immediate && isFunction(setter.reset)) setter.reset(value);
610
- else setter(value);
611
- return true;
612
- }
613
-
614
- const p = (this.widget as any)[prop];
615
- if (p && typeof p == "object") {
616
- if (p.debounce) {
617
- this.definePropertySetter(
618
- prop,
619
- validatedDebounce(
620
- (value: any) => this.doSet(prop, value),
621
- () => this.dataSelector!(this.store)[prop],
622
- p.debounce,
623
- ),
624
- );
625
- this.set(prop, value, options);
626
- return true;
627
- }
628
-
629
- if (p.throttle) {
630
- this.definePropertySetter(
631
- prop,
632
- throttle((value: any) => this.doSet(prop, value), p.throttle),
633
- );
634
- this.set(prop, value, options);
635
- return true;
636
- }
637
- }
638
-
639
- return this.doSet(prop, value);
640
- }
641
-
642
- public definePropertySetter(prop: string, setter: any): void {
643
- if (!this.setters) this.setters = {};
644
- this.setters[prop] = setter;
645
- }
646
-
647
- protected doSet(prop: string, value: any): boolean {
648
- let changed = false;
649
- batchUpdates(() => {
650
- const p = (this.widget as any)[prop];
651
- if (isObject(p)) {
652
- const pObj = p as any;
653
- if (pObj.set) {
654
- if (isFunction(pObj.set)) {
655
- pObj.set(value, this);
656
- changed = true;
657
- } else if (isString(pObj.set)) {
658
- (this.controller as any)?.[pObj.set](value, this);
659
- changed = true;
660
- }
661
- } else if (pObj.action) {
662
- const action = pObj.action(value, this);
663
- this.store.dispatch(action);
664
- changed = true;
665
- } else if (isString(pObj.bind) || isAccessorChain(pObj.bind)) {
666
- changed = this.store.set(pObj.bind, value);
667
- }
668
- } else if (isAccessorChain(p)) {
669
- changed = this.store.set(p.toString(), value);
670
- }
671
- });
672
- return changed;
673
- }
674
-
675
- public nestedDataSet(
676
- key: string,
677
- value: any,
678
- dataConfig: Record<string, any>,
679
- useParentStore?: boolean,
680
- ): boolean {
681
- let config = dataConfig[key];
682
- if (!config)
683
- throw new Error(
684
- `Unknown nested data key ${key}. Known keys are ${Object.keys(dataConfig).join(", ")}.`,
685
- );
686
-
687
- if (isAccessorChain(config)) config = { bind: config.toString() };
688
-
689
- if (config.bind) {
690
- let store = this.store;
691
- //in case of Rescope or DataProxy, bindings point to the data in the parent store
692
- if (useParentStore && store.store) store = store.store;
693
- return isUndefined(value)
694
- ? store.deleteItem(config.bind)
695
- : store.setItem(config.bind, value);
696
- }
697
-
698
- if (!config.set)
699
- throw new Error(
700
- `Cannot change nested data value for ${key} as it's read-only. Either define it as a binding or define a set function.`,
701
- );
702
- if (isString(config.set))
703
- this.getControllerMethod(config.set)(value, this);
704
- else if (isFunction(config.set)) config.set(value, this);
705
- else
706
- throw new Error(
707
- `Cannot change nested data value for ${key} the defined setter is neither a function nor a controller method.`,
708
- );
709
-
710
- return true;
711
- }
712
-
713
- public replaceState(state: Record<string, any>): void {
714
- this.cached.state = this.state;
715
- this.state = state;
716
- this.store.notify();
717
- }
718
-
719
- public getInstanceCache(): InstanceCache {
720
- if (!this.instanceCache)
721
- this.instanceCache = new InstanceCache(
722
- this,
723
- this.widget.isPureContainer ? this.key : null,
724
- );
725
- return this.instanceCache;
726
- }
727
-
728
- public clearChildrenCache(): void {
729
- if (this.instanceCache) this.instanceCache.destroy();
730
- }
731
-
732
- public getChild(
733
- context: RenderingContext | null,
734
- widget: Widget,
735
- key?: string | number | null,
736
- store?: any,
737
- ): Instance {
738
- return this.getInstanceCache().getChild(widget, store ?? this.store, key);
739
- }
740
-
741
- public getDetachedChild(widget: Widget, key: string, store?: any): Instance {
742
- const child = widget.createInstance(key, this, store ?? this.store);
743
- child.detached = true;
744
- return child;
745
- }
746
-
747
- public prepareRenderCleanupChild(
748
- widget: any,
749
- store?: any,
750
- keyPrefix?: string,
751
- options?: any,
752
- ): any {
753
- return widget.prepareRenderCleanup(
754
- store ?? this.store,
755
- options,
756
- keyPrefix,
757
- this,
758
- );
759
- }
760
-
761
- public getJsxEventProps(): Record<string, any> | null {
762
- const { widget } = this;
763
-
764
- if (!isArray(widget.jsxAttributes)) return null;
765
-
766
- const props: Record<string, any> = {};
767
- widget.jsxAttributes.forEach((attr) => {
768
- if (attr.indexOf("on") == 0 && attr.length > 2) {
769
- props[attr] = (e: any) => this.invoke(attr, e, this);
770
- }
771
- });
772
- return props;
773
- }
774
-
775
- public getCallback(methodName: string): (...args: any[]) => any {
776
- const scope = this.widget as any;
777
- const callback = scope[methodName];
778
-
779
- if (typeof callback === "string")
780
- return this.getControllerMethod(callback);
781
-
782
- if (typeof callback !== "function")
783
- throw new Error(
784
- `Cannot invoke callback method ${methodName} as assigned value is not a function.`,
785
- );
786
-
787
- return callback.bind(scope);
788
- }
789
-
790
- /**
791
- * Finds the first controller in the instance tree matching the predicate
792
- * @param predicate Function to test each controller
793
- * @returns The matching controller or undefined
794
- */
795
- public findController(
796
- predicate: (controller: Controller) => boolean,
797
- ): Controller | undefined {
798
- let at: Instance | undefined = this;
799
- while (at?.controller != null) {
800
- if (predicate(at.controller)) {
801
- return at.controller;
802
- }
803
- at = at.parent;
804
- }
805
- return undefined;
806
- }
807
-
808
- /**
809
- * Finds a controller of the specified type in the instance tree
810
- * @param type Controller class/constructor to find
811
- * @returns The matching controller cast to the specified type, or undefined
812
- */
813
- public findControllerByType<T extends Controller>(
814
- type: new (...args: any[]) => T,
815
- ): T | undefined {
816
- return this.findController((c) => c instanceof type) as T | undefined;
817
- }
818
-
819
- /**
820
- * Gets the first controller in the instance tree matching the predicate
821
- * @param predicate Function to test each controller
822
- * @returns The matching controller
823
- * @throws Error if no matching controller is found
824
- */
825
- public getController(
826
- predicate: (controller: Controller) => boolean,
827
- ): Controller {
828
- const controller = this.findController(predicate);
829
- if (!controller)
830
- throw new Error(
831
- "Cannot find a controller matching the given predicate in the instance tree.",
832
- );
833
- return controller;
834
- }
835
-
836
- /**
837
- * Gets a controller of the specified type in the instance tree
838
- * @param type Controller class/constructor to find
839
- * @returns The matching controller cast to the specified type
840
- * @throws Error if no controller of the specified type is found
841
- */
842
- public getControllerByType<T extends Controller>(
843
- type: new (...args: any[]) => T,
844
- ): T {
845
- const controller = this.findControllerByType(type);
846
- if (!controller)
847
- throw new Error(
848
- `Cannot find a controller of type "${type.name}" in the instance tree.`,
849
- );
850
- return controller;
851
- }
852
-
853
- public getControllerMethod(methodName: string): (...args: any[]) => any {
854
- if (!this.controller)
855
- throw new Error(
856
- `Cannot invoke controller method "${methodName}" as controller is not assigned to the widget.`,
857
- );
858
-
859
- const controller = this.findController((c) => !!(c as any)[methodName]);
860
-
861
- if (!controller)
862
- throw new Error(
863
- `Cannot invoke controller method "${methodName}". The method cannot be found in any of the assigned controllers.`,
864
- );
865
-
866
- return (controller as any)[methodName].bind(controller);
867
- }
868
-
869
- public invoke(methodName: string, ...args: any[]): any {
870
- return this.getCallback(methodName).apply(null, args);
871
- }
872
-
873
- public invokeControllerMethod(methodName: string, ...args: any[]): any {
874
- return this.getControllerMethod(methodName).apply(null, args);
875
- }
876
- }
877
-
878
- function renderResultFix(res: any): any {
879
- return res != null && isDefined(res.content) ? res : { content: res };
880
- }
881
-
882
- export class InstanceCache {
883
- declare public children: Record<string, Instance>;
884
- declare public parent: Instance;
885
- declare public marked: Record<string, Instance>;
886
- declare public monitored: Record<string, Instance> | null;
887
- declare public keyPrefix: string;
888
-
889
- constructor(parent: Instance, keyPrefix?: string | number | null) {
890
- this.children = {};
891
- this.parent = parent;
892
- this.marked = {};
893
- this.monitored = null;
894
- this.keyPrefix = keyPrefix != null ? keyPrefix + "-" : "";
895
- }
896
-
897
- public getChild(
898
- widget: Widget,
899
- parentStore: View,
900
- key?: string | number | null,
901
- ): Instance {
902
- const k =
903
- this.keyPrefix +
904
- (key != null ? key : widget.vdomKey || widget.widgetId);
905
- let instance = this.children[k];
906
-
907
- if (
908
- !instance ||
909
- instance.widget !== widget ||
910
- (!instance.visible &&
911
- (instance.widget.controller || instance.widget.onInit))
912
- ) {
913
- instance = widget.createInstance(k, this.parent, parentStore);
914
- this.children[k] = instance;
915
- } else if (instance.parentStore !== parentStore) {
916
- instance.setParentStore(parentStore);
917
- }
918
-
919
- return instance;
920
- }
921
-
922
- public addChild(instance: Instance): void {
923
- this.marked[instance.key] = instance;
924
- }
925
-
926
- public mark(): void {
927
- this.marked = {};
928
- }
929
-
930
- public trackDestroy(instance: Instance): void {
931
- if (!this.monitored) this.monitored = {};
932
- this.monitored[instance.key as string] = instance;
933
- }
934
-
935
- public destroy(): void {
936
- this.children = {};
937
- this.marked = {};
938
-
939
- if (!this.monitored) return;
940
-
941
- for (const key in this.monitored) {
942
- this.monitored[key].destroy();
943
- }
944
-
945
- this.monitored = null;
946
- }
947
-
948
- public sweep(): void {
949
- this.children = this.marked;
950
- if (!this.monitored) return;
951
- let activeCount = 0;
952
- for (const key in this.monitored) {
953
- const monitoredChild = this.monitored[key];
954
- const child = this.children[key];
955
- if (child !== monitoredChild || !monitoredChild.visible) {
956
- monitoredChild.destroy();
957
- delete this.monitored[key];
958
- if (child === monitoredChild) delete this.children[key];
959
- } else activeCount++;
960
- }
961
- if (activeCount === 0) this.monitored = null;
962
- }
963
- }
1
+ import { isAccessorChain } from "../data/createAccessorModelProxy";
2
+ import { View } from "../data/View";
3
+ import { debug, destroyFlag, processDataFlag, renderFlag } from "../util/Debug";
4
+ import { GlobalCacheIdentifier } from "../util/GlobalCacheIdentifier";
5
+ import { isArray } from "../util/isArray";
6
+ import { isDefined } from "../util/isDefined";
7
+ import { isFunction } from "../util/isFunction";
8
+ import { isNonEmptyArray } from "../util/isNonEmptyArray";
9
+ import { isObject } from "../util/isObject";
10
+ import { isString } from "../util/isString";
11
+ import { isUndefined } from "../util/isUndefined";
12
+ import { throttle } from "../util/throttle";
13
+ import { validatedDebounce } from "../util/validatedDebounce";
14
+ import { batchUpdates } from "./batchUpdates";
15
+ import { Controller } from "./Controller";
16
+ import { RenderingContext } from "./RenderingContext";
17
+ import type { Widget } from "./Widget";
18
+
19
+ /**
20
+ * Serializable value types that can be safely passed through the framework
21
+ */
22
+ export type SerializableValue =
23
+ | string
24
+ | number
25
+ | boolean
26
+ | object
27
+ | null
28
+ | undefined;
29
+
30
+ /**
31
+ * Core instance data structure used by widgets
32
+ *
33
+ * For custom widget data, extend this interface:
34
+ * @example
35
+ * interface MyWidgetData extends WidgetData {
36
+ * customProp: string;
37
+ * }
38
+ */
39
+ export interface WidgetData {
40
+ visible?: boolean;
41
+ disabled?: boolean;
42
+ enabled?: boolean;
43
+ text?: string;
44
+ innerHtml?: string;
45
+ attrs?: Record<string, unknown>;
46
+ data?: Record<string, unknown>;
47
+ classNames?: string;
48
+ className?: string;
49
+ class?: string;
50
+ style?: Record<string, string | number> | string;
51
+ stateMods?: Record<string, boolean | undefined>;
52
+ mod?: Record<string, boolean | undefined>;
53
+ pressed?: boolean;
54
+ icon?: string | boolean;
55
+ confirm?: string | ConfirmConfig;
56
+ parentDisabled?: boolean;
57
+ parentStrict?: boolean;
58
+ error?: string;
59
+ }
60
+
61
+ /**
62
+ * Confirmation dialog configuration
63
+ */
64
+ export interface ConfirmConfig {
65
+ message: string;
66
+ title?: string;
67
+ yesText?: string;
68
+ noText?: string;
69
+ }
70
+
71
+ /**
72
+ * Parent options passed down from overlay/modal components
73
+ *
74
+ * For custom parent options, extend this interface:
75
+ * @example
76
+ * interface MyParentOptions extends ParentOptions {
77
+ * customOption: string;
78
+ * }
79
+ */
80
+ export interface ParentOptions {
81
+ dismiss?: () => void;
82
+ }
83
+
84
+ /**
85
+ * Props object used for rendering (passed to React.createElement)
86
+ */
87
+ export type RenderProps = Record<string, any>;
88
+
89
+ /**
90
+ * Result type for yes/no dialogs
91
+ */
92
+ export const enum YesNoResult {
93
+ Yes = "yes",
94
+ No = "no",
95
+ }
96
+
97
+ /**
98
+ * Partial instance-like object used in some render methods
99
+ * This is a hack to allow passing only the data property instead of full Instance
100
+ */
101
+ export interface PartialInstance {
102
+ data: Record<string, any>;
103
+ widget?: Widget;
104
+ state?: Record<string, any>;
105
+ }
106
+
107
+ /**
108
+ * Example widget-specific properties for dropdown widgets
109
+ */
110
+ export interface DropdownWidgetProps {
111
+ lastDropdown?: Instance;
112
+ dropdownOpen?: boolean;
113
+ selectedIndex?: number;
114
+ }
115
+
116
+ /**
117
+ * Example widget-specific properties for form field widgets
118
+ */
119
+ export interface FieldWidgetProps {
120
+ inputElement?: HTMLInputElement;
121
+ validationState?: "valid" | "invalid" | "pending";
122
+ }
123
+
124
+ /**
125
+ * Type aliases for common widget instance types using intersection types
126
+ */
127
+ export type DropdownInstance = Instance & DropdownWidgetProps;
128
+ export type FieldInstance = Instance & FieldWidgetProps;
129
+
130
+ let instanceId = 1000;
131
+
132
+ /**
133
+ * Base Instance class
134
+ */
135
+ export class Instance<WidgetType extends Widget<any, any> = Widget<any, any>> {
136
+ // Core properties
137
+ declare public widget: WidgetType;
138
+ declare public key: string;
139
+ declare public id: string;
140
+ declare public parent?: Instance;
141
+ declare public parentStore: View;
142
+ declare public store: View;
143
+ declare public controller?: Controller;
144
+
145
+ // Data and state
146
+ declare public data: Record<string, any>;
147
+ declare public rawData: Record<string, any>;
148
+ declare public state?: Record<string, any>;
149
+ declare public cached: Record<string, any>;
150
+ declare public cacheList?: Record<string, any> | null;
151
+
152
+ // Selectors
153
+ public dataSelector?: (store: any) => Record<string, any>;
154
+
155
+ // Lifecycle flags
156
+ public initialized?: boolean;
157
+ declare public visible?: boolean;
158
+ declare public explored?: boolean;
159
+ declare public prepared?: boolean;
160
+ declare public shouldUpdate?: boolean;
161
+ declare public childStateDirty?: boolean;
162
+ declare public detached?: boolean;
163
+
164
+ // Cleanup tracking
165
+ declare public needsExploreCleanup?: boolean;
166
+ declare public needsPrepare?: boolean;
167
+ declare public needsCleanup?: boolean;
168
+ declare public destroyTracked?: boolean;
169
+ public destroySubscriptions?: Array<() => void> | null;
170
+
171
+ // Child management
172
+ declare public instanceCache?: InstanceCache | null;
173
+ declare public children?: Instance[];
174
+ public helpers?: Record<string, Instance>;
175
+ public components?: Record<string, Instance>;
176
+
177
+ // Rendering
178
+ declare public vdom?: any;
179
+ declare public contentVDOM?: any;
180
+ declare public renderList?: any;
181
+ declare public assignedRenderList?: any;
182
+
183
+ // Layout
184
+ declare public outerLayout?: Instance;
185
+ declare public contentPlaceholder?: any;
186
+ declare public parentOptions?: any;
187
+
188
+ // Setters
189
+ declare public setters?: Record<string, any>;
190
+
191
+ // Other
192
+ declare public record?: any;
193
+ declare public mappedRecords?: any[];
194
+
195
+ // List-specific
196
+ declare public instances?: Instance[];
197
+ declare public selected?: boolean;
198
+
199
+ constructor(
200
+ widget: WidgetType,
201
+ key: string,
202
+ parent?: Instance,
203
+ parentStore?: any,
204
+ ) {
205
+ this.widget = widget;
206
+ this.key = key;
207
+ this.id = String(++instanceId);
208
+ this.cached = {};
209
+ this.parent = parent;
210
+ this.parentStore = parentStore ?? parent?.store;
211
+
212
+ if (this.parentStore == null)
213
+ throw new Error("Cannot create instance without a parent store.");
214
+ }
215
+
216
+ public setParentStore(parentStore: any): void {
217
+ this.parentStore = parentStore;
218
+ this.widget.applyParentStore(this);
219
+ }
220
+
221
+ public init(context: RenderingContext): void {
222
+ // widget is initialized when the first instance is initialized
223
+ if (!this.widget.initialized) {
224
+ this.widget.init();
225
+ this.widget.initialized = true;
226
+ }
227
+
228
+ if (!this.dataSelector) {
229
+ this.widget.selector!.init(this.parentStore);
230
+ this.dataSelector = this.widget.selector!.createStoreSelector();
231
+ }
232
+
233
+ // init instance might change the store, so this must go before the controller initialization
234
+ this.widget.initInstance(context, this);
235
+
236
+ // initInstance can set the store, otherwise use parent store
237
+ if (!this.store) this.store = this.parentStore;
238
+ if (this.widget.onInit) this.widget.onInit(context, this);
239
+
240
+ this.widget.initState(context, this);
241
+
242
+ if (this.widget.controller)
243
+ this.controller = Controller.create(this.widget.controller, {
244
+ widget: this.widget,
245
+ instance: this,
246
+ store: this.store,
247
+ });
248
+
249
+ if (
250
+ this.widget.exploreCleanup ||
251
+ this.widget.outerLayout ||
252
+ this.widget.isContent ||
253
+ this.widget.controller ||
254
+ this.widget.prepareCleanup
255
+ )
256
+ this.needsExploreCleanup = true;
257
+ if (this.widget.prepare || this.widget.controller)
258
+ this.needsPrepare = true;
259
+ if (this.widget.cleanup || this.widget.controller)
260
+ this.needsCleanup = true;
261
+ this.initialized = true;
262
+ }
263
+
264
+ public checkVisible(context: RenderingContext): boolean {
265
+ if (!this.initialized) this.init(context);
266
+
267
+ const wasVisible = this.visible;
268
+ this.rawData = this.dataSelector!(this.store);
269
+ this.visible = this.widget.checkVisible(context, this, this.rawData);
270
+ if (this.visible && !this.detached)
271
+ this.parent!.instanceCache!.addChild(this);
272
+ this.explored = false;
273
+ this.prepared = false;
274
+
275
+ if (!this.visible && wasVisible) this.destroy();
276
+
277
+ return this.visible;
278
+ }
279
+
280
+ public scheduleExploreIfVisible(context: RenderingContext): boolean {
281
+ if (this.checkVisible(context)) {
282
+ context.exploreStack.push(this);
283
+
284
+ if (this.needsExploreCleanup) context.exploreStack.push(this);
285
+
286
+ return true;
287
+ }
288
+ return false;
289
+ }
290
+
291
+ public cache(key: string, value: any): boolean {
292
+ const oldValue = this.cached[key];
293
+ if (oldValue === value) return false;
294
+
295
+ if (!this.cacheList) this.cacheList = {};
296
+ this.cacheList[key] = value;
297
+ return true;
298
+ }
299
+
300
+ public markShouldUpdate(context: RenderingContext): void {
301
+ let ins: Instance | undefined = this;
302
+ let renderList = this.renderList;
303
+ renderList.markReverseIndex();
304
+
305
+ //notify all parents that child state changed to bust up caching
306
+ while (ins && !ins.shouldUpdate && ins.explored) {
307
+ if (ins.renderList !== renderList) {
308
+ renderList.reverse();
309
+ renderList = ins.renderList;
310
+ renderList.markReverseIndex();
311
+ }
312
+ ins.shouldUpdate = true;
313
+ renderList.data.push(ins);
314
+ ins = ins.widget.isContent
315
+ ? ins.contentPlaceholder
316
+ : ins.parent?.outerLayout === ins
317
+ ? ins.parent?.parent
318
+ : ins.parent;
319
+ }
320
+ renderList.reverse();
321
+ }
322
+
323
+ public explore(context: RenderingContext): void {
324
+ if (!this.visible) throw new Error("Explore invisible!");
325
+
326
+ if (this.explored) {
327
+ if (this.widget.prepareCleanup) context.prepareList.push(this);
328
+
329
+ if (this.widget.exploreCleanup)
330
+ this.widget.exploreCleanup(context, this);
331
+
332
+ if (this.parent?.outerLayout === this)
333
+ context.popNamedValue("content", "body");
334
+
335
+ if (this.widget.controller) context.pop("controller");
336
+
337
+ return;
338
+ }
339
+
340
+ this.explored = true;
341
+
342
+ if (this.needsPrepare) context.prepareList.push(this);
343
+ else this.prepared = true;
344
+
345
+ if (this.needsCleanup) context.cleanupList.push(this);
346
+
347
+ if (this.instanceCache) this.instanceCache.mark();
348
+
349
+ //controller may reconfigure the widget and need to go before shouldUpdate calculation
350
+ this.parentOptions = context.parentOptions;
351
+
352
+ if (!this.controller) {
353
+ if (context.controller) this.controller = context.controller;
354
+ else if (this.parent?.controller)
355
+ this.controller = this.parent?.controller;
356
+ }
357
+
358
+ this.destroyTracked = false;
359
+
360
+ if (this.controller) {
361
+ if (this.widget.controller) {
362
+ if (!this.controller.initialized) {
363
+ this.controller.init(context);
364
+ this.controller.initialized = true;
365
+ }
366
+ context.push("controller", this.controller);
367
+ this.controller.explore(context);
368
+ if (
369
+ this.controller.onDestroy &&
370
+ this.controller.widget == this.widget
371
+ )
372
+ this.trackDestroy();
373
+ }
374
+ }
375
+
376
+ if (this.widget.onDestroy || isNonEmptyArray(this.destroySubscriptions))
377
+ this.trackDestroy();
378
+
379
+ this.renderList =
380
+ this.assignedRenderList ||
381
+ this.parent?.renderList ||
382
+ context.getRootRenderList();
383
+
384
+ let shouldUpdate =
385
+ this.rawData !== this.cached.rawData ||
386
+ this.state !== this.cached.state ||
387
+ this.widget.version !== this.cached.widgetVersion ||
388
+ this.cached.globalCacheIdentifier !== GlobalCacheIdentifier.get();
389
+
390
+ if (shouldUpdate) {
391
+ this.data = { ...this.rawData };
392
+ this.widget.prepareData(context, this);
393
+ debug(processDataFlag, this.widget);
394
+ }
395
+
396
+ //onExplore might set the outer layout
397
+ if (this.widget.onExplore) this.widget.onExplore(context, this);
398
+
399
+ if (this.parent?.outerLayout === this) {
400
+ this.renderList = this.renderList.insertRight();
401
+ context.pushNamedValue("content", "body", this.parent);
402
+ }
403
+
404
+ if (this.widget.outerLayout) {
405
+ this.outerLayout = this.getChild(
406
+ context,
407
+ this.widget.outerLayout,
408
+ null,
409
+ this.store,
410
+ );
411
+ this.outerLayout.scheduleExploreIfVisible(context);
412
+ this.renderList = this.renderList.insertLeft();
413
+ }
414
+
415
+ if (this.widget.isContent) {
416
+ this.contentPlaceholder =
417
+ context.contentPlaceholder &&
418
+ context.contentPlaceholder[this.widget.putInto!];
419
+ if (this.contentPlaceholder)
420
+ context.contentPlaceholder[this.widget.putInto!](this);
421
+ else {
422
+ this.renderList = this.renderList.insertLeft();
423
+ context.pushNamedValue("content", this.widget.putInto!, this);
424
+ if (!context.contentList) context.contentList = {};
425
+ let list = context.contentList[this.widget.putInto!];
426
+ if (!list) list = context.contentList[this.widget.putInto!] = [];
427
+ list.push(this);
428
+ }
429
+ }
430
+
431
+ this.shouldUpdate = false;
432
+ if (shouldUpdate || this.childStateDirty || !this.widget.memoize)
433
+ this.markShouldUpdate(context);
434
+
435
+ context.exploreStack.hop();
436
+
437
+ if (this.widget.helpers) {
438
+ this.helpers = {};
439
+ for (let cmp in this.widget.helpers) {
440
+ let helper = this.widget.helpers[cmp];
441
+ if (helper) {
442
+ let ins = this.getChild(context, helper);
443
+ if (ins.scheduleExploreIfVisible(context))
444
+ this.helpers[cmp] = ins;
445
+ }
446
+ }
447
+ }
448
+
449
+ //TODO: check do we need to pass data here?
450
+ this.widget.explore(context, this, this.data);
451
+ }
452
+
453
+ public prepare(context: RenderingContext): void {
454
+ if (!this.visible) throw new Error("Prepare invisible!");
455
+
456
+ if (this.prepared) {
457
+ if (this.widget.prepareCleanup)
458
+ this.widget.prepareCleanup(context, this);
459
+ return;
460
+ }
461
+
462
+ this.prepared = true;
463
+ if (this.widget.prepare) this.widget.prepare(context, this);
464
+
465
+ if (this.widget.controller && this.controller?.prepare)
466
+ this.controller.prepare(context);
467
+ }
468
+
469
+ public render(
470
+ context: RenderingContext,
471
+ ): null | Record<string, React.ReactNode> | React.ReactNode[] {
472
+ if (!this.visible) throw new Error("Render invisible!");
473
+
474
+ if (this.shouldUpdate) {
475
+ debug(renderFlag, this.widget, this.key);
476
+ const vdom = renderResultFix(
477
+ this.widget.render(context, this, this.key),
478
+ );
479
+ if (this.widget.isContent || this.outerLayout) this.contentVDOM = vdom;
480
+ else this.vdom = vdom;
481
+ }
482
+
483
+ if (this.cacheList) {
484
+ for (const key in this.cacheList) {
485
+ this.cached[key] = this.cacheList[key];
486
+ }
487
+ }
488
+
489
+ this.cacheList = null;
490
+
491
+ this.cached.rawData = this.rawData;
492
+ this.cached.data = this.data;
493
+ this.cached.state = this.state;
494
+ this.cached.widgetVersion = this.widget.version;
495
+ this.cached.globalCacheIdentifier = GlobalCacheIdentifier.get();
496
+ this.childStateDirty = false;
497
+
498
+ if (this.instanceCache) this.instanceCache.sweep();
499
+
500
+ if (this.parent?.outerLayout === this) {
501
+ //if outer layouts are chained we need to find the originating element (last element with OL set)
502
+ let parent = this.parent;
503
+ while (parent.parent?.outerLayout == parent) parent = parent.parent;
504
+ parent.vdom = this.vdom;
505
+ }
506
+
507
+ return this.vdom;
508
+ }
509
+
510
+ public cleanup(context: RenderingContext): void {
511
+ if (this.widget.controller && this.controller?.cleanup)
512
+ this.controller.cleanup(context);
513
+
514
+ if (this.widget.cleanup) this.widget.cleanup(context, this);
515
+ }
516
+
517
+ private trackDestroy(): void {
518
+ if (!this.destroyTracked) {
519
+ this.destroyTracked = true;
520
+ if (this.parent && !this.detached)
521
+ this.parent.trackDestroyableChild(this);
522
+ }
523
+ }
524
+
525
+ private trackDestroyableChild(child: Instance): void {
526
+ this.instanceCache!.trackDestroy(child);
527
+ this.trackDestroy();
528
+ }
529
+
530
+ public subscribeOnDestroy(callback: () => void): () => void {
531
+ if (!this.destroySubscriptions) this.destroySubscriptions = [];
532
+ this.destroySubscriptions.push(callback);
533
+ this.trackDestroy();
534
+ return () => {
535
+ if (this.destroySubscriptions) {
536
+ this.destroySubscriptions = this.destroySubscriptions.filter(
537
+ (cb) => cb !== callback,
538
+ );
539
+ }
540
+ };
541
+ }
542
+
543
+ public destroy(): void {
544
+ if (this.instanceCache) {
545
+ this.instanceCache.destroy();
546
+ this.instanceCache = null;
547
+ }
548
+
549
+ if (this.destroySubscriptions) {
550
+ this.destroySubscriptions.forEach((cb) => cb());
551
+ this.destroySubscriptions = null;
552
+ }
553
+
554
+ if (this.destroyTracked) {
555
+ debug(destroyFlag, this);
556
+
557
+ if (this.widget.onDestroy) this.widget.onDestroy(this);
558
+
559
+ if (
560
+ this.widget.controller &&
561
+ this.controller &&
562
+ this.controller.onDestroy &&
563
+ this.controller.widget == this.widget
564
+ )
565
+ this.controller.onDestroy();
566
+
567
+ this.destroyTracked = false;
568
+ }
569
+ }
570
+
571
+ public setState(state: Record<string, any>): void {
572
+ let skip = !!this.state;
573
+ if (this.state) {
574
+ for (const k in state) {
575
+ if (this.state[k] !== state[k]) {
576
+ skip = false;
577
+ break;
578
+ }
579
+ }
580
+ }
581
+
582
+ if (skip) return;
583
+
584
+ this.state = Object.assign({}, this.state, state);
585
+ let parent = this.parent;
586
+ //notify all parents that child state change to bust up caching
587
+ while (parent) {
588
+ parent.childStateDirty = true;
589
+ parent = parent.parent;
590
+ }
591
+ batchUpdates(() => {
592
+ this.store.notify();
593
+ });
594
+ }
595
+
596
+ public set(
597
+ prop: string,
598
+ value: any,
599
+ options: { internal?: boolean; immediate?: boolean } = {},
600
+ ): boolean {
601
+ //skip re-rendering (used for reading state from uncontrolled components)
602
+ if (options.internal && this.rawData) {
603
+ this.rawData[prop] = value;
604
+ this.data[prop] = value;
605
+ }
606
+
607
+ const setter = this.setters && this.setters[prop];
608
+ if (setter) {
609
+ if (options.immediate && isFunction(setter.reset)) setter.reset(value);
610
+ else setter(value);
611
+ return true;
612
+ }
613
+
614
+ const p = (this.widget as any)[prop];
615
+ if (p && typeof p == "object") {
616
+ if (p.debounce) {
617
+ this.definePropertySetter(
618
+ prop,
619
+ validatedDebounce(
620
+ (value: any) => this.doSet(prop, value),
621
+ () => this.dataSelector!(this.store)[prop],
622
+ p.debounce,
623
+ ),
624
+ );
625
+ this.set(prop, value, options);
626
+ return true;
627
+ }
628
+
629
+ if (p.throttle) {
630
+ this.definePropertySetter(
631
+ prop,
632
+ throttle((value: any) => this.doSet(prop, value), p.throttle),
633
+ );
634
+ this.set(prop, value, options);
635
+ return true;
636
+ }
637
+ }
638
+
639
+ return this.doSet(prop, value);
640
+ }
641
+
642
+ public definePropertySetter(prop: string, setter: any): void {
643
+ if (!this.setters) this.setters = {};
644
+ this.setters[prop] = setter;
645
+ }
646
+
647
+ protected doSet(prop: string, value: any): boolean {
648
+ let changed = false;
649
+ batchUpdates(() => {
650
+ const p = (this.widget as any)[prop];
651
+ if (isObject(p)) {
652
+ const pObj = p as any;
653
+ if (pObj.set) {
654
+ if (isFunction(pObj.set)) {
655
+ pObj.set(value, this);
656
+ changed = true;
657
+ } else if (isString(pObj.set)) {
658
+ (this.controller as any)?.[pObj.set](value, this);
659
+ changed = true;
660
+ }
661
+ } else if (pObj.action) {
662
+ const action = pObj.action(value, this);
663
+ this.store.dispatch(action);
664
+ changed = true;
665
+ } else if (isString(pObj.bind) || isAccessorChain(pObj.bind)) {
666
+ changed = this.store.set(pObj.bind, value);
667
+ }
668
+ } else if (isAccessorChain(p)) {
669
+ changed = this.store.set(p.toString(), value);
670
+ }
671
+ });
672
+ return changed;
673
+ }
674
+
675
+ public nestedDataSet(
676
+ key: string,
677
+ value: any,
678
+ dataConfig: Record<string, any>,
679
+ useParentStore?: boolean,
680
+ ): boolean {
681
+ let config = dataConfig[key];
682
+ if (!config)
683
+ throw new Error(
684
+ `Unknown nested data key ${key}. Known keys are ${Object.keys(dataConfig).join(", ")}.`,
685
+ );
686
+
687
+ if (isAccessorChain(config)) config = { bind: config.toString() };
688
+
689
+ if (config.bind) {
690
+ let store = this.store;
691
+ //in case of Rescope or DataProxy, bindings point to the data in the parent store
692
+ if (useParentStore && store.store) store = store.store;
693
+ return isUndefined(value)
694
+ ? store.deleteItem(config.bind)
695
+ : store.setItem(config.bind, value);
696
+ }
697
+
698
+ if (!config.set)
699
+ throw new Error(
700
+ `Cannot change nested data value for ${key} as it's read-only. Either define it as a binding or define a set function.`,
701
+ );
702
+ if (isString(config.set))
703
+ this.getControllerMethod(config.set)(value, this);
704
+ else if (isFunction(config.set)) config.set(value, this);
705
+ else
706
+ throw new Error(
707
+ `Cannot change nested data value for ${key} the defined setter is neither a function nor a controller method.`,
708
+ );
709
+
710
+ return true;
711
+ }
712
+
713
+ public replaceState(state: Record<string, any>): void {
714
+ this.cached.state = this.state;
715
+ this.state = state;
716
+ this.store.notify();
717
+ }
718
+
719
+ public getInstanceCache(): InstanceCache {
720
+ if (!this.instanceCache)
721
+ this.instanceCache = new InstanceCache(
722
+ this,
723
+ this.widget.isPureContainer ? this.key : null,
724
+ );
725
+ return this.instanceCache;
726
+ }
727
+
728
+ public clearChildrenCache(): void {
729
+ if (this.instanceCache) this.instanceCache.destroy();
730
+ }
731
+
732
+ public getChild(
733
+ context: RenderingContext | null,
734
+ widget: Widget,
735
+ key?: string | number | null,
736
+ store?: any,
737
+ ): Instance {
738
+ return this.getInstanceCache().getChild(widget, store ?? this.store, key);
739
+ }
740
+
741
+ public getDetachedChild(widget: Widget, key: string, store?: any): Instance {
742
+ const child = widget.createInstance(key, this, store ?? this.store);
743
+ child.detached = true;
744
+ return child;
745
+ }
746
+
747
+ public prepareRenderCleanupChild(
748
+ widget: any,
749
+ store?: any,
750
+ keyPrefix?: string,
751
+ options?: any,
752
+ ): any {
753
+ return widget.prepareRenderCleanup(
754
+ store ?? this.store,
755
+ options,
756
+ keyPrefix,
757
+ this,
758
+ );
759
+ }
760
+
761
+ public getJsxEventProps(): Record<string, any> | null {
762
+ const { widget } = this;
763
+
764
+ if (!isArray(widget.jsxAttributes)) return null;
765
+
766
+ const props: Record<string, any> = {};
767
+ widget.jsxAttributes.forEach((attr) => {
768
+ if (attr.indexOf("on") == 0 && attr.length > 2) {
769
+ props[attr] = (e: any) => this.invoke(attr, e, this);
770
+ }
771
+ });
772
+ return props;
773
+ }
774
+
775
+ public getCallback(methodName: string): (...args: any[]) => any {
776
+ const scope = this.widget as any;
777
+ const callback = scope[methodName];
778
+
779
+ if (typeof callback === "string")
780
+ return this.getControllerMethod(callback);
781
+
782
+ if (typeof callback !== "function")
783
+ throw new Error(
784
+ `Cannot invoke callback method ${methodName} as assigned value is not a function.`,
785
+ );
786
+
787
+ return callback.bind(scope);
788
+ }
789
+
790
+ /**
791
+ * Finds the first controller in the instance tree matching the predicate
792
+ * @param predicate Function to test each controller
793
+ * @returns The matching controller or undefined
794
+ */
795
+ public findController(
796
+ predicate: (controller: Controller) => boolean,
797
+ ): Controller | undefined {
798
+ let at: Instance | undefined = this;
799
+ while (at?.controller != null) {
800
+ if (predicate(at.controller)) {
801
+ return at.controller;
802
+ }
803
+ at = at.parent;
804
+ }
805
+ return undefined;
806
+ }
807
+
808
+ /**
809
+ * Finds a controller of the specified type in the instance tree
810
+ * @param type Controller class/constructor to find
811
+ * @returns The matching controller cast to the specified type, or undefined
812
+ */
813
+ public findControllerByType<T extends Controller>(
814
+ type: new (...args: any[]) => T,
815
+ ): T | undefined {
816
+ return this.findController((c) => c instanceof type) as T | undefined;
817
+ }
818
+
819
+ /**
820
+ * Gets the first controller in the instance tree matching the predicate
821
+ * @param predicate Function to test each controller
822
+ * @returns The matching controller
823
+ * @throws Error if no matching controller is found
824
+ */
825
+ public getController(
826
+ predicate: (controller: Controller) => boolean,
827
+ ): Controller {
828
+ const controller = this.findController(predicate);
829
+ if (!controller)
830
+ throw new Error(
831
+ "Cannot find a controller matching the given predicate in the instance tree.",
832
+ );
833
+ return controller;
834
+ }
835
+
836
+ /**
837
+ * Gets a controller of the specified type in the instance tree
838
+ * @param type Controller class/constructor to find
839
+ * @returns The matching controller cast to the specified type
840
+ * @throws Error if no controller of the specified type is found
841
+ */
842
+ public getControllerByType<T extends Controller>(
843
+ type: new (...args: any[]) => T,
844
+ ): T {
845
+ const controller = this.findControllerByType(type);
846
+ if (!controller)
847
+ throw new Error(
848
+ `Cannot find a controller of type "${type.name}" in the instance tree.`,
849
+ );
850
+ return controller;
851
+ }
852
+
853
+ public getControllerMethod(methodName: string): (...args: any[]) => any {
854
+ if (!this.controller)
855
+ throw new Error(
856
+ `Cannot invoke controller method "${methodName}" as controller is not assigned to the widget.`,
857
+ );
858
+
859
+ const controller = this.findController((c) => !!(c as any)[methodName]);
860
+
861
+ if (!controller)
862
+ throw new Error(
863
+ `Cannot invoke controller method "${methodName}". The method cannot be found in any of the assigned controllers.`,
864
+ );
865
+
866
+ return (controller as any)[methodName].bind(controller);
867
+ }
868
+
869
+ public invoke(methodName: string, ...args: any[]): any {
870
+ return this.getCallback(methodName).apply(null, args);
871
+ }
872
+
873
+ public invokeControllerMethod(methodName: string, ...args: any[]): any {
874
+ return this.getControllerMethod(methodName).apply(null, args);
875
+ }
876
+ }
877
+
878
+ function renderResultFix(res: any): any {
879
+ return res != null && isDefined(res.content) ? res : { content: res };
880
+ }
881
+
882
+ export class InstanceCache {
883
+ declare public children: Record<string, Instance>;
884
+ declare public parent: Instance;
885
+ declare public marked: Record<string, Instance>;
886
+ declare public monitored: Record<string, Instance> | null;
887
+ declare public keyPrefix: string;
888
+
889
+ constructor(parent: Instance, keyPrefix?: string | number | null) {
890
+ this.children = {};
891
+ this.parent = parent;
892
+ this.marked = {};
893
+ this.monitored = null;
894
+ this.keyPrefix = keyPrefix != null ? keyPrefix + "-" : "";
895
+ }
896
+
897
+ public getChild(
898
+ widget: Widget,
899
+ parentStore: View,
900
+ key?: string | number | null,
901
+ ): Instance {
902
+ const k =
903
+ this.keyPrefix +
904
+ (key != null ? key : widget.vdomKey || widget.widgetId);
905
+ let instance = this.children[k];
906
+
907
+ if (
908
+ !instance ||
909
+ instance.widget !== widget ||
910
+ (!instance.visible &&
911
+ (instance.widget.controller || instance.widget.onInit))
912
+ ) {
913
+ instance = widget.createInstance(k, this.parent, parentStore);
914
+ this.children[k] = instance;
915
+ } else if (instance.parentStore !== parentStore) {
916
+ instance.setParentStore(parentStore);
917
+ }
918
+
919
+ return instance;
920
+ }
921
+
922
+ public addChild(instance: Instance): void {
923
+ this.marked[instance.key] = instance;
924
+ }
925
+
926
+ public mark(): void {
927
+ this.marked = {};
928
+ }
929
+
930
+ public trackDestroy(instance: Instance): void {
931
+ if (!this.monitored) this.monitored = {};
932
+ this.monitored[instance.key as string] = instance;
933
+ }
934
+
935
+ public destroy(): void {
936
+ this.children = {};
937
+ this.marked = {};
938
+
939
+ if (!this.monitored) return;
940
+
941
+ for (const key in this.monitored) {
942
+ this.monitored[key].destroy();
943
+ }
944
+
945
+ this.monitored = null;
946
+ }
947
+
948
+ public sweep(): void {
949
+ this.children = this.marked;
950
+ if (!this.monitored) return;
951
+ let activeCount = 0;
952
+ for (const key in this.monitored) {
953
+ const monitoredChild = this.monitored[key];
954
+ const child = this.children[key];
955
+ if (child !== monitoredChild || !monitoredChild.visible) {
956
+ monitoredChild.destroy();
957
+ delete this.monitored[key];
958
+ if (child === monitoredChild) delete this.children[key];
959
+ } else activeCount++;
960
+ }
961
+ if (activeCount === 0) this.monitored = null;
962
+ }
963
+ }