cx 26.1.0 → 26.1.2

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 (540) 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/data/ArrayElementView.d.ts +4 -3
  6. package/build/ui/Widget.js +0 -5
  7. package/build/ui/adapter/ArrayAdapter.js +0 -2
  8. package/build/ui/adapter/DataAdapter.d.ts +3 -3
  9. package/build/util/Component.js +5 -0
  10. package/build/util/test/createTestRenderer.d.ts +3 -1
  11. package/build/util/test/createTestRenderer.js +8 -2
  12. package/build/widgets/icons/calendar.js +4 -3
  13. package/build/widgets/icons/check.js +2 -2
  14. package/build/widgets/icons/clear.js +2 -2
  15. package/build/widgets/icons/close.js +2 -2
  16. package/build/widgets/icons/cx.js +2 -2
  17. package/build/widgets/icons/drop-down.js +2 -2
  18. package/build/widgets/icons/file.js +2 -2
  19. package/build/widgets/icons/folder-open.js +2 -2
  20. package/build/widgets/icons/folder.js +2 -2
  21. package/build/widgets/icons/forward.js +2 -2
  22. package/build/widgets/icons/loading.js +2 -2
  23. package/build/widgets/icons/menu.js +2 -2
  24. package/build/widgets/icons/pixel-picker.js +2 -2
  25. package/build/widgets/icons/search.js +2 -2
  26. package/build/widgets/icons/sort-asc.js +2 -2
  27. package/build/widgets/icons/square.js +2 -2
  28. package/build.js +133 -133
  29. package/dist/manifest.js +863 -863
  30. package/dist/ui.js +1 -10
  31. package/dist/util.js +4 -0
  32. package/dist/widgets.js +377 -313
  33. package/package.json +100 -97
  34. package/src/charts/Bar.scss +28 -28
  35. package/src/charts/Bar.ts +114 -114
  36. package/src/charts/BarGraph.scss +28 -28
  37. package/src/charts/BarGraph.tsx +145 -145
  38. package/src/charts/BubbleGraph.scss +31 -31
  39. package/src/charts/BubbleGraph.tsx +165 -165
  40. package/src/charts/Chart.ts +108 -108
  41. package/src/charts/ColorMap.ts +150 -150
  42. package/src/charts/Column.scss +28 -28
  43. package/src/charts/Column.ts +124 -124
  44. package/src/charts/ColumnBarBase.tsx +284 -284
  45. package/src/charts/ColumnBarGraphBase.tsx +229 -229
  46. package/src/charts/ColumnGraph.scss +29 -29
  47. package/src/charts/ColumnGraph.tsx +153 -153
  48. package/src/charts/Grid.ts +5 -5
  49. package/src/charts/Gridlines.scss +24 -24
  50. package/src/charts/Gridlines.tsx +88 -88
  51. package/src/charts/Legend.scss +50 -50
  52. package/src/charts/Legend.tsx +270 -270
  53. package/src/charts/LegendEntry.scss +29 -29
  54. package/src/charts/LegendEntry.tsx +197 -197
  55. package/src/charts/LineGraph.scss +24 -24
  56. package/src/charts/LineGraph.tsx +455 -455
  57. package/src/charts/Marker.scss +43 -43
  58. package/src/charts/Marker.tsx +483 -483
  59. package/src/charts/MarkerLine.scss +19 -19
  60. package/src/charts/MarkerLine.tsx +214 -214
  61. package/src/charts/MouseTracker.tsx +112 -112
  62. package/src/charts/Pie.ts +8 -8
  63. package/src/charts/PieChart.scss +28 -28
  64. package/src/charts/PieChart.tsx +717 -717
  65. package/src/charts/PieLabel.tsx +106 -106
  66. package/src/charts/PieLabelsContainer.ts +68 -68
  67. package/src/charts/Range.scss +19 -19
  68. package/src/charts/Range.tsx +318 -318
  69. package/src/charts/RangeMarker.scss +17 -17
  70. package/src/charts/RangeMarker.tsx +233 -233
  71. package/src/charts/ScatterGraph.scss +23 -23
  72. package/src/charts/ScatterGraph.tsx +245 -245
  73. package/src/charts/Swimlane.scss +16 -16
  74. package/src/charts/Swimlane.tsx +195 -195
  75. package/src/charts/Swimlanes.scss +16 -16
  76. package/src/charts/Swimlanes.tsx +179 -179
  77. package/src/charts/axis/Axis.scss +22 -22
  78. package/src/charts/axis/Axis.tsx +444 -444
  79. package/src/charts/axis/CategoryAxis.scss +34 -34
  80. package/src/charts/axis/CategoryAxis.tsx +313 -313
  81. package/src/charts/axis/NumericAxis.scss +34 -34
  82. package/src/charts/axis/NumericAxis.tsx +437 -437
  83. package/src/charts/axis/Stack.ts +61 -61
  84. package/src/charts/axis/TimeAxis.scss +33 -33
  85. package/src/charts/axis/TimeAxis.tsx +718 -718
  86. package/src/charts/axis/index.scss +5 -5
  87. package/src/charts/axis/index.ts +3 -3
  88. package/src/charts/axis/variables.scss +2 -2
  89. package/src/charts/helpers/MinMaxFinder.ts +66 -66
  90. package/src/charts/helpers/PointReducer.ts +135 -135
  91. package/src/charts/helpers/SnapPointFinder.ts +136 -136
  92. package/src/charts/helpers/ValueAtFinder.ts +72 -72
  93. package/src/charts/helpers/index.ts +4 -4
  94. package/src/charts/index.scss +22 -22
  95. package/src/charts/index.ts +34 -34
  96. package/src/charts/palette.scss +97 -97
  97. package/src/charts/shapes.tsx +86 -86
  98. package/src/charts/variables.scss +22 -22
  99. package/src/data/AggregateFunction.ts +171 -171
  100. package/src/data/ArrayElementView.spec.ts +88 -88
  101. package/src/data/ArrayElementView.ts +4 -3
  102. package/src/data/ArrayRef.ts +34 -34
  103. package/src/data/Binding.spec.ts +69 -69
  104. package/src/data/Expression.spec.ts +229 -229
  105. package/src/data/Expression.ts +233 -233
  106. package/src/data/Grouper.ts +158 -158
  107. package/src/data/Ref.spec.ts +79 -79
  108. package/src/data/Selector.ts +10 -10
  109. package/src/data/Store.spec.ts +22 -22
  110. package/src/data/StoreRef.spec.ts +24 -24
  111. package/src/data/StringTemplate.spec.ts +132 -132
  112. package/src/data/StructuredSelector.ts +146 -146
  113. package/src/data/View.spec.ts +60 -60
  114. package/src/data/View.ts +289 -289
  115. package/src/data/ZoomIntoPropertyView.spec.ts +64 -64
  116. package/src/data/comparer.spec.ts +60 -60
  117. package/src/data/computable.spec.ts +87 -87
  118. package/src/data/computable.ts +69 -69
  119. package/src/data/createAccessorModelProxy.spec.tsx +145 -145
  120. package/src/data/createAccessorModelProxy.ts +66 -66
  121. package/src/data/createStructuredSelector.spec.ts +45 -45
  122. package/src/data/createStructuredSelector.ts +62 -62
  123. package/src/data/defaultCompare.ts +14 -14
  124. package/src/data/diff/diffArrays.ts +49 -49
  125. package/src/data/diff/diffs.spec.ts +49 -49
  126. package/src/data/diff/index.ts +1 -1
  127. package/src/data/enableFatArrowExpansion.ts +6 -6
  128. package/src/data/getAccessor.spec.ts +11 -11
  129. package/src/data/getAccessor.ts +74 -74
  130. package/src/data/getSelector.spec.ts +43 -43
  131. package/src/data/getSelector.ts +66 -66
  132. package/src/data/index.ts +30 -30
  133. package/src/data/isSelector.ts +26 -26
  134. package/src/data/ops/append.spec.ts +28 -28
  135. package/src/data/ops/append.ts +5 -5
  136. package/src/data/ops/filter.spec.ts +35 -35
  137. package/src/data/ops/filter.ts +9 -9
  138. package/src/data/ops/findTreeNode.spec.ts +23 -23
  139. package/src/data/ops/findTreeNode.ts +14 -14
  140. package/src/data/ops/findTreePath.ts +23 -23
  141. package/src/data/ops/index.ts +11 -11
  142. package/src/data/ops/insertElement.ts +3 -3
  143. package/src/data/ops/merge.spec.ts +27 -27
  144. package/src/data/ops/merge.ts +13 -13
  145. package/src/data/ops/moveElement.ts +21 -21
  146. package/src/data/ops/removeTreeNodes.spec.ts +37 -37
  147. package/src/data/ops/removeTreeNodes.ts +15 -15
  148. package/src/data/ops/updateArray.spec.ts +69 -69
  149. package/src/data/ops/updateTree.spec.ts +54 -54
  150. package/src/data/ops/updateTree.ts +23 -23
  151. package/src/data/test-types.ts +7 -7
  152. package/src/global.scss +13 -13
  153. package/src/hooks/createLocalStorageRef.ts +21 -21
  154. package/src/hooks/index.ts +8 -8
  155. package/src/hooks/invokeCallback.spec.tsx +59 -59
  156. package/src/hooks/invokeCallback.ts +8 -8
  157. package/src/hooks/resolveCallback.spec.tsx +71 -71
  158. package/src/hooks/resolveCallback.ts +16 -16
  159. package/src/hooks/store.spec.tsx +67 -67
  160. package/src/hooks/store.ts +46 -46
  161. package/src/hooks/useEffect.ts +14 -14
  162. package/src/hooks/useInterval.ts +8 -8
  163. package/src/hooks/useState.ts +20 -20
  164. package/src/hooks/useTrigger.spec.tsx +105 -99
  165. package/src/hooks/useTrigger.ts +26 -26
  166. package/src/index.ts +7 -7
  167. package/src/jsx-runtime.spec.tsx +431 -431
  168. package/src/locale/de-de.ts +76 -76
  169. package/src/locale/en-us.ts +75 -75
  170. package/src/locale/es-es.ts +76 -76
  171. package/src/locale/fr-fr.ts +76 -76
  172. package/src/locale/nl-nl.ts +76 -76
  173. package/src/locale/pt-pt.ts +76 -76
  174. package/src/locale/sr-latn-ba.ts +76 -76
  175. package/src/svg/BoundedObject.ts +101 -101
  176. package/src/svg/ClipRect.tsx +29 -29
  177. package/src/svg/Ellipse.tsx +68 -68
  178. package/src/svg/Line.tsx +58 -58
  179. package/src/svg/NonOverlappingRect.ts +26 -26
  180. package/src/svg/NonOverlappingRectGroup.ts +49 -49
  181. package/src/svg/Rectangle.tsx +90 -90
  182. package/src/svg/Svg.scss +27 -27
  183. package/src/svg/Svg.tsx +241 -241
  184. package/src/svg/Text.tsx +134 -134
  185. package/src/svg/TextualBoundedObject.ts +35 -35
  186. package/src/svg/index.scss +8 -8
  187. package/src/svg/index.ts +17 -17
  188. package/src/svg/util/Rect.ts +105 -105
  189. package/src/ui/CSSHelper.ts +17 -17
  190. package/src/ui/Container.tsx +216 -216
  191. package/src/ui/ContentResolver.spec.tsx +585 -583
  192. package/src/ui/ContentResolver.ts +132 -132
  193. package/src/ui/Controller.spec.tsx +574 -566
  194. package/src/ui/Controller.ts +202 -202
  195. package/src/ui/Culture.ts +159 -159
  196. package/src/ui/Cx.spec.tsx +210 -208
  197. package/src/ui/Cx.tsx +386 -386
  198. package/src/ui/DataProxy.spec.tsx +337 -337
  199. package/src/ui/DetachedScope.tsx +159 -159
  200. package/src/ui/Format.ts +108 -108
  201. package/src/ui/HoverSync.tsx +189 -189
  202. package/src/ui/Instance.ts +866 -866
  203. package/src/ui/IsolatedScope.spec.tsx +62 -55
  204. package/src/ui/IsolatedScope.ts +50 -50
  205. package/src/ui/Localization.ts +70 -70
  206. package/src/ui/Prop.ts +140 -140
  207. package/src/ui/PureContainer.spec.tsx +232 -230
  208. package/src/ui/PureContainer.tsx +19 -19
  209. package/src/ui/RenderingContext.ts +99 -99
  210. package/src/ui/Repeater.spec.tsx +143 -141
  211. package/src/ui/Repeater.ts +194 -194
  212. package/src/ui/Rescope.spec.tsx +199 -199
  213. package/src/ui/ResizeManager.ts +30 -30
  214. package/src/ui/Restate.spec.tsx +422 -418
  215. package/src/ui/Restate.tsx +217 -217
  216. package/src/ui/StaticText.ts +11 -11
  217. package/src/ui/StructuredInstanceDataAccessor.ts +32 -32
  218. package/src/ui/Text.ts +49 -49
  219. package/src/ui/Widget.spec.tsx +53 -53
  220. package/src/ui/Widget.tsx +301 -308
  221. package/src/ui/ZIndexManager.ts +11 -11
  222. package/src/ui/adapter/ArrayAdapter.ts +229 -229
  223. package/src/ui/adapter/DataAdapter.ts +52 -52
  224. package/src/ui/adapter/GroupAdapter.ts +235 -235
  225. package/src/ui/adapter/TreeAdapter.spec.ts +76 -76
  226. package/src/ui/adapter/TreeAdapter.ts +185 -185
  227. package/src/ui/adapter/index.ts +4 -4
  228. package/src/ui/app/History.ts +133 -133
  229. package/src/ui/app/Url.spec.ts +50 -50
  230. package/src/ui/app/Url.ts +102 -102
  231. package/src/ui/app/index.ts +5 -5
  232. package/src/ui/app/startAppLoop.tsx +71 -71
  233. package/src/ui/app/startHotAppLoop.ts +41 -41
  234. package/src/ui/batchUpdates.ts +77 -77
  235. package/src/ui/bind.ts +10 -10
  236. package/src/ui/createFunctionalComponent.spec.tsx +454 -452
  237. package/src/ui/createFunctionalComponent.ts +86 -86
  238. package/src/ui/expr.ts +47 -47
  239. package/src/ui/exprHelpers.spec.ts +379 -379
  240. package/src/ui/exprHelpers.ts +78 -78
  241. package/src/ui/flattenProps.ts +21 -21
  242. package/src/ui/index.scss +2 -2
  243. package/src/ui/index.ts +47 -47
  244. package/src/ui/keyboardShortcuts.ts +40 -40
  245. package/src/ui/layout/ContentPlaceholder.spec.tsx +599 -587
  246. package/src/ui/layout/ContentPlaceholder.ts +133 -133
  247. package/src/ui/layout/FirstVisibleChildLayout.spec.tsx +207 -195
  248. package/src/ui/layout/FirstVisibleChildLayout.ts +60 -60
  249. package/src/ui/layout/LabelsLeftLayout.scss +46 -46
  250. package/src/ui/layout/LabelsLeftLayout.tsx +76 -76
  251. package/src/ui/layout/LabelsTopLayout.scss +64 -64
  252. package/src/ui/layout/LabelsTopLayout.tsx +156 -156
  253. package/src/ui/layout/UseParentLayout.ts +8 -8
  254. package/src/ui/layout/exploreChildren.ts +38 -38
  255. package/src/ui/layout/index.scss +3 -3
  256. package/src/ui/layout/index.ts +10 -10
  257. package/src/ui/layout/variables.scss +2 -2
  258. package/src/ui/selection/KeySelection.ts +153 -153
  259. package/src/ui/selection/Selection.ts +128 -128
  260. package/src/ui/selection/index.ts +3 -3
  261. package/src/ui/tpl.ts +4 -4
  262. package/src/ui/variables.scss +1 -1
  263. package/src/util/Component.spec.ts +411 -381
  264. package/src/util/Component.ts +301 -296
  265. package/src/util/Console.ts +13 -13
  266. package/src/util/Debug.ts +71 -71
  267. package/src/util/GlobalCacheIdentifier.ts +11 -11
  268. package/src/util/KeyCode.ts +21 -21
  269. package/src/util/SubscriberList.ts +86 -86
  270. package/src/util/Timing.ts +58 -58
  271. package/src/util/TraversalStack.spec.ts +53 -53
  272. package/src/util/TraversalStack.ts +47 -47
  273. package/src/util/calculateNaturalElementHeight.ts +22 -22
  274. package/src/util/call-once.scss +6 -6
  275. package/src/util/capitalize.ts +4 -4
  276. package/src/util/coalesce.ts +6 -6
  277. package/src/util/color/hslToRgb.ts +34 -34
  278. package/src/util/color/index.ts +4 -4
  279. package/src/util/color/parseColor.ts +173 -173
  280. package/src/util/color/rgbToHex.ts +14 -14
  281. package/src/util/date/dateDiff.ts +9 -9
  282. package/src/util/date/diff.ts +13 -13
  283. package/src/util/date/encodeDate.ts +8 -8
  284. package/src/util/date/encodeDateWithTimezoneOffset.ts +18 -18
  285. package/src/util/date/index.ts +11 -11
  286. package/src/util/date/lowerBoundCheck.ts +13 -13
  287. package/src/util/date/maxDate.ts +14 -14
  288. package/src/util/date/minDate.ts +14 -14
  289. package/src/util/date/monthStart.ts +8 -8
  290. package/src/util/date/parseDateInvariant.ts +20 -20
  291. package/src/util/date/sameDate.ts +11 -11
  292. package/src/util/date/upperBoundCheck.spec.ts +30 -30
  293. package/src/util/date/upperBoundCheck.ts +13 -13
  294. package/src/util/date/zeroTime.ts +9 -9
  295. package/src/util/debounce.ts +26 -26
  296. package/src/util/dummyCallback.ts +1 -1
  297. package/src/util/escapeSpecialRegexCharacters.ts +8 -8
  298. package/src/util/eventCallbacks.ts +12 -12
  299. package/src/util/expandFatArrows.ts +118 -118
  300. package/src/util/findScrollableParent.ts +15 -15
  301. package/src/util/getParentFrameBoundingClientRect.ts +13 -13
  302. package/src/util/getScrollerBoundingClientRect.ts +12 -12
  303. package/src/util/getSearchQueryPredicate.spec.ts +40 -40
  304. package/src/util/getSearchQueryPredicate.ts +58 -58
  305. package/src/util/getTopLevelBoundingClientRect.ts +7 -7
  306. package/src/util/getVendorPrefix.ts +33 -33
  307. package/src/util/hasKey.ts +18 -18
  308. package/src/util/index.scss +10 -10
  309. package/src/util/index.ts +55 -55
  310. package/src/util/isArray.ts +3 -3
  311. package/src/util/isDefined.ts +3 -3
  312. package/src/util/isDigit.ts +8 -8
  313. package/src/util/isFunction.ts +3 -3
  314. package/src/util/isNonEmptyArray.ts +3 -3
  315. package/src/util/isNumber.ts +3 -3
  316. package/src/util/isObject.ts +3 -3
  317. package/src/util/isPromise.ts +6 -6
  318. package/src/util/isString.ts +3 -3
  319. package/src/util/isTextInputElement.ts +2 -2
  320. package/src/util/isTouchDevice.ts +7 -7
  321. package/src/util/isTouchEvent.ts +64 -64
  322. package/src/util/isUndefined.ts +3 -3
  323. package/src/util/isValidIdentifierName.spec.ts +33 -33
  324. package/src/util/isValidIdentifierName.ts +5 -5
  325. package/src/util/onIdleCallback.ts +10 -10
  326. package/src/util/parseStyle.ts +29 -29
  327. package/src/util/quote.ts +6 -6
  328. package/src/util/reverseSlice.ts +9 -9
  329. package/src/util/routeAppend.spec.ts +19 -19
  330. package/src/util/routeAppend.ts +15 -15
  331. package/src/util/scrollElementIntoView.ts +40 -40
  332. package/src/util/scss/add-rules.scss +40 -40
  333. package/src/util/scss/calc.scss +44 -44
  334. package/src/util/scss/call-once.scss +12 -12
  335. package/src/util/scss/clockwise.scss +49 -49
  336. package/src/util/scss/colors.scss +9 -9
  337. package/src/util/scss/deep-get.scss +11 -11
  338. package/src/util/scss/deep-merge.scss +21 -21
  339. package/src/util/scss/divide.scss +3 -3
  340. package/src/util/scss/include.scss +48 -48
  341. package/src/util/scss/index.scss +9 -9
  342. package/src/util/shallowEquals.ts +43 -43
  343. package/src/util/test/createTestRenderer.tsx +19 -12
  344. package/src/util/throttle.ts +21 -21
  345. package/src/util/validatedDebounce.ts +23 -23
  346. package/src/variables.scss +217 -217
  347. package/src/widgets/AccessorBindings.spec.tsx +90 -90
  348. package/src/widgets/Button.scss +119 -119
  349. package/src/widgets/Button.tsx +189 -189
  350. package/src/widgets/Button.variables.scss +117 -117
  351. package/src/widgets/CxCredit.scss +39 -39
  352. package/src/widgets/CxCredit.tsx +46 -46
  353. package/src/widgets/DocumentTitle.ts +95 -95
  354. package/src/widgets/FlexBox.scss +148 -148
  355. package/src/widgets/FlexBox.tsx +168 -168
  356. package/src/widgets/Heading.scss +40 -40
  357. package/src/widgets/Heading.ts +42 -42
  358. package/src/widgets/HighlightedSearchText.scss +20 -20
  359. package/src/widgets/HighlightedSearchText.tsx +54 -54
  360. package/src/widgets/HtmlElement.spec.helpers.tsx +108 -108
  361. package/src/widgets/HtmlElement.spec.tsx +89 -89
  362. package/src/widgets/HtmlElement.tsx +407 -407
  363. package/src/widgets/Icon.scss +22 -22
  364. package/src/widgets/Icon.ts +64 -64
  365. package/src/widgets/List.scss +93 -93
  366. package/src/widgets/List.tsx +793 -793
  367. package/src/widgets/ProgressBar.scss +50 -50
  368. package/src/widgets/ProgressBar.tsx +66 -66
  369. package/src/widgets/ReactElementWrapper.spec.tsx +452 -452
  370. package/src/widgets/ReactElementWrapper.tsx +108 -108
  371. package/src/widgets/Resizer.scss +43 -43
  372. package/src/widgets/Resizer.tsx +200 -200
  373. package/src/widgets/Section.scss +55 -55
  374. package/src/widgets/Section.tsx +187 -187
  375. package/src/widgets/animations.scss +10 -10
  376. package/src/widgets/autoFocus.ts +9 -9
  377. package/src/widgets/cx.ts +63 -63
  378. package/src/widgets/drag-drop/DragClone.scss +35 -35
  379. package/src/widgets/drag-drop/DragHandle.scss +18 -18
  380. package/src/widgets/drag-drop/DragHandle.tsx +47 -47
  381. package/src/widgets/drag-drop/DragSource.scss +26 -26
  382. package/src/widgets/drag-drop/DragSource.tsx +237 -237
  383. package/src/widgets/drag-drop/DropZone.scss +76 -76
  384. package/src/widgets/drag-drop/DropZone.tsx +353 -353
  385. package/src/widgets/drag-drop/index.scss +3 -3
  386. package/src/widgets/drag-drop/index.ts +4 -4
  387. package/src/widgets/drag-drop/ops.tsx +422 -422
  388. package/src/widgets/drag-drop/variables.scss +14 -14
  389. package/src/widgets/enableAllInternalDependencies.ts +11 -11
  390. package/src/widgets/form/Calendar.scss +199 -199
  391. package/src/widgets/form/Calendar.tsx +760 -760
  392. package/src/widgets/form/Calendar.variables.scss +63 -63
  393. package/src/widgets/form/Checkbox.scss +129 -129
  394. package/src/widgets/form/Checkbox.tsx +287 -287
  395. package/src/widgets/form/Checkbox.variables.scss +39 -39
  396. package/src/widgets/form/ColorField.scss +98 -98
  397. package/src/widgets/form/ColorField.tsx +497 -497
  398. package/src/widgets/form/ColorPicker.scss +285 -285
  399. package/src/widgets/form/ColorPicker.tsx +544 -544
  400. package/src/widgets/form/ColorPicker.variables.scss +22 -22
  401. package/src/widgets/form/DateField.ts +21 -21
  402. package/src/widgets/form/DateTimeField.scss +92 -92
  403. package/src/widgets/form/DateTimeField.tsx +726 -726
  404. package/src/widgets/form/DateTimePicker.scss +46 -46
  405. package/src/widgets/form/DateTimePicker.tsx +429 -429
  406. package/src/widgets/form/Field.scss +164 -164
  407. package/src/widgets/form/Field.tsx +608 -608
  408. package/src/widgets/form/FieldGroup.ts +10 -10
  409. package/src/widgets/form/FieldIcon.ts +61 -61
  410. package/src/widgets/form/HelpText.scss +24 -24
  411. package/src/widgets/form/HelpText.ts +15 -15
  412. package/src/widgets/form/Label.scss +37 -37
  413. package/src/widgets/form/Label.tsx +117 -117
  414. package/src/widgets/form/LabeledContainer.ts +77 -77
  415. package/src/widgets/form/LookupField.scss +221 -221
  416. package/src/widgets/form/LookupField.spec.tsx +93 -93
  417. package/src/widgets/form/LookupField.tsx +1421 -1421
  418. package/src/widgets/form/MonthField.scss +100 -100
  419. package/src/widgets/form/MonthField.tsx +670 -670
  420. package/src/widgets/form/MonthPicker.scss +125 -125
  421. package/src/widgets/form/MonthPicker.tsx +822 -822
  422. package/src/widgets/form/NumberField.scss +63 -63
  423. package/src/widgets/form/NumberField.tsx +543 -543
  424. package/src/widgets/form/Radio.scss +123 -123
  425. package/src/widgets/form/Radio.tsx +249 -249
  426. package/src/widgets/form/Radio.variables.scss +45 -45
  427. package/src/widgets/form/Select.scss +101 -101
  428. package/src/widgets/form/Select.tsx +327 -327
  429. package/src/widgets/form/Slider.scss +121 -121
  430. package/src/widgets/form/Slider.tsx +473 -473
  431. package/src/widgets/form/Switch.scss +142 -142
  432. package/src/widgets/form/Switch.tsx +176 -176
  433. package/src/widgets/form/TextArea.scss +45 -45
  434. package/src/widgets/form/TextArea.tsx +229 -229
  435. package/src/widgets/form/TextField.scss +57 -57
  436. package/src/widgets/form/TextField.tsx +407 -407
  437. package/src/widgets/form/TimeField.ts +10 -10
  438. package/src/widgets/form/TimeList.tsx +105 -105
  439. package/src/widgets/form/UploadButton.scss +49 -49
  440. package/src/widgets/form/UploadButton.tsx +307 -307
  441. package/src/widgets/form/ValidationError.scss +22 -22
  442. package/src/widgets/form/ValidationError.tsx +72 -72
  443. package/src/widgets/form/ValidationGroup.spec.tsx +147 -147
  444. package/src/widgets/form/ValidationGroup.ts +141 -141
  445. package/src/widgets/form/Validator.ts +60 -60
  446. package/src/widgets/form/Wheel.scss +152 -152
  447. package/src/widgets/form/Wheel.tsx +314 -314
  448. package/src/widgets/form/index.scss +24 -24
  449. package/src/widgets/form/index.ts +28 -28
  450. package/src/widgets/form/variables.scss +355 -355
  451. package/src/widgets/grid/Grid.scss +640 -640
  452. package/src/widgets/grid/Grid.tsx +4243 -4243
  453. package/src/widgets/grid/GridCellEditor.tsx +58 -58
  454. package/src/widgets/grid/GridRow.ts +302 -302
  455. package/src/widgets/grid/GridRowLine.ts +49 -49
  456. package/src/widgets/grid/Pagination.scss +115 -115
  457. package/src/widgets/grid/Pagination.tsx +126 -126
  458. package/src/widgets/grid/TreeNode.scss +90 -90
  459. package/src/widgets/grid/TreeNode.tsx +154 -154
  460. package/src/widgets/grid/createGridCellEditor.tsx +18 -18
  461. package/src/widgets/grid/index.scss +3 -3
  462. package/src/widgets/grid/index.ts +16 -16
  463. package/src/widgets/grid/variables.scss +137 -137
  464. package/src/widgets/icons/arrow-down.svg +3 -3
  465. package/src/widgets/icons/arrow-right.svg +2 -2
  466. package/src/widgets/icons/base.svg +104 -104
  467. package/src/widgets/icons/calendar-old.svg +169 -169
  468. package/src/widgets/icons/calendar.svg +187 -187
  469. package/src/widgets/icons/calendar.tsx +20 -15
  470. package/src/widgets/icons/check.tsx +2 -1
  471. package/src/widgets/icons/clear.svg +74 -74
  472. package/src/widgets/icons/clear.tsx +2 -1
  473. package/src/widgets/icons/close.svg +74 -74
  474. package/src/widgets/icons/close.tsx +2 -2
  475. package/src/widgets/icons/cx.tsx +2 -1
  476. package/src/widgets/icons/drop-down.tsx +2 -1
  477. package/src/widgets/icons/dropdown-arrow.svg +61 -61
  478. package/src/widgets/icons/file.svg +4 -4
  479. package/src/widgets/icons/file.tsx +2 -1
  480. package/src/widgets/icons/folder-open.svg +5 -5
  481. package/src/widgets/icons/folder-open.tsx +2 -1
  482. package/src/widgets/icons/folder.svg +58 -58
  483. package/src/widgets/icons/folder.tsx +2 -1
  484. package/src/widgets/icons/forward.svg +67 -67
  485. package/src/widgets/icons/forward.tsx +2 -1
  486. package/src/widgets/icons/index.ts +14 -14
  487. package/src/widgets/icons/loading.svg +4 -4
  488. package/src/widgets/icons/loading.tsx +2 -1
  489. package/src/widgets/icons/menu.tsx +2 -1
  490. package/src/widgets/icons/pixel-picker.tsx +2 -2
  491. package/src/widgets/icons/registry.ts +56 -56
  492. package/src/widgets/icons/search.svg +107 -107
  493. package/src/widgets/icons/search.tsx +2 -1
  494. package/src/widgets/icons/sort-asc.svg +3 -3
  495. package/src/widgets/icons/sort-asc.tsx +2 -1
  496. package/src/widgets/icons/square.tsx +2 -1
  497. package/src/widgets/index.d.ts +55 -55
  498. package/src/widgets/index.scss +16 -16
  499. package/src/widgets/index.ts +58 -58
  500. package/src/widgets/nav/Link.scss +20 -20
  501. package/src/widgets/nav/Link.ts +11 -11
  502. package/src/widgets/nav/LinkButton.ts +176 -176
  503. package/src/widgets/nav/Menu.scss +76 -76
  504. package/src/widgets/nav/Menu.tsx +489 -489
  505. package/src/widgets/nav/Menu.variables.scss +25 -25
  506. package/src/widgets/nav/MenuItem.scss +130 -130
  507. package/src/widgets/nav/MenuItem.tsx +525 -525
  508. package/src/widgets/nav/MenuSpacer.ts +18 -18
  509. package/src/widgets/nav/RedirectRoute.ts +49 -49
  510. package/src/widgets/nav/Route.spec.tsx +24 -24
  511. package/src/widgets/nav/Scroller.scss +148 -148
  512. package/src/widgets/nav/Scroller.tsx +252 -252
  513. package/src/widgets/nav/Submenu.ts +6 -6
  514. package/src/widgets/nav/Tab.scss +82 -82
  515. package/src/widgets/nav/Tab.ts +120 -120
  516. package/src/widgets/nav/Tab.variables.scss +84 -84
  517. package/src/widgets/nav/cover.scss +22 -22
  518. package/src/widgets/nav/index.scss +5 -5
  519. package/src/widgets/nav/index.ts +10 -10
  520. package/src/widgets/nav/variables.scss +27 -27
  521. package/src/widgets/overlay/ContextMenu.ts +42 -42
  522. package/src/widgets/overlay/Dropdown.scss +186 -186
  523. package/src/widgets/overlay/FlyweightTooltipTracker.ts +57 -57
  524. package/src/widgets/overlay/Overlay.scss +68 -68
  525. package/src/widgets/overlay/Overlay.tsx +947 -947
  526. package/src/widgets/overlay/Toast.scss +163 -163
  527. package/src/widgets/overlay/Tooltip.scss +177 -177
  528. package/src/widgets/overlay/Tooltip.tsx +393 -393
  529. package/src/widgets/overlay/Window.scss +129 -129
  530. package/src/widgets/overlay/Window.variables.scss +62 -62
  531. package/src/widgets/overlay/captureMouse.scss +13 -13
  532. package/src/widgets/overlay/captureMouse.ts +195 -195
  533. package/src/widgets/overlay/createHotPromiseWindowFactory.ts +71 -71
  534. package/src/widgets/overlay/index.scss +15 -15
  535. package/src/widgets/overlay/tooltip-ops.ts +173 -173
  536. package/src/widgets/overlay/variables.scss +85 -85
  537. package/src/widgets/variables.scss +146 -146
  538. package/tsconfig.compile.json +4 -4
  539. package/tsconfig.json +34 -34
  540. package/tsconfig.mocha.json +14 -14
@@ -1,822 +1,822 @@
1
- /**@jsxImportSource react */
2
-
3
- import { Widget, VDOM } from "../../ui/Widget";
4
- import { Field, getFieldTooltip, FieldInstance, FieldConfig } from "./Field";
5
- import { Culture } from "../../ui/Culture";
6
- import { FocusManager, oneFocusOut, offFocusOut, preventFocusOnTouch } from "../../ui/FocusManager";
7
- import { StringTemplate } from "../../data/StringTemplate";
8
- import { monthStart } from "../../util/date/monthStart";
9
- import { dateDiff } from "../../util/date/dateDiff";
10
- import { minDate } from "../../util/date/minDate";
11
- import { maxDate } from "../../util/date/maxDate";
12
- import { lowerBoundCheck } from "../../util/date/lowerBoundCheck";
13
- import { upperBoundCheck } from "../../util/date/upperBoundCheck";
14
- import { Console } from "../../util/Console";
15
- import { KeyCode } from "../../util/KeyCode";
16
- import {
17
- tooltipParentWillReceiveProps,
18
- tooltipParentWillUnmount,
19
- tooltipMouseMove,
20
- tooltipMouseLeave,
21
- tooltipParentDidMount,
22
- } from "../overlay/tooltip-ops";
23
- import { Localization } from "../../ui/Localization";
24
- import { scrollElementIntoView } from "../../util/scrollElementIntoView";
25
- import { stopPropagation } from "../../util/eventCallbacks";
26
- import { isString } from "../../util/isString";
27
- import { isTouchEvent } from "../../util/isTouchEvent";
28
- import { getCursorPos } from "../overlay/captureMouse";
29
- import type { RenderingContext } from "../../ui/RenderingContext";
30
- import type { Instance } from "../../ui/Instance";
31
- import type { Prop, BooleanProp } from "../../ui/Prop";
32
-
33
- import { enableCultureSensitiveFormatting } from "../../ui/Format";
34
- import { parseDateInvariant } from "../../util";
35
- import { HtmlElement } from "../HtmlElement";
36
- enableCultureSensitiveFormatting();
37
-
38
- export class MonthPickerInstance<F extends MonthPicker = MonthPicker> extends FieldInstance<F> {
39
- isMonthDateSelectable?: (monthDate: Date) => boolean;
40
- }
41
-
42
- export interface MonthPickerConfig extends FieldConfig {
43
- range?: BooleanProp;
44
- from?: Prop<string | Date>;
45
- to?: Prop<string | Date>;
46
- value?: Prop<string | Date>;
47
- refDate?: Prop<string | Date>;
48
- minValue?: Prop<string | Date>;
49
- minExclusive?: BooleanProp;
50
- maxValue?: Prop<string | Date>;
51
- maxExclusive?: BooleanProp;
52
- startYear?: number;
53
- endYear?: number;
54
- bufferSize?: number;
55
- maxValueErrorText?: string;
56
- maxExclusiveErrorText?: string;
57
- minValueErrorText?: string;
58
- minExclusiveErrorText?: string;
59
- encoding?: (date: Date) => any;
60
- inclusiveTo?: boolean;
61
- onBeforeSelect?: (e: Event, instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => boolean;
62
- onSelect?: (instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => void;
63
- hideQuarters?: boolean;
64
- onCreateIsMonthDateSelectable?: (
65
- validationParams: Record<string, any>,
66
- instance: MonthPickerInstance,
67
- ) => (monthDate: Date) => boolean;
68
- handleSelect?: (e: React.MouseEvent, instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => void;
69
- onBlur?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
70
- onFocusOut?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
71
- autoFocus?: boolean;
72
- }
73
-
74
- export class MonthPicker<Config extends MonthPickerConfig = MonthPickerConfig> extends Field<
75
- Config,
76
- MonthPickerInstance
77
- > {
78
- declare public baseClass: string;
79
- declare public mode?: string;
80
- declare public range?: BooleanProp;
81
- declare public from?: Prop<string | Date>;
82
- declare public to?: Prop<string | Date>;
83
- declare public value?: Prop<string | Date>;
84
- declare public refDate?: Prop<string | Date>;
85
- declare public minValue?: Prop<string | Date>;
86
- declare public minExclusive?: BooleanProp;
87
- declare public maxValue?: Prop<string | Date>;
88
- declare public maxExclusive?: BooleanProp;
89
- declare public startYear: number;
90
- declare public endYear: number;
91
- declare public bufferSize: number;
92
- declare public maxValueErrorText: string;
93
- declare public maxExclusiveErrorText: string;
94
- declare public minValueErrorText: string;
95
- declare public minExclusiveErrorText: string;
96
- declare public encoding?: (date: Date) => any;
97
- declare public inclusiveTo?: boolean;
98
- declare public onBeforeSelect?: (e: Event, instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => boolean;
99
- declare public onSelect?: (instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => void;
100
- declare public hideQuarters?: boolean;
101
- declare public onCreateIsMonthDateSelectable?: (
102
- validationParams: Record<string, any>,
103
- instance: MonthPickerInstance,
104
- ) => (monthDate: Date) => boolean;
105
- declare public onBlur?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
106
- declare public onFocusOut?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
107
-
108
- declareData(...args: Record<string, unknown>[]): void {
109
- let values: Record<string, unknown> = {};
110
-
111
- if (this.mode == "range") {
112
- this.range = true;
113
- this.mode = "edit";
114
- Console.warn('Please use the range flag on MonthPickers. Syntax mode="range" is deprecated.', this);
115
- }
116
-
117
- if (this.range) {
118
- values = {
119
- from: null,
120
- to: null,
121
- };
122
- } else {
123
- values = {
124
- value: this.emptyValue,
125
- };
126
- }
127
-
128
- super.declareData(
129
- values,
130
- {
131
- refDate: undefined,
132
- disabled: undefined,
133
- minValue: undefined,
134
- minExclusive: undefined,
135
- maxValue: undefined,
136
- maxExclusive: undefined,
137
- },
138
- ...args,
139
- );
140
- }
141
-
142
- init(): void {
143
- super.init();
144
- }
145
-
146
- prepareData(context: RenderingContext, instance: MonthPickerInstance): void {
147
- let { data } = instance;
148
- data.stateMods = {
149
- disabled: data.disabled,
150
- };
151
-
152
- if (!this.range && data.value) data.date = monthStart(parseDateInvariant(data.value));
153
-
154
- if (this.range) {
155
- if (data.from) data.from = monthStart(parseDateInvariant(data.from));
156
-
157
- if (data.to) {
158
- let date = parseDateInvariant(data.to);
159
- if (this.inclusiveTo) date.setDate(date.getDate() + 1);
160
- data.to = monthStart(date);
161
- }
162
- }
163
-
164
- if (data.refDate) data.refDate = monthStart(parseDateInvariant(data.refDate));
165
-
166
- if (data.maxValue) data.maxValue = monthStart(parseDateInvariant(data.maxValue));
167
-
168
- if (data.minValue) data.minValue = monthStart(parseDateInvariant(data.minValue));
169
-
170
- if (this.onCreateIsMonthDateSelectable) {
171
- instance.isMonthDateSelectable = instance.invoke(
172
- "onCreateIsMonthDateSelectable",
173
- data.validationParams,
174
- instance,
175
- );
176
- }
177
-
178
- super.prepareData(context, instance);
179
- }
180
-
181
- validate(context: RenderingContext, instance: MonthPickerInstance): void {
182
- super.validate(context, instance);
183
- let { data } = instance;
184
- if (!data.error && data.date) {
185
- let d;
186
- if (data.maxValue) {
187
- d = dateDiff(data.date, data.maxValue);
188
- if (d > 0) data.error = StringTemplate.format(this.maxValueErrorText, data.maxValue);
189
- else if (d == 0 && data.maxExclusive)
190
- data.error = StringTemplate.format(this.maxExclusiveErrorText, data.maxValue);
191
- }
192
-
193
- if (data.minValue) {
194
- d = dateDiff(data.date, data.minValue);
195
- if (d < 0) data.error = StringTemplate.format(this.minValueErrorText, data.minValue);
196
- else if (d == 0 && data.minExclusive)
197
- data.error = StringTemplate.format(this.minExclusiveErrorText, data.minValue);
198
- }
199
- }
200
- }
201
-
202
- renderInput(context: RenderingContext, instance: MonthPickerInstance, key: string): React.ReactNode {
203
- return <MonthPickerComponent key={key} instance={instance} autoFocus={this.autoFocus} />;
204
- }
205
-
206
- handleSelect(
207
- e: React.KeyboardEvent | React.MouseEvent | React.TouchEvent,
208
- instance: MonthPickerInstance,
209
- date1: Date,
210
- date2: Date,
211
- ): void {
212
- let { data, widget, isMonthDateSelectable } = instance;
213
- let encode = widget.encoding || Culture.getDefaultDateEncoding();
214
-
215
- if (data.disabled) return;
216
-
217
- if (isMonthDateSelectable && !isMonthDateSelectable(date1)) return;
218
- if (!dateSelectableCheck(date1, data)) return;
219
-
220
- if (this.onBeforeSelect && instance.invoke("onBeforeSelect", e, instance, date1, date2) === false) return;
221
-
222
- if (this.range) {
223
- instance.set("from", encode(date1));
224
- let toDate = new Date(date2);
225
- if (this.inclusiveTo) toDate.setDate(toDate.getDate() - 1);
226
- instance.set("to", encode(toDate));
227
- } else instance.set("value", encode(date1));
228
-
229
- if (this.onSelect) instance.invoke("onSelect", instance, date1, date2);
230
- }
231
- }
232
-
233
- MonthPicker.prototype.baseClass = "monthpicker";
234
- MonthPicker.prototype.range = false;
235
- MonthPicker.prototype.startYear = 1980;
236
- MonthPicker.prototype.endYear = 2030;
237
- MonthPicker.prototype.bufferSize = 15;
238
- MonthPicker.prototype.hideQuarters = false;
239
-
240
- // Localization
241
- MonthPicker.prototype.maxValueErrorText = "Select {0:d} or before.";
242
- MonthPicker.prototype.maxExclusiveErrorText = "Select a date before {0:d}.";
243
- MonthPicker.prototype.minValueErrorText = "Select {0:d} or later.";
244
- MonthPicker.prototype.minExclusiveErrorText = "Select a date after {0:d}.";
245
- MonthPicker.prototype.inclusiveTo = false;
246
-
247
- Localization.registerPrototype("cx/widgets/MonthPicker", MonthPicker);
248
-
249
- Widget.alias("month-picker", MonthPicker);
250
-
251
- const dateSelectableCheck = (date: Date, data: Record<string, any>): boolean => {
252
- if (data.maxValue && !upperBoundCheck(date, data.maxValue as Date, data.maxExclusive)) return false;
253
-
254
- if (data.minValue && !lowerBoundCheck(date, data.minValue as Date, data.minExclusive)) return false;
255
-
256
- return true;
257
- };
258
-
259
- const monthNumber = (date: Date): number => {
260
- return date.getFullYear() * 12 + date.getMonth();
261
- };
262
-
263
- interface MonthPickerComponentProps {
264
- instance: MonthPickerInstance;
265
- onBlur?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
266
- onFocusOut?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
267
- onKeyDown?: string | ((e: React.KeyboardEvent, instance: MonthPickerInstance) => boolean | void);
268
- autoFocus?: boolean;
269
- }
270
-
271
- interface MonthPickerComponentState {
272
- cursorYear: number;
273
- cursorMonth: number;
274
- cursorQuarter: number;
275
- column: string;
276
- start: number;
277
- end: number;
278
- state?: string;
279
- hover?: boolean;
280
- focused?: boolean;
281
- yearHeight?: number;
282
- }
283
-
284
- interface CursorInfo {
285
- column: string;
286
- cursorYear: number;
287
- cursorMonth: number;
288
- cursorQuarter: number;
289
- hover?: boolean;
290
- }
291
-
292
- export class MonthPickerComponent extends VDOM.Component<MonthPickerComponentProps, MonthPickerComponentState> {
293
- dom: {
294
- el?: HTMLDivElement | null;
295
- table?: HTMLTableElement | null;
296
- } = {};
297
- dragStartDates?: [Date, Date];
298
-
299
- constructor(props: MonthPickerComponentProps) {
300
- super(props);
301
- let { data, widget } = props.instance;
302
-
303
- let cursor = monthStart(data.refDate ? data.refDate : data.date || data.from || new Date());
304
-
305
- this.dom = {};
306
-
307
- this.state = {
308
- cursorYear: cursor.getFullYear(),
309
- cursorMonth: cursor.getMonth() + 1,
310
- cursorQuarter: cursor.getMonth() / 3,
311
- column: "M",
312
- start: widget.startYear,
313
- end: Math.min(widget.startYear + widget.bufferSize, widget.endYear),
314
- };
315
-
316
- this.handleMouseDown = this.handleMouseDown.bind(this);
317
- this.handleMouseUp = this.handleMouseUp.bind(this);
318
- this.handleMouseEnter = this.handleMouseEnter.bind(this);
319
- this.handleKeyPress = this.handleKeyPress.bind(this);
320
- this.handleTouchMove = this.handleTouchMove.bind(this);
321
- this.handleTouchEnd = this.handleTouchEnd.bind(this);
322
- }
323
-
324
- extractCursorInfo(el: HTMLElement): CursorInfo | false {
325
- const dataPoint = el.getAttribute("data-point");
326
- if (!dataPoint) return false;
327
- let parts = dataPoint.split("-");
328
- if (parts[0] != "Y") return false;
329
- let cursor: CursorInfo = {
330
- column: "Y",
331
- cursorYear: Number(parts[1]),
332
- cursorMonth: 1,
333
- cursorQuarter: 1,
334
- };
335
- if (parts.length == 4) {
336
- cursor.column = parts[2];
337
- if (cursor.column == "M") cursor.cursorMonth = Number(parts[3]);
338
- else cursor.cursorQuarter = Number(parts[3]);
339
- }
340
- return cursor;
341
- }
342
-
343
- moveCursor<K extends keyof MonthPickerComponentState>(
344
- e: React.KeyboardEvent | React.MouseEvent | React.TouchEvent,
345
- data: Pick<MonthPickerComponentState, K>,
346
- options: { ensureVisible?: boolean } = {},
347
- ): void {
348
- e.preventDefault();
349
- e.stopPropagation();
350
-
351
- if ("cursorYear" in data && data.cursorYear !== undefined) {
352
- let { startYear, endYear } = this.props.instance.widget;
353
- (data as any).cursorYear = Math.max(startYear, Math.min(endYear, data.cursorYear as number));
354
- }
355
-
356
- if (Object.keys(data).every((k) => this.state[k as K] == (data as any)[k])) return;
357
-
358
- this.setState(data, () => {
359
- if (options.ensureVisible) {
360
- let index = this.state.cursorYear - this.state.start;
361
- let tbody = this.dom.table?.children?.[index];
362
- if (tbody) scrollElementIntoView(tbody);
363
- }
364
- });
365
- }
366
-
367
- handleKeyPress(e: React.KeyboardEvent): void {
368
- let { instance } = this.props;
369
- let { widget } = this.props.instance;
370
- let { cursorMonth, cursorYear, cursorQuarter, column } = this.state;
371
-
372
- switch (e.keyCode) {
373
- case KeyCode.enter:
374
- // if (widget.range && e.shiftKey && !this.dragStartDates) {
375
- // this.handleMouseDown(e, {}, false);
376
- // } else {
377
- // this.handleMouseUp(e);
378
- // }
379
- this.handleMouseUp(e);
380
- e.preventDefault();
381
- e.stopPropagation();
382
- break;
383
-
384
- case KeyCode.left:
385
- if (column == "Y") this.moveCursor(e, { cursorQuarter: 3, cursorYear: cursorYear - 1, column: "Q" });
386
- else if (column == "Q") this.moveCursor(e, { cursorMonth: cursorQuarter * 4, column: "M" });
387
- else if (column == "M" && (cursorMonth - 1) % 3 == 0) this.moveCursor(e, { column: "Y" });
388
- else this.moveCursor(e, { cursorMonth: cursorMonth - 1 });
389
- break;
390
-
391
- case KeyCode.right:
392
- if (column == "Y") this.moveCursor(e, { cursorMonth: 1, column: "M" });
393
- else if (column == "Q")
394
- this.moveCursor(e, { column: "Y", cursorYear: cursorQuarter == 3 ? cursorYear + 1 : cursorYear });
395
- else if (column == "M" && (cursorMonth - 1) % 3 == 2)
396
- this.moveCursor(e, { column: "Q", cursorQuarter: Math.floor((cursorMonth - 1) / 3) });
397
- else this.moveCursor(e, { cursorMonth: cursorMonth + 1 });
398
- break;
399
-
400
- case KeyCode.up:
401
- if (column == "Y") this.moveCursor(e, { cursorYear: cursorYear - 1 }, { ensureVisible: true });
402
- else if (column == "Q")
403
- this.moveCursor(
404
- e,
405
- {
406
- cursorQuarter: (cursorQuarter + 3) % 4,
407
- cursorYear: cursorQuarter == 0 ? cursorYear - 1 : cursorYear,
408
- },
409
- { ensureVisible: true },
410
- );
411
- else if (column == "M")
412
- if (cursorMonth > 3) this.moveCursor(e, { cursorMonth: cursorMonth - 3 }, { ensureVisible: true });
413
- else
414
- this.moveCursor(
415
- e,
416
- { cursorMonth: cursorMonth + 9, cursorYear: cursorYear - 1 },
417
- { ensureVisible: true },
418
- );
419
- break;
420
-
421
- case KeyCode.down:
422
- if (column == "Y") this.moveCursor(e, { cursorYear: cursorYear + 1 }, { ensureVisible: true });
423
- else if (column == "Q")
424
- this.moveCursor(
425
- e,
426
- {
427
- cursorQuarter: (cursorQuarter + 1) % 4,
428
- cursorYear: cursorQuarter == 3 ? cursorYear + 1 : cursorYear,
429
- },
430
- { ensureVisible: true },
431
- );
432
- else if (column == "M")
433
- if (cursorMonth < 10) this.moveCursor(e, { cursorMonth: cursorMonth + 3 }, { ensureVisible: true });
434
- else
435
- this.moveCursor(
436
- e,
437
- { cursorMonth: cursorMonth - 9, cursorYear: cursorYear + 1 },
438
- { ensureVisible: true },
439
- );
440
- break;
441
-
442
- case KeyCode.pageUp:
443
- this.moveCursor(e, { cursorYear: this.state.cursorYear - 1 });
444
- break;
445
-
446
- case KeyCode.pageDown:
447
- this.moveCursor(e, { cursorYear: this.state.cursorYear + 1 });
448
- break;
449
-
450
- default:
451
- if (widget.onKeyDown) instance.invoke("onKeyDown", e, instance);
452
- break;
453
- }
454
- }
455
-
456
- handleBlur(e: React.FocusEvent): void {
457
- FocusManager.nudge();
458
- let { instance } = this.props;
459
- let { widget } = instance;
460
- if (widget.onBlur) instance.invoke("onBlur", e, instance);
461
- this.setState({
462
- focused: false,
463
- });
464
- }
465
-
466
- handleFocus(e: React.FocusEvent): void {
467
- this.setState({
468
- focused: true,
469
- });
470
- if (this.props.onFocusOut && this.dom.el) oneFocusOut(this, this.dom.el, this.handleFocusOut.bind(this));
471
- }
472
-
473
- handleFocusOut(e: React.FocusEvent): void {
474
- let { instance } = this.props;
475
- let { widget } = instance;
476
- if (widget.onFocusOut) instance.invoke("onFocusOut", e, instance);
477
- }
478
-
479
- getCursorDates(cursor?: CursorInfo): [Date, Date] {
480
- let { cursorMonth, cursorYear, cursorQuarter, column } = cursor ?? this.state;
481
- switch (column) {
482
- case "M":
483
- return [new Date(cursorYear, cursorMonth - 1, 1), new Date(cursorYear, cursorMonth, 1)];
484
-
485
- case "Q":
486
- return [new Date(cursorYear, cursorQuarter * 3, 1), new Date(cursorYear, cursorQuarter * 3 + 3, 1)];
487
-
488
- default:
489
- case "Y":
490
- return [new Date(cursorYear, 0, 1), new Date(cursorYear + 1, 0, 1)];
491
- }
492
- }
493
-
494
- handleTouchMove(e: React.TouchEvent): void {
495
- let cursorPos = getCursorPos(e);
496
- let el = document.elementFromPoint(cursorPos.clientX, cursorPos.clientY);
497
- if (
498
- this.dom.table &&
499
- el &&
500
- this.dom.table.contains(el) &&
501
- el instanceof HTMLElement &&
502
- isString(el.dataset.point)
503
- ) {
504
- let cursor = this.extractCursorInfo(el);
505
- if (cursor) this.moveCursor(e, cursor);
506
- }
507
- }
508
-
509
- handleTouchEnd(e: React.TouchEvent): void {
510
- if (this.state.state == "drag") this.handleMouseUp(e);
511
- }
512
-
513
- handleMouseEnter(e: React.MouseEvent): void {
514
- let cursor = this.extractCursorInfo(e.target as HTMLElement);
515
- if (cursor) {
516
- cursor.hover = !isTouchEvent();
517
- this.moveCursor(e, cursor);
518
- }
519
- }
520
-
521
- handleMouseDown(e: React.MouseEvent | React.TouchEvent, cursor?: CursorInfo | false, drag: boolean = true): void {
522
- let { instance } = this.props;
523
- let { widget } = instance;
524
-
525
- if (!cursor) {
526
- cursor = this.extractCursorInfo(e.currentTarget as HTMLElement);
527
- if (!cursor) return;
528
- this.moveCursor(e, cursor);
529
- }
530
-
531
- e.stopPropagation();
532
- preventFocusOnTouch(e);
533
-
534
- this.dragStartDates = this.getCursorDates(cursor);
535
- if (drag) {
536
- this.setState({
537
- state: "drag",
538
- ...cursor,
539
- });
540
- }
541
- }
542
-
543
- handleMouseUp(e: React.KeyboardEvent | React.MouseEvent | React.TouchEvent): void {
544
- let { instance } = this.props;
545
- let { widget, data } = instance;
546
-
547
- e.stopPropagation();
548
- e.preventDefault();
549
-
550
- let [cursorFromDate, cursorToDate] = this.getCursorDates();
551
- let originFromDate = cursorFromDate,
552
- originToDate = cursorToDate;
553
- if (widget.range && e.shiftKey) {
554
- if (data.from) originFromDate = data.from;
555
- if (data.to) originToDate = data.to;
556
- } else if (this.state.state == "drag") {
557
- if (widget.range) {
558
- [originFromDate, originToDate] = this.dragStartDates!;
559
- }
560
- this.setState({ state: "normal" });
561
- } else {
562
- //skip mouse events originated somewhere else
563
- if (e.type != "keydown") return;
564
- }
565
- widget.handleSelect(e, instance, minDate(originFromDate, cursorFromDate), maxDate(originToDate, cursorToDate));
566
- }
567
-
568
- render(): React.ReactNode {
569
- let { instance } = this.props;
570
- let { data, widget, isMonthDateSelectable } = instance;
571
- let { CSS, baseClass, startYear, endYear, hideQuarters } = widget;
572
-
573
- let years = [];
574
-
575
- let { start, end } = this.state;
576
-
577
- let from = 10000,
578
- to = 0,
579
- a,
580
- b;
581
-
582
- if (data.date && !widget.range) {
583
- from = monthNumber(data.date);
584
- to = from + 0.1;
585
- } else if (widget.range) {
586
- if (this.state.state == "drag") {
587
- let [originFromDate, originToDate] = this.dragStartDates!;
588
- let [cursorFromDate, cursorToDate] = this.getCursorDates();
589
- a = Math.min(monthNumber(originFromDate), monthNumber(cursorFromDate));
590
- b = Math.max(monthNumber(originToDate), monthNumber(cursorToDate));
591
- from = Math.min(a, b);
592
- to = Math.max(a, b);
593
- } else if (data.from && data.to) {
594
- a = monthNumber(data.from);
595
- b = monthNumber(data.to);
596
- from = Math.min(a, b);
597
- to = Math.max(a, b);
598
- }
599
- }
600
-
601
- let monthNames = Culture.getDateTimeCulture().getMonthNames("short");
602
- let showCursor = this.state.hover || this.state.focused;
603
-
604
- for (let y = start; y <= end; y++) {
605
- let selectableMonths = 0b111111111111;
606
- // Loop through the months in a year to check if all months are unselectable
607
- for (let i = 0; i < 12; i++) {
608
- if (
609
- (isMonthDateSelectable && !isMonthDateSelectable(new Date(y, i, 1))) ||
610
- !dateSelectableCheck(new Date(y, i, 1), data)
611
- ) {
612
- // Set month as unselectable at specified bit
613
- selectableMonths &= ~(1 << i);
614
- }
615
- }
616
-
617
- // All bits are 0 - all months are unselectable
618
- const unselectableYear = selectableMonths === 0;
619
-
620
- let rows = [];
621
- for (let q = 0; q < 4; q++) {
622
- let row = [];
623
- if (q == 0) {
624
- row.push(
625
- <th
626
- key="year"
627
- rowSpan={4}
628
- data-point={`Y-${y}`}
629
- className={CSS.expand(
630
- CSS.element(baseClass, "year", {
631
- cursor: showCursor && this.state.column == "Y" && y == this.state.cursorYear,
632
- }),
633
- CSS.state({ unselectable: unselectableYear }),
634
- )}
635
- onMouseEnter={unselectableYear ? undefined : this.handleMouseEnter}
636
- onMouseDown={unselectableYear ? undefined : this.handleMouseDown}
637
- onMouseUp={unselectableYear ? undefined : this.handleMouseUp}
638
- >
639
- {y}
640
- </th>,
641
- );
642
- }
643
-
644
- for (let i = 0; i < 3; i++) {
645
- let m = q * 3 + i + 1;
646
- const unselectableMonth = (selectableMonths & (1 << (m - 1))) === 0;
647
- let mno = y * 12 + m - 1;
648
- let handle = true; //isTouchDevice(); //mno === from || mno === to - 1;
649
- row.push(
650
- <td
651
- key={`M${m}`}
652
- className={CSS.state({
653
- cursor:
654
- showCursor &&
655
- this.state.column == "M" &&
656
- y == this.state.cursorYear &&
657
- m == this.state.cursorMonth,
658
- handle,
659
- selected: mno >= from && mno < to,
660
- unselectable: unselectableMonth,
661
- })}
662
- data-point={`Y-${y}-M-${m}`}
663
- onMouseEnter={unselectableMonth ? undefined : this.handleMouseEnter}
664
- onMouseDown={unselectableMonth ? undefined : this.handleMouseDown}
665
- onMouseUp={unselectableMonth ? undefined : this.handleMouseUp}
666
- onTouchStart={unselectableMonth ? undefined : this.handleMouseDown}
667
- onTouchMove={unselectableMonth ? undefined : this.handleTouchMove}
668
- onTouchEnd={this.handleMouseUp}
669
- >
670
- {monthNames[m - 1].substr(0, 3)}
671
- </td>,
672
- );
673
- }
674
-
675
- if (!hideQuarters) {
676
- let unselectableQuarter = true;
677
- const start = q * 3;
678
- for (let i = start; i < start + 3; i++) {
679
- if ((selectableMonths & (1 << i)) !== 0) {
680
- // found a selectable month in a quarter
681
- unselectableQuarter = false;
682
- break;
683
- }
684
- }
685
-
686
- row.push(
687
- <th
688
- key={`q${q}`}
689
- className={CSS.state({
690
- cursor:
691
- showCursor &&
692
- this.state.column == "Q" &&
693
- y == this.state.cursorYear &&
694
- q == this.state.cursorQuarter,
695
- unselectable: unselectableQuarter,
696
- })}
697
- data-point={`Y-${y}-Q-${q}`}
698
- onMouseEnter={unselectableQuarter ? undefined : this.handleMouseEnter}
699
- onMouseDown={unselectableQuarter ? undefined : this.handleMouseDown}
700
- onMouseUp={unselectableQuarter ? undefined : this.handleMouseUp}
701
- >
702
- {`Q${q + 1}`}
703
- </th>,
704
- );
705
- }
706
-
707
- rows.push(row);
708
- }
709
- years.push(rows);
710
- }
711
-
712
- return (
713
- <div
714
- ref={(el) => {
715
- this.dom.el = el;
716
- }}
717
- className={data.classNames}
718
- style={data.style}
719
- tabIndex={data.disabled ? null : data.tabIndex || 0}
720
- onKeyDown={this.handleKeyPress}
721
- onMouseDown={stopPropagation}
722
- onMouseMove={(e) => tooltipMouseMove(e, ...getFieldTooltip(this.props.instance))}
723
- onMouseLeave={this.handleMouseLeave.bind(this)}
724
- onFocus={(e) => this.handleFocus(e)}
725
- onBlur={this.handleBlur.bind(this)}
726
- onScroll={this.onScroll.bind(this)}
727
- >
728
- {this.state.yearHeight && <div style={{ height: `${(start - startYear) * this.state.yearHeight}px` }} />}
729
- <table
730
- ref={(el) => {
731
- this.dom.table = el;
732
- }}
733
- >
734
- {years.map((rows, y) => (
735
- <tbody key={start + y}>
736
- {rows.map((cells, i) => (
737
- <tr key={i}>{cells}</tr>
738
- ))}
739
- </tbody>
740
- ))}
741
- </table>
742
- {this.state.yearHeight && (
743
- <div style={{ height: `${Math.max(0, endYear - end) * this.state.yearHeight}px` }} />
744
- )}
745
- </div>
746
- );
747
- }
748
-
749
- onScroll(): void {
750
- if (!this.dom.el || !this.state.yearHeight) return;
751
- let { startYear, endYear, bufferSize } = this.props.instance.widget;
752
- let visibleItems = ceil5(Math.ceil(this.dom.el.offsetHeight / this.state.yearHeight));
753
- let start = Math.max(
754
- startYear,
755
- startYear + floor5(Math.floor(this.dom.el.scrollTop / this.state.yearHeight)) - visibleItems,
756
- );
757
- if (start != this.state.start && start + bufferSize <= endYear) {
758
- this.setState({
759
- start,
760
- end: start + 15,
761
- });
762
- }
763
- }
764
-
765
- handleMouseLeave(e: React.MouseEvent): void {
766
- tooltipMouseLeave(e, ...getFieldTooltip(this.props.instance));
767
- this.moveCursor(e, {
768
- hover: false,
769
- });
770
- }
771
-
772
- componentDidMount(): void {
773
- //non-input, ok to focus on mobile
774
- if (this.props.autoFocus && this.dom.el) this.dom.el.focus();
775
-
776
- if (this.dom.el && this.dom.table) {
777
- tooltipParentDidMount(this.dom.el, ...getFieldTooltip(this.props.instance));
778
- let yearHeight = this.dom.table.scrollHeight / (this.props.instance.widget.bufferSize + 1);
779
- this.setState(
780
- {
781
- yearHeight: yearHeight,
782
- },
783
- () => {
784
- let { widget, data } = this.props.instance;
785
- let { startYear } = widget;
786
- let yearCount = 1;
787
- if (widget.range && data.from && data.to) {
788
- yearCount = data.to.getFullYear() - data.from.getFullYear() + 1;
789
- if (data.to.getMonth() == 0 && data.to.getDate() == 1) yearCount--;
790
- }
791
- if (this.dom.el && this.state.yearHeight) {
792
- this.dom.el.scrollTop =
793
- (this.state.cursorYear - startYear + yearCount / 2) * this.state.yearHeight -
794
- this.dom.el.offsetHeight / 2;
795
- }
796
- },
797
- );
798
- }
799
- }
800
-
801
- UNSAFE_componentWillReceiveProps(props: MonthPickerComponentProps): void {
802
- this.setState({
803
- state: "normal",
804
- });
805
- if (this.dom.el) {
806
- tooltipParentWillReceiveProps(this.dom.el, ...getFieldTooltip(props.instance));
807
- }
808
- }
809
-
810
- componentWillUnmount(): void {
811
- offFocusOut(this);
812
- tooltipParentWillUnmount(this.props.instance);
813
- }
814
- }
815
-
816
- function ceil5(x: number): number {
817
- return Math.ceil(x / 5) * 5;
818
- }
819
-
820
- function floor5(x: number): number {
821
- return Math.floor(x / 5) * 5;
822
- }
1
+ /**@jsxImportSource react */
2
+
3
+ import { Widget, VDOM } from "../../ui/Widget";
4
+ import { Field, getFieldTooltip, FieldInstance, FieldConfig } from "./Field";
5
+ import { Culture } from "../../ui/Culture";
6
+ import { FocusManager, oneFocusOut, offFocusOut, preventFocusOnTouch } from "../../ui/FocusManager";
7
+ import { StringTemplate } from "../../data/StringTemplate";
8
+ import { monthStart } from "../../util/date/monthStart";
9
+ import { dateDiff } from "../../util/date/dateDiff";
10
+ import { minDate } from "../../util/date/minDate";
11
+ import { maxDate } from "../../util/date/maxDate";
12
+ import { lowerBoundCheck } from "../../util/date/lowerBoundCheck";
13
+ import { upperBoundCheck } from "../../util/date/upperBoundCheck";
14
+ import { Console } from "../../util/Console";
15
+ import { KeyCode } from "../../util/KeyCode";
16
+ import {
17
+ tooltipParentWillReceiveProps,
18
+ tooltipParentWillUnmount,
19
+ tooltipMouseMove,
20
+ tooltipMouseLeave,
21
+ tooltipParentDidMount,
22
+ } from "../overlay/tooltip-ops";
23
+ import { Localization } from "../../ui/Localization";
24
+ import { scrollElementIntoView } from "../../util/scrollElementIntoView";
25
+ import { stopPropagation } from "../../util/eventCallbacks";
26
+ import { isString } from "../../util/isString";
27
+ import { isTouchEvent } from "../../util/isTouchEvent";
28
+ import { getCursorPos } from "../overlay/captureMouse";
29
+ import type { RenderingContext } from "../../ui/RenderingContext";
30
+ import type { Instance } from "../../ui/Instance";
31
+ import type { Prop, BooleanProp } from "../../ui/Prop";
32
+
33
+ import { enableCultureSensitiveFormatting } from "../../ui/Format";
34
+ import { parseDateInvariant } from "../../util";
35
+ import { HtmlElement } from "../HtmlElement";
36
+ enableCultureSensitiveFormatting();
37
+
38
+ export class MonthPickerInstance<F extends MonthPicker = MonthPicker> extends FieldInstance<F> {
39
+ isMonthDateSelectable?: (monthDate: Date) => boolean;
40
+ }
41
+
42
+ export interface MonthPickerConfig extends FieldConfig {
43
+ range?: BooleanProp;
44
+ from?: Prop<string | Date>;
45
+ to?: Prop<string | Date>;
46
+ value?: Prop<string | Date>;
47
+ refDate?: Prop<string | Date>;
48
+ minValue?: Prop<string | Date>;
49
+ minExclusive?: BooleanProp;
50
+ maxValue?: Prop<string | Date>;
51
+ maxExclusive?: BooleanProp;
52
+ startYear?: number;
53
+ endYear?: number;
54
+ bufferSize?: number;
55
+ maxValueErrorText?: string;
56
+ maxExclusiveErrorText?: string;
57
+ minValueErrorText?: string;
58
+ minExclusiveErrorText?: string;
59
+ encoding?: (date: Date) => any;
60
+ inclusiveTo?: boolean;
61
+ onBeforeSelect?: (e: Event, instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => boolean;
62
+ onSelect?: (instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => void;
63
+ hideQuarters?: boolean;
64
+ onCreateIsMonthDateSelectable?: (
65
+ validationParams: Record<string, any>,
66
+ instance: MonthPickerInstance,
67
+ ) => (monthDate: Date) => boolean;
68
+ handleSelect?: (e: React.MouseEvent, instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => void;
69
+ onBlur?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
70
+ onFocusOut?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
71
+ autoFocus?: boolean;
72
+ }
73
+
74
+ export class MonthPicker<Config extends MonthPickerConfig = MonthPickerConfig> extends Field<
75
+ Config,
76
+ MonthPickerInstance
77
+ > {
78
+ declare public baseClass: string;
79
+ declare public mode?: string;
80
+ declare public range?: BooleanProp;
81
+ declare public from?: Prop<string | Date>;
82
+ declare public to?: Prop<string | Date>;
83
+ declare public value?: Prop<string | Date>;
84
+ declare public refDate?: Prop<string | Date>;
85
+ declare public minValue?: Prop<string | Date>;
86
+ declare public minExclusive?: BooleanProp;
87
+ declare public maxValue?: Prop<string | Date>;
88
+ declare public maxExclusive?: BooleanProp;
89
+ declare public startYear: number;
90
+ declare public endYear: number;
91
+ declare public bufferSize: number;
92
+ declare public maxValueErrorText: string;
93
+ declare public maxExclusiveErrorText: string;
94
+ declare public minValueErrorText: string;
95
+ declare public minExclusiveErrorText: string;
96
+ declare public encoding?: (date: Date) => any;
97
+ declare public inclusiveTo?: boolean;
98
+ declare public onBeforeSelect?: (e: Event, instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => boolean;
99
+ declare public onSelect?: (instance: MonthPickerInstance, dateFrom?: Date, dateTo?: Date) => void;
100
+ declare public hideQuarters?: boolean;
101
+ declare public onCreateIsMonthDateSelectable?: (
102
+ validationParams: Record<string, any>,
103
+ instance: MonthPickerInstance,
104
+ ) => (monthDate: Date) => boolean;
105
+ declare public onBlur?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
106
+ declare public onFocusOut?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
107
+
108
+ declareData(...args: Record<string, unknown>[]): void {
109
+ let values: Record<string, unknown> = {};
110
+
111
+ if (this.mode == "range") {
112
+ this.range = true;
113
+ this.mode = "edit";
114
+ Console.warn('Please use the range flag on MonthPickers. Syntax mode="range" is deprecated.', this);
115
+ }
116
+
117
+ if (this.range) {
118
+ values = {
119
+ from: null,
120
+ to: null,
121
+ };
122
+ } else {
123
+ values = {
124
+ value: this.emptyValue,
125
+ };
126
+ }
127
+
128
+ super.declareData(
129
+ values,
130
+ {
131
+ refDate: undefined,
132
+ disabled: undefined,
133
+ minValue: undefined,
134
+ minExclusive: undefined,
135
+ maxValue: undefined,
136
+ maxExclusive: undefined,
137
+ },
138
+ ...args,
139
+ );
140
+ }
141
+
142
+ init(): void {
143
+ super.init();
144
+ }
145
+
146
+ prepareData(context: RenderingContext, instance: MonthPickerInstance): void {
147
+ let { data } = instance;
148
+ data.stateMods = {
149
+ disabled: data.disabled,
150
+ };
151
+
152
+ if (!this.range && data.value) data.date = monthStart(parseDateInvariant(data.value));
153
+
154
+ if (this.range) {
155
+ if (data.from) data.from = monthStart(parseDateInvariant(data.from));
156
+
157
+ if (data.to) {
158
+ let date = parseDateInvariant(data.to);
159
+ if (this.inclusiveTo) date.setDate(date.getDate() + 1);
160
+ data.to = monthStart(date);
161
+ }
162
+ }
163
+
164
+ if (data.refDate) data.refDate = monthStart(parseDateInvariant(data.refDate));
165
+
166
+ if (data.maxValue) data.maxValue = monthStart(parseDateInvariant(data.maxValue));
167
+
168
+ if (data.minValue) data.minValue = monthStart(parseDateInvariant(data.minValue));
169
+
170
+ if (this.onCreateIsMonthDateSelectable) {
171
+ instance.isMonthDateSelectable = instance.invoke(
172
+ "onCreateIsMonthDateSelectable",
173
+ data.validationParams,
174
+ instance,
175
+ );
176
+ }
177
+
178
+ super.prepareData(context, instance);
179
+ }
180
+
181
+ validate(context: RenderingContext, instance: MonthPickerInstance): void {
182
+ super.validate(context, instance);
183
+ let { data } = instance;
184
+ if (!data.error && data.date) {
185
+ let d;
186
+ if (data.maxValue) {
187
+ d = dateDiff(data.date, data.maxValue);
188
+ if (d > 0) data.error = StringTemplate.format(this.maxValueErrorText, data.maxValue);
189
+ else if (d == 0 && data.maxExclusive)
190
+ data.error = StringTemplate.format(this.maxExclusiveErrorText, data.maxValue);
191
+ }
192
+
193
+ if (data.minValue) {
194
+ d = dateDiff(data.date, data.minValue);
195
+ if (d < 0) data.error = StringTemplate.format(this.minValueErrorText, data.minValue);
196
+ else if (d == 0 && data.minExclusive)
197
+ data.error = StringTemplate.format(this.minExclusiveErrorText, data.minValue);
198
+ }
199
+ }
200
+ }
201
+
202
+ renderInput(context: RenderingContext, instance: MonthPickerInstance, key: string): React.ReactNode {
203
+ return <MonthPickerComponent key={key} instance={instance} autoFocus={this.autoFocus} />;
204
+ }
205
+
206
+ handleSelect(
207
+ e: React.KeyboardEvent | React.MouseEvent | React.TouchEvent,
208
+ instance: MonthPickerInstance,
209
+ date1: Date,
210
+ date2: Date,
211
+ ): void {
212
+ let { data, widget, isMonthDateSelectable } = instance;
213
+ let encode = widget.encoding || Culture.getDefaultDateEncoding();
214
+
215
+ if (data.disabled) return;
216
+
217
+ if (isMonthDateSelectable && !isMonthDateSelectable(date1)) return;
218
+ if (!dateSelectableCheck(date1, data)) return;
219
+
220
+ if (this.onBeforeSelect && instance.invoke("onBeforeSelect", e, instance, date1, date2) === false) return;
221
+
222
+ if (this.range) {
223
+ instance.set("from", encode(date1));
224
+ let toDate = new Date(date2);
225
+ if (this.inclusiveTo) toDate.setDate(toDate.getDate() - 1);
226
+ instance.set("to", encode(toDate));
227
+ } else instance.set("value", encode(date1));
228
+
229
+ if (this.onSelect) instance.invoke("onSelect", instance, date1, date2);
230
+ }
231
+ }
232
+
233
+ MonthPicker.prototype.baseClass = "monthpicker";
234
+ MonthPicker.prototype.range = false;
235
+ MonthPicker.prototype.startYear = 1980;
236
+ MonthPicker.prototype.endYear = 2030;
237
+ MonthPicker.prototype.bufferSize = 15;
238
+ MonthPicker.prototype.hideQuarters = false;
239
+
240
+ // Localization
241
+ MonthPicker.prototype.maxValueErrorText = "Select {0:d} or before.";
242
+ MonthPicker.prototype.maxExclusiveErrorText = "Select a date before {0:d}.";
243
+ MonthPicker.prototype.minValueErrorText = "Select {0:d} or later.";
244
+ MonthPicker.prototype.minExclusiveErrorText = "Select a date after {0:d}.";
245
+ MonthPicker.prototype.inclusiveTo = false;
246
+
247
+ Localization.registerPrototype("cx/widgets/MonthPicker", MonthPicker);
248
+
249
+ Widget.alias("month-picker", MonthPicker);
250
+
251
+ const dateSelectableCheck = (date: Date, data: Record<string, any>): boolean => {
252
+ if (data.maxValue && !upperBoundCheck(date, data.maxValue as Date, data.maxExclusive)) return false;
253
+
254
+ if (data.minValue && !lowerBoundCheck(date, data.minValue as Date, data.minExclusive)) return false;
255
+
256
+ return true;
257
+ };
258
+
259
+ const monthNumber = (date: Date): number => {
260
+ return date.getFullYear() * 12 + date.getMonth();
261
+ };
262
+
263
+ interface MonthPickerComponentProps {
264
+ instance: MonthPickerInstance;
265
+ onBlur?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
266
+ onFocusOut?: string | ((e: React.FocusEvent, instance: MonthPickerInstance) => void);
267
+ onKeyDown?: string | ((e: React.KeyboardEvent, instance: MonthPickerInstance) => boolean | void);
268
+ autoFocus?: boolean;
269
+ }
270
+
271
+ interface MonthPickerComponentState {
272
+ cursorYear: number;
273
+ cursorMonth: number;
274
+ cursorQuarter: number;
275
+ column: string;
276
+ start: number;
277
+ end: number;
278
+ state?: string;
279
+ hover?: boolean;
280
+ focused?: boolean;
281
+ yearHeight?: number;
282
+ }
283
+
284
+ interface CursorInfo {
285
+ column: string;
286
+ cursorYear: number;
287
+ cursorMonth: number;
288
+ cursorQuarter: number;
289
+ hover?: boolean;
290
+ }
291
+
292
+ export class MonthPickerComponent extends VDOM.Component<MonthPickerComponentProps, MonthPickerComponentState> {
293
+ dom: {
294
+ el?: HTMLDivElement | null;
295
+ table?: HTMLTableElement | null;
296
+ } = {};
297
+ dragStartDates?: [Date, Date];
298
+
299
+ constructor(props: MonthPickerComponentProps) {
300
+ super(props);
301
+ let { data, widget } = props.instance;
302
+
303
+ let cursor = monthStart(data.refDate ? data.refDate : data.date || data.from || new Date());
304
+
305
+ this.dom = {};
306
+
307
+ this.state = {
308
+ cursorYear: cursor.getFullYear(),
309
+ cursorMonth: cursor.getMonth() + 1,
310
+ cursorQuarter: cursor.getMonth() / 3,
311
+ column: "M",
312
+ start: widget.startYear,
313
+ end: Math.min(widget.startYear + widget.bufferSize, widget.endYear),
314
+ };
315
+
316
+ this.handleMouseDown = this.handleMouseDown.bind(this);
317
+ this.handleMouseUp = this.handleMouseUp.bind(this);
318
+ this.handleMouseEnter = this.handleMouseEnter.bind(this);
319
+ this.handleKeyPress = this.handleKeyPress.bind(this);
320
+ this.handleTouchMove = this.handleTouchMove.bind(this);
321
+ this.handleTouchEnd = this.handleTouchEnd.bind(this);
322
+ }
323
+
324
+ extractCursorInfo(el: HTMLElement): CursorInfo | false {
325
+ const dataPoint = el.getAttribute("data-point");
326
+ if (!dataPoint) return false;
327
+ let parts = dataPoint.split("-");
328
+ if (parts[0] != "Y") return false;
329
+ let cursor: CursorInfo = {
330
+ column: "Y",
331
+ cursorYear: Number(parts[1]),
332
+ cursorMonth: 1,
333
+ cursorQuarter: 1,
334
+ };
335
+ if (parts.length == 4) {
336
+ cursor.column = parts[2];
337
+ if (cursor.column == "M") cursor.cursorMonth = Number(parts[3]);
338
+ else cursor.cursorQuarter = Number(parts[3]);
339
+ }
340
+ return cursor;
341
+ }
342
+
343
+ moveCursor<K extends keyof MonthPickerComponentState>(
344
+ e: React.KeyboardEvent | React.MouseEvent | React.TouchEvent,
345
+ data: Pick<MonthPickerComponentState, K>,
346
+ options: { ensureVisible?: boolean } = {},
347
+ ): void {
348
+ e.preventDefault();
349
+ e.stopPropagation();
350
+
351
+ if ("cursorYear" in data && data.cursorYear !== undefined) {
352
+ let { startYear, endYear } = this.props.instance.widget;
353
+ (data as any).cursorYear = Math.max(startYear, Math.min(endYear, data.cursorYear as number));
354
+ }
355
+
356
+ if (Object.keys(data).every((k) => this.state[k as K] == (data as any)[k])) return;
357
+
358
+ this.setState(data, () => {
359
+ if (options.ensureVisible) {
360
+ let index = this.state.cursorYear - this.state.start;
361
+ let tbody = this.dom.table?.children?.[index];
362
+ if (tbody) scrollElementIntoView(tbody);
363
+ }
364
+ });
365
+ }
366
+
367
+ handleKeyPress(e: React.KeyboardEvent): void {
368
+ let { instance } = this.props;
369
+ let { widget } = this.props.instance;
370
+ let { cursorMonth, cursorYear, cursorQuarter, column } = this.state;
371
+
372
+ switch (e.keyCode) {
373
+ case KeyCode.enter:
374
+ // if (widget.range && e.shiftKey && !this.dragStartDates) {
375
+ // this.handleMouseDown(e, {}, false);
376
+ // } else {
377
+ // this.handleMouseUp(e);
378
+ // }
379
+ this.handleMouseUp(e);
380
+ e.preventDefault();
381
+ e.stopPropagation();
382
+ break;
383
+
384
+ case KeyCode.left:
385
+ if (column == "Y") this.moveCursor(e, { cursorQuarter: 3, cursorYear: cursorYear - 1, column: "Q" });
386
+ else if (column == "Q") this.moveCursor(e, { cursorMonth: cursorQuarter * 4, column: "M" });
387
+ else if (column == "M" && (cursorMonth - 1) % 3 == 0) this.moveCursor(e, { column: "Y" });
388
+ else this.moveCursor(e, { cursorMonth: cursorMonth - 1 });
389
+ break;
390
+
391
+ case KeyCode.right:
392
+ if (column == "Y") this.moveCursor(e, { cursorMonth: 1, column: "M" });
393
+ else if (column == "Q")
394
+ this.moveCursor(e, { column: "Y", cursorYear: cursorQuarter == 3 ? cursorYear + 1 : cursorYear });
395
+ else if (column == "M" && (cursorMonth - 1) % 3 == 2)
396
+ this.moveCursor(e, { column: "Q", cursorQuarter: Math.floor((cursorMonth - 1) / 3) });
397
+ else this.moveCursor(e, { cursorMonth: cursorMonth + 1 });
398
+ break;
399
+
400
+ case KeyCode.up:
401
+ if (column == "Y") this.moveCursor(e, { cursorYear: cursorYear - 1 }, { ensureVisible: true });
402
+ else if (column == "Q")
403
+ this.moveCursor(
404
+ e,
405
+ {
406
+ cursorQuarter: (cursorQuarter + 3) % 4,
407
+ cursorYear: cursorQuarter == 0 ? cursorYear - 1 : cursorYear,
408
+ },
409
+ { ensureVisible: true },
410
+ );
411
+ else if (column == "M")
412
+ if (cursorMonth > 3) this.moveCursor(e, { cursorMonth: cursorMonth - 3 }, { ensureVisible: true });
413
+ else
414
+ this.moveCursor(
415
+ e,
416
+ { cursorMonth: cursorMonth + 9, cursorYear: cursorYear - 1 },
417
+ { ensureVisible: true },
418
+ );
419
+ break;
420
+
421
+ case KeyCode.down:
422
+ if (column == "Y") this.moveCursor(e, { cursorYear: cursorYear + 1 }, { ensureVisible: true });
423
+ else if (column == "Q")
424
+ this.moveCursor(
425
+ e,
426
+ {
427
+ cursorQuarter: (cursorQuarter + 1) % 4,
428
+ cursorYear: cursorQuarter == 3 ? cursorYear + 1 : cursorYear,
429
+ },
430
+ { ensureVisible: true },
431
+ );
432
+ else if (column == "M")
433
+ if (cursorMonth < 10) this.moveCursor(e, { cursorMonth: cursorMonth + 3 }, { ensureVisible: true });
434
+ else
435
+ this.moveCursor(
436
+ e,
437
+ { cursorMonth: cursorMonth - 9, cursorYear: cursorYear + 1 },
438
+ { ensureVisible: true },
439
+ );
440
+ break;
441
+
442
+ case KeyCode.pageUp:
443
+ this.moveCursor(e, { cursorYear: this.state.cursorYear - 1 });
444
+ break;
445
+
446
+ case KeyCode.pageDown:
447
+ this.moveCursor(e, { cursorYear: this.state.cursorYear + 1 });
448
+ break;
449
+
450
+ default:
451
+ if (widget.onKeyDown) instance.invoke("onKeyDown", e, instance);
452
+ break;
453
+ }
454
+ }
455
+
456
+ handleBlur(e: React.FocusEvent): void {
457
+ FocusManager.nudge();
458
+ let { instance } = this.props;
459
+ let { widget } = instance;
460
+ if (widget.onBlur) instance.invoke("onBlur", e, instance);
461
+ this.setState({
462
+ focused: false,
463
+ });
464
+ }
465
+
466
+ handleFocus(e: React.FocusEvent): void {
467
+ this.setState({
468
+ focused: true,
469
+ });
470
+ if (this.props.onFocusOut && this.dom.el) oneFocusOut(this, this.dom.el, this.handleFocusOut.bind(this));
471
+ }
472
+
473
+ handleFocusOut(e: React.FocusEvent): void {
474
+ let { instance } = this.props;
475
+ let { widget } = instance;
476
+ if (widget.onFocusOut) instance.invoke("onFocusOut", e, instance);
477
+ }
478
+
479
+ getCursorDates(cursor?: CursorInfo): [Date, Date] {
480
+ let { cursorMonth, cursorYear, cursorQuarter, column } = cursor ?? this.state;
481
+ switch (column) {
482
+ case "M":
483
+ return [new Date(cursorYear, cursorMonth - 1, 1), new Date(cursorYear, cursorMonth, 1)];
484
+
485
+ case "Q":
486
+ return [new Date(cursorYear, cursorQuarter * 3, 1), new Date(cursorYear, cursorQuarter * 3 + 3, 1)];
487
+
488
+ default:
489
+ case "Y":
490
+ return [new Date(cursorYear, 0, 1), new Date(cursorYear + 1, 0, 1)];
491
+ }
492
+ }
493
+
494
+ handleTouchMove(e: React.TouchEvent): void {
495
+ let cursorPos = getCursorPos(e);
496
+ let el = document.elementFromPoint(cursorPos.clientX, cursorPos.clientY);
497
+ if (
498
+ this.dom.table &&
499
+ el &&
500
+ this.dom.table.contains(el) &&
501
+ el instanceof HTMLElement &&
502
+ isString(el.dataset.point)
503
+ ) {
504
+ let cursor = this.extractCursorInfo(el);
505
+ if (cursor) this.moveCursor(e, cursor);
506
+ }
507
+ }
508
+
509
+ handleTouchEnd(e: React.TouchEvent): void {
510
+ if (this.state.state == "drag") this.handleMouseUp(e);
511
+ }
512
+
513
+ handleMouseEnter(e: React.MouseEvent): void {
514
+ let cursor = this.extractCursorInfo(e.target as HTMLElement);
515
+ if (cursor) {
516
+ cursor.hover = !isTouchEvent();
517
+ this.moveCursor(e, cursor);
518
+ }
519
+ }
520
+
521
+ handleMouseDown(e: React.MouseEvent | React.TouchEvent, cursor?: CursorInfo | false, drag: boolean = true): void {
522
+ let { instance } = this.props;
523
+ let { widget } = instance;
524
+
525
+ if (!cursor) {
526
+ cursor = this.extractCursorInfo(e.currentTarget as HTMLElement);
527
+ if (!cursor) return;
528
+ this.moveCursor(e, cursor);
529
+ }
530
+
531
+ e.stopPropagation();
532
+ preventFocusOnTouch(e);
533
+
534
+ this.dragStartDates = this.getCursorDates(cursor);
535
+ if (drag) {
536
+ this.setState({
537
+ state: "drag",
538
+ ...cursor,
539
+ });
540
+ }
541
+ }
542
+
543
+ handleMouseUp(e: React.KeyboardEvent | React.MouseEvent | React.TouchEvent): void {
544
+ let { instance } = this.props;
545
+ let { widget, data } = instance;
546
+
547
+ e.stopPropagation();
548
+ e.preventDefault();
549
+
550
+ let [cursorFromDate, cursorToDate] = this.getCursorDates();
551
+ let originFromDate = cursorFromDate,
552
+ originToDate = cursorToDate;
553
+ if (widget.range && e.shiftKey) {
554
+ if (data.from) originFromDate = data.from;
555
+ if (data.to) originToDate = data.to;
556
+ } else if (this.state.state == "drag") {
557
+ if (widget.range) {
558
+ [originFromDate, originToDate] = this.dragStartDates!;
559
+ }
560
+ this.setState({ state: "normal" });
561
+ } else {
562
+ //skip mouse events originated somewhere else
563
+ if (e.type != "keydown") return;
564
+ }
565
+ widget.handleSelect(e, instance, minDate(originFromDate, cursorFromDate), maxDate(originToDate, cursorToDate));
566
+ }
567
+
568
+ render(): React.ReactNode {
569
+ let { instance } = this.props;
570
+ let { data, widget, isMonthDateSelectable } = instance;
571
+ let { CSS, baseClass, startYear, endYear, hideQuarters } = widget;
572
+
573
+ let years = [];
574
+
575
+ let { start, end } = this.state;
576
+
577
+ let from = 10000,
578
+ to = 0,
579
+ a,
580
+ b;
581
+
582
+ if (data.date && !widget.range) {
583
+ from = monthNumber(data.date);
584
+ to = from + 0.1;
585
+ } else if (widget.range) {
586
+ if (this.state.state == "drag") {
587
+ let [originFromDate, originToDate] = this.dragStartDates!;
588
+ let [cursorFromDate, cursorToDate] = this.getCursorDates();
589
+ a = Math.min(monthNumber(originFromDate), monthNumber(cursorFromDate));
590
+ b = Math.max(monthNumber(originToDate), monthNumber(cursorToDate));
591
+ from = Math.min(a, b);
592
+ to = Math.max(a, b);
593
+ } else if (data.from && data.to) {
594
+ a = monthNumber(data.from);
595
+ b = monthNumber(data.to);
596
+ from = Math.min(a, b);
597
+ to = Math.max(a, b);
598
+ }
599
+ }
600
+
601
+ let monthNames = Culture.getDateTimeCulture().getMonthNames("short");
602
+ let showCursor = this.state.hover || this.state.focused;
603
+
604
+ for (let y = start; y <= end; y++) {
605
+ let selectableMonths = 0b111111111111;
606
+ // Loop through the months in a year to check if all months are unselectable
607
+ for (let i = 0; i < 12; i++) {
608
+ if (
609
+ (isMonthDateSelectable && !isMonthDateSelectable(new Date(y, i, 1))) ||
610
+ !dateSelectableCheck(new Date(y, i, 1), data)
611
+ ) {
612
+ // Set month as unselectable at specified bit
613
+ selectableMonths &= ~(1 << i);
614
+ }
615
+ }
616
+
617
+ // All bits are 0 - all months are unselectable
618
+ const unselectableYear = selectableMonths === 0;
619
+
620
+ let rows = [];
621
+ for (let q = 0; q < 4; q++) {
622
+ let row = [];
623
+ if (q == 0) {
624
+ row.push(
625
+ <th
626
+ key="year"
627
+ rowSpan={4}
628
+ data-point={`Y-${y}`}
629
+ className={CSS.expand(
630
+ CSS.element(baseClass, "year", {
631
+ cursor: showCursor && this.state.column == "Y" && y == this.state.cursorYear,
632
+ }),
633
+ CSS.state({ unselectable: unselectableYear }),
634
+ )}
635
+ onMouseEnter={unselectableYear ? undefined : this.handleMouseEnter}
636
+ onMouseDown={unselectableYear ? undefined : this.handleMouseDown}
637
+ onMouseUp={unselectableYear ? undefined : this.handleMouseUp}
638
+ >
639
+ {y}
640
+ </th>,
641
+ );
642
+ }
643
+
644
+ for (let i = 0; i < 3; i++) {
645
+ let m = q * 3 + i + 1;
646
+ const unselectableMonth = (selectableMonths & (1 << (m - 1))) === 0;
647
+ let mno = y * 12 + m - 1;
648
+ let handle = true; //isTouchDevice(); //mno === from || mno === to - 1;
649
+ row.push(
650
+ <td
651
+ key={`M${m}`}
652
+ className={CSS.state({
653
+ cursor:
654
+ showCursor &&
655
+ this.state.column == "M" &&
656
+ y == this.state.cursorYear &&
657
+ m == this.state.cursorMonth,
658
+ handle,
659
+ selected: mno >= from && mno < to,
660
+ unselectable: unselectableMonth,
661
+ })}
662
+ data-point={`Y-${y}-M-${m}`}
663
+ onMouseEnter={unselectableMonth ? undefined : this.handleMouseEnter}
664
+ onMouseDown={unselectableMonth ? undefined : this.handleMouseDown}
665
+ onMouseUp={unselectableMonth ? undefined : this.handleMouseUp}
666
+ onTouchStart={unselectableMonth ? undefined : this.handleMouseDown}
667
+ onTouchMove={unselectableMonth ? undefined : this.handleTouchMove}
668
+ onTouchEnd={this.handleMouseUp}
669
+ >
670
+ {monthNames[m - 1].substr(0, 3)}
671
+ </td>,
672
+ );
673
+ }
674
+
675
+ if (!hideQuarters) {
676
+ let unselectableQuarter = true;
677
+ const start = q * 3;
678
+ for (let i = start; i < start + 3; i++) {
679
+ if ((selectableMonths & (1 << i)) !== 0) {
680
+ // found a selectable month in a quarter
681
+ unselectableQuarter = false;
682
+ break;
683
+ }
684
+ }
685
+
686
+ row.push(
687
+ <th
688
+ key={`q${q}`}
689
+ className={CSS.state({
690
+ cursor:
691
+ showCursor &&
692
+ this.state.column == "Q" &&
693
+ y == this.state.cursorYear &&
694
+ q == this.state.cursorQuarter,
695
+ unselectable: unselectableQuarter,
696
+ })}
697
+ data-point={`Y-${y}-Q-${q}`}
698
+ onMouseEnter={unselectableQuarter ? undefined : this.handleMouseEnter}
699
+ onMouseDown={unselectableQuarter ? undefined : this.handleMouseDown}
700
+ onMouseUp={unselectableQuarter ? undefined : this.handleMouseUp}
701
+ >
702
+ {`Q${q + 1}`}
703
+ </th>,
704
+ );
705
+ }
706
+
707
+ rows.push(row);
708
+ }
709
+ years.push(rows);
710
+ }
711
+
712
+ return (
713
+ <div
714
+ ref={(el) => {
715
+ this.dom.el = el;
716
+ }}
717
+ className={data.classNames}
718
+ style={data.style}
719
+ tabIndex={data.disabled ? null : data.tabIndex || 0}
720
+ onKeyDown={this.handleKeyPress}
721
+ onMouseDown={stopPropagation}
722
+ onMouseMove={(e) => tooltipMouseMove(e, ...getFieldTooltip(this.props.instance))}
723
+ onMouseLeave={this.handleMouseLeave.bind(this)}
724
+ onFocus={(e) => this.handleFocus(e)}
725
+ onBlur={this.handleBlur.bind(this)}
726
+ onScroll={this.onScroll.bind(this)}
727
+ >
728
+ {this.state.yearHeight && <div style={{ height: `${(start - startYear) * this.state.yearHeight}px` }} />}
729
+ <table
730
+ ref={(el) => {
731
+ this.dom.table = el;
732
+ }}
733
+ >
734
+ {years.map((rows, y) => (
735
+ <tbody key={start + y}>
736
+ {rows.map((cells, i) => (
737
+ <tr key={i}>{cells}</tr>
738
+ ))}
739
+ </tbody>
740
+ ))}
741
+ </table>
742
+ {this.state.yearHeight && (
743
+ <div style={{ height: `${Math.max(0, endYear - end) * this.state.yearHeight}px` }} />
744
+ )}
745
+ </div>
746
+ );
747
+ }
748
+
749
+ onScroll(): void {
750
+ if (!this.dom.el || !this.state.yearHeight) return;
751
+ let { startYear, endYear, bufferSize } = this.props.instance.widget;
752
+ let visibleItems = ceil5(Math.ceil(this.dom.el.offsetHeight / this.state.yearHeight));
753
+ let start = Math.max(
754
+ startYear,
755
+ startYear + floor5(Math.floor(this.dom.el.scrollTop / this.state.yearHeight)) - visibleItems,
756
+ );
757
+ if (start != this.state.start && start + bufferSize <= endYear) {
758
+ this.setState({
759
+ start,
760
+ end: start + 15,
761
+ });
762
+ }
763
+ }
764
+
765
+ handleMouseLeave(e: React.MouseEvent): void {
766
+ tooltipMouseLeave(e, ...getFieldTooltip(this.props.instance));
767
+ this.moveCursor(e, {
768
+ hover: false,
769
+ });
770
+ }
771
+
772
+ componentDidMount(): void {
773
+ //non-input, ok to focus on mobile
774
+ if (this.props.autoFocus && this.dom.el) this.dom.el.focus();
775
+
776
+ if (this.dom.el && this.dom.table) {
777
+ tooltipParentDidMount(this.dom.el, ...getFieldTooltip(this.props.instance));
778
+ let yearHeight = this.dom.table.scrollHeight / (this.props.instance.widget.bufferSize + 1);
779
+ this.setState(
780
+ {
781
+ yearHeight: yearHeight,
782
+ },
783
+ () => {
784
+ let { widget, data } = this.props.instance;
785
+ let { startYear } = widget;
786
+ let yearCount = 1;
787
+ if (widget.range && data.from && data.to) {
788
+ yearCount = data.to.getFullYear() - data.from.getFullYear() + 1;
789
+ if (data.to.getMonth() == 0 && data.to.getDate() == 1) yearCount--;
790
+ }
791
+ if (this.dom.el && this.state.yearHeight) {
792
+ this.dom.el.scrollTop =
793
+ (this.state.cursorYear - startYear + yearCount / 2) * this.state.yearHeight -
794
+ this.dom.el.offsetHeight / 2;
795
+ }
796
+ },
797
+ );
798
+ }
799
+ }
800
+
801
+ UNSAFE_componentWillReceiveProps(props: MonthPickerComponentProps): void {
802
+ this.setState({
803
+ state: "normal",
804
+ });
805
+ if (this.dom.el) {
806
+ tooltipParentWillReceiveProps(this.dom.el, ...getFieldTooltip(props.instance));
807
+ }
808
+ }
809
+
810
+ componentWillUnmount(): void {
811
+ offFocusOut(this);
812
+ tooltipParentWillUnmount(this.props.instance);
813
+ }
814
+ }
815
+
816
+ function ceil5(x: number): number {
817
+ return Math.ceil(x / 5) * 5;
818
+ }
819
+
820
+ function floor5(x: number): number {
821
+ return Math.floor(x / 5) * 5;
822
+ }