@geotab/zenith 3.1.1-beta.6 → 3.2.0-beta.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 (434) hide show
  1. package/README.md +20 -0
  2. package/dist/advancedGroupsFilter/advancedGroupsFilter.js +136 -30
  3. package/dist/advancedGroupsFilter/advancedGroupsFilterForm.js +139 -34
  4. package/dist/advancedGroupsFilter/advancedGroupsFilterFormSection.js +352 -87
  5. package/dist/advancedGroupsFilter/advancedGroupsFilterSectionTooltip.d.ts +0 -1
  6. package/dist/advancedGroupsFilter/advancedGroupsFilterSectionTooltip.js +83 -9
  7. package/dist/alertRaw/alertRaw.js +175 -54
  8. package/dist/banner/bannerMultipLine.js +131 -23
  9. package/dist/banner/bannerSingleLine.js +110 -16
  10. package/dist/betaPill/betaPill.js +111 -20
  11. package/dist/bookmark/bookmark.js +95 -28
  12. package/dist/bulkEditControl/bulkEditControl.js +167 -34
  13. package/dist/calendar/calendar.js +943 -273
  14. package/dist/calendar/calendarUtils.js +157 -85
  15. package/dist/card/card.js +268 -123
  16. package/dist/chart/accessibleChart/accessibleChartNarrative.js +648 -555
  17. package/dist/chart/accessibleChart/accessibleChartTable.js +245 -86
  18. package/dist/chart/chart.js +36 -21
  19. package/dist/chart/chartAxis/chartAxis.js +85 -7
  20. package/dist/checkboxListWithAction/checkboxListWithAction.js +224 -69
  21. package/dist/chip/chip.js +195 -91
  22. package/dist/columnsSelector/columnsSelector.js +97 -12
  23. package/dist/columnsSelector/columnsTab/columnsTab.js +59 -15
  24. package/dist/columnsSelector/columnsTabGroup/columnsTabGroup.js +81 -34
  25. package/dist/comboboxSelected/comboboxSelected.js +1 -3
  26. package/dist/dataFeed/feedExpandControl/feedExpandControl.js +25 -10
  27. package/dist/dataGrid/columns/checkboxColumn/checkboxHeaderCell.js +92 -11
  28. package/dist/dataGrid/dataGrid.js +227 -117
  29. package/dist/dataGrid/emptySearchList/emptySearchList.js +56 -9
  30. package/dist/dataGrid/entitiesListActions/actions/columnsListButton.js +51 -7
  31. package/dist/dataGrid/entitiesListActions/actions/fullscreenButton.js +64 -18
  32. package/dist/dataGrid/withFlexibleColumns/components/columnSettings.js +84 -10
  33. package/dist/dataGrid/withFlexibleColumns/components/columnSettingsSidePanel.js +48 -16
  34. package/dist/dataGrid/withSelectableRows/components/bulkActions/bulkActions.js +223 -32
  35. package/dist/dataGrid/withSelectableRows/withSelectableRows.js +286 -213
  36. package/dist/dataGrid/withSortableColumns/columns/sortableColumnWrapper.js +178 -95
  37. package/dist/dateInputInner/dateInputInner.js +791 -476
  38. package/dist/dateInputInner/dateInputInnerControlBlock.js +125 -22
  39. package/dist/dateInputRaw/dateInputRaw.js +315 -104
  40. package/dist/dateInputRaw/utils/getLabel.js +38 -7
  41. package/dist/dateRangeInner/dateRangeInner.js +173 -59
  42. package/dist/dateRangeRaw/dateRangeRaw.js +601 -239
  43. package/dist/dateRangeRaw/utils/dateRangeUtils.js +629 -241
  44. package/dist/dateSelectionWrapper/dateSelectionWrapper.js +152 -14
  45. package/dist/dialog/dialogContent.js +123 -40
  46. package/dist/dropdownRaw/dropdownHelper.d.ts +2 -2
  47. package/dist/dropdownRaw/dropdownHelper.js +9 -9
  48. package/dist/dropdownRaw/dropdownList.js +447 -78
  49. package/dist/dropdownRaw/dropdownPopup.js +218 -20
  50. package/dist/dropdownRaw/dropdownRaw.js +866 -506
  51. package/dist/dropdownRaw/dropdownSearchableTrigger.js +223 -46
  52. package/dist/dropdownRaw/stateReducer/stateAction.d.ts +5 -1
  53. package/dist/dropdownRaw/stateReducer/stateActionType.d.ts +2 -1
  54. package/dist/dropdownRaw/stateReducer/stateActionType.js +1 -0
  55. package/dist/dropdownRaw/stateReducer/stateReducer.d.ts +6 -1
  56. package/dist/dropdownRaw/stateReducer/stateReducer.js +24 -12
  57. package/dist/dropdownRaw/stateReducer/stateReducerHelper.d.ts +2 -0
  58. package/dist/dropdownRaw/stateReducer/stateReducerHelper.js +20 -1
  59. package/dist/dropdownRaw/stateReducer/stateReducerTestData.d.ts +39 -0
  60. package/dist/dropdownRaw/stateReducer/stateReducerTestData.js +74 -0
  61. package/dist/dropdownRaw/types.d.ts +1 -0
  62. package/dist/favoriteButton/favoriteButton.js +59 -10
  63. package/dist/filters/components/filtersContainer.js +151 -64
  64. package/dist/filters/components/filtersEmptySelectedList.js +30 -4
  65. package/dist/filters/components/filtersSaveModal.js +140 -42
  66. package/dist/filters/components/filtersSavedChipComponent.js +318 -108
  67. package/dist/filters/components/filtersSearchItemData.js +127 -47
  68. package/dist/filters/components/filtersSearchList.js +381 -179
  69. package/dist/filters/components/filtersSelect.js +128 -61
  70. package/dist/filters/components/filtersSelectListItem.js +125 -13
  71. package/dist/filters/components/filtersSidePanel.js +510 -178
  72. package/dist/filters/components/filtersSidePanelDropdown.js +2 -2
  73. package/dist/filters/filters.js +445 -268
  74. package/dist/filtersBar/components/filtersBarPeriodPicker/getRangeOption.js +729 -272
  75. package/dist/filtersBar/components/resetComponentButton.js +45 -5
  76. package/dist/filtersBar/filtersBarActions/filtersBarActions.js +126 -15
  77. package/dist/filtersBar/filtersBarSidePanel/components/filtersBarSidePanelRadioGroup/filtersBarSidePanelRadioGroup.js +2 -2
  78. package/dist/filtersBar/filtersBarSidePanel/components/filtersBarSidePanelRange/filtersBarSidePanelRange.js +155 -49
  79. package/dist/filtersBar/filtersBarSidePanel/filtersBarSidePanel.js +360 -104
  80. package/dist/filtersBar/filtersContainer/filtersContainer.js +204 -134
  81. package/dist/formField/components/formFieldWithLabel.d.ts +2 -1
  82. package/dist/formField/components/formFieldWithLabel.js +3 -2
  83. package/dist/formField/components/formFieldWithoutLabel.d.ts +2 -1
  84. package/dist/formField/components/formFieldWithoutLabel.js +3 -3
  85. package/dist/formField/components/trailingComponent.d.ts +8 -0
  86. package/dist/formField/components/trailingComponent.js +11 -0
  87. package/dist/formField/formField.js +26 -11
  88. package/dist/formField/hooks/useError.js +100 -36
  89. package/dist/formGroup/components/reviewListToggle/reviewListToggle.js +72 -7
  90. package/dist/formGroup/hooks/useToggle.js +37 -11
  91. package/dist/formGroup/utils/getControls.js +8 -7
  92. package/dist/formLayout/hooks/useError.js +55 -15
  93. package/dist/formLayout/hooks/useFormButtons.js +128 -27
  94. package/dist/formSection/components/formSectionModal.js +1 -1
  95. package/dist/formSection/formSection.js +1 -1
  96. package/dist/formSection/hooks/useError.js +70 -22
  97. package/dist/formStepper/components/formStep.js +65 -10
  98. package/dist/formStepper/formStepper.js +129 -33
  99. package/dist/formStepperButtons/formStepperButtons.js +184 -38
  100. package/dist/groupsFilter/groupsHelper.d.ts +1 -1
  101. package/dist/groupsFilterRaw/groupsFilterAdjustmentState.js +162 -17
  102. package/dist/groupsFilterRaw/groupsFilterBox.js +137 -32
  103. package/dist/groupsFilterRaw/groupsFilterCommon.js +75 -8
  104. package/dist/groupsFilterRaw/groupsFilterCurrentlySelectedState.js +184 -25
  105. package/dist/groupsFilterRaw/groupsFilterHelper.d.ts +2 -1
  106. package/dist/groupsFilterRaw/groupsFilterHelper.js +284 -168
  107. package/dist/groupsFilterRaw/groupsFilterInitialState.js +266 -18
  108. package/dist/groupsFilterRaw/groupsFilterMenu.js +124 -9
  109. package/dist/groupsFilterRaw/groupsFilterRaw.d.ts +1 -0
  110. package/dist/groupsFilterRaw/groupsFilterRaw.js +816 -308
  111. package/dist/groupsFilterRaw/groupsFilterTestData.d.ts +155 -2
  112. package/dist/groupsFilterRaw/groupsFilterTestData.js +153 -58
  113. package/dist/groupsFilterRaw/groupsFilterTrigger.js +139 -35
  114. package/dist/groupsFilterRaw/groupsHelper.js +739 -208
  115. package/dist/groupsFilterRaw/stateReducer/stateReducerHelper.js +3 -1
  116. package/dist/groupsFilterRaw/types.d.ts +1 -0
  117. package/dist/header/components/collapsedItemsControl/collapsedItemsControl.js +107 -52
  118. package/dist/header/components/mobileFilterControl/mobileFilterControl.js +62 -9
  119. package/dist/header/components/mobileSearchControl/mobileSearchControl.js +119 -14
  120. package/dist/header/headerBack.js +64 -20
  121. package/dist/index.css +116 -74
  122. package/dist/index.d.ts +1 -0
  123. package/dist/index.js +4 -1
  124. package/dist/list/itemData/itemDataInternal.js +216 -51
  125. package/dist/list/listItem/listItem.js +168 -55
  126. package/dist/menu/components/menuErrorItem.js +33 -5
  127. package/dist/mobileSheet/mobileSheet.js +195 -69
  128. package/dist/modal/modal.js +300 -142
  129. package/dist/nav/nav.js +1 -1
  130. package/dist/nav/navFooter/navFooter.js +82 -39
  131. package/dist/nav/navFooter/navFooterAction/navFooterAction.js +52 -13
  132. package/dist/nav/navHeader/navHeader.js +86 -36
  133. package/dist/nav/navHeader/navHeaderSearch/navHeaderSearch.js +88 -29
  134. package/dist/nav/navItem/navItem.d.ts +3 -3
  135. package/dist/nav/navItem/navItem.js +35 -33
  136. package/dist/nav/navMobileBar/navMobileBar.js +67 -21
  137. package/dist/notification/notification.js +124 -21
  138. package/dist/pagination/paginationArrow.js +81 -11
  139. package/dist/pagination/paginationText/paginationText.js +45 -11
  140. package/dist/pill/components/pillNonActionable/pillNonActionable.js +93 -24
  141. package/dist/pillBox/components/pillBoxItem.js +52 -9
  142. package/dist/pillBox/pillBox.js +121 -20
  143. package/dist/pillExpandable/pillExpandable.js +333 -139
  144. package/dist/rangeRaw/rangeRaw.js +486 -141
  145. package/dist/rangeRaw/utils/rangeHelper.js +209 -39
  146. package/dist/searchInputRaw/searchInputRaw.js +180 -65
  147. package/dist/skeleton/skeleton.js +51 -6
  148. package/dist/sortControl/sortControl.js +152 -42
  149. package/dist/stepperRaw/stepperRaw.js +116 -42
  150. package/dist/summary/summary.js +94 -8
  151. package/dist/table/actions/actionsMenu.js +171 -78
  152. package/dist/table/flexible/columnSettings.js +80 -10
  153. package/dist/table/flexible/columnsList.js +110 -43
  154. package/dist/table/flexible/columnsPopup.js +77 -20
  155. package/dist/table/nested/useNestedRows.js +167 -77
  156. package/dist/table/selectable/selectableHeader.js +180 -41
  157. package/dist/table/selectable/useSelectableRows.js +270 -191
  158. package/dist/table/sortable/sortableHeader.js +153 -75
  159. package/dist/tabs/tabs.js +227 -118
  160. package/dist/timePickerRaw/timePickerRaw.js +278 -58
  161. package/dist/toastRaw/toastRaw.js +138 -32
  162. package/dist/toggleButton/toggleButton.d.ts +0 -1
  163. package/dist/toggleButtonRaw/toggleButtonRaw.d.ts +1 -0
  164. package/dist/toggleButtonRaw/toggleButtonRaw.js +146 -40
  165. package/dist/utils/formatDate.js +1001 -117
  166. package/{esm/utils/localization/translations/cs-json.js → dist/utils/localization/translations/cs.json} +11 -12
  167. package/{esm/utils/localization/translations/da-DK-json.js → dist/utils/localization/translations/da-DK.json} +31 -23
  168. package/{esm/utils/localization/translations/de-json.js → dist/utils/localization/translations/de.json} +12 -13
  169. package/dist/utils/localization/translations/en.json +308 -0
  170. package/{esm/utils/localization/translations/es-json.js → dist/utils/localization/translations/es.json} +11 -12
  171. package/{esm/utils/localization/translations/fi-FI-json.js → dist/utils/localization/translations/fi-FI.json} +31 -23
  172. package/{esm/utils/localization/translations/fr-FR-json.js → dist/utils/localization/translations/fr-FR.json} +12 -12
  173. package/{esm/utils/localization/translations/fr-json.js → dist/utils/localization/translations/fr.json} +11 -12
  174. package/{esm/utils/localization/translations/hu-HU-json.js → dist/utils/localization/translations/hu-HU.json} +31 -23
  175. package/{esm/utils/localization/translations/id-json.js → dist/utils/localization/translations/id.json} +11 -13
  176. package/{esm/utils/localization/translations/it-json.js → dist/utils/localization/translations/it.json} +11 -12
  177. package/{esm/utils/localization/translations/ja-json.js → dist/utils/localization/translations/ja.json} +11 -12
  178. package/{esm/utils/localization/translations/ko-KR-json.js → dist/utils/localization/translations/ko-KR.json} +24 -23
  179. package/{esm/utils/localization/translations/ms-json.js → dist/utils/localization/translations/ms.json} +11 -12
  180. package/{esm/utils/localization/translations/nb-NO-json.js → dist/utils/localization/translations/nb-NO.json} +31 -23
  181. package/{esm/utils/localization/translations/nl-json.js → dist/utils/localization/translations/nl.json} +11 -12
  182. package/{esm/utils/localization/translations/pl-json.js → dist/utils/localization/translations/pl.json} +11 -12
  183. package/{esm/utils/localization/translations/pt-BR-json.js → dist/utils/localization/translations/pt-BR.json} +11 -12
  184. package/{esm/utils/localization/translations/sk-SK-json.js → dist/utils/localization/translations/sk-SK.json} +31 -23
  185. package/{esm/utils/localization/translations/sv-json.js → dist/utils/localization/translations/sv.json} +11 -12
  186. package/{esm/utils/localization/translations/th-json.js → dist/utils/localization/translations/th.json} +11 -12
  187. package/{esm/utils/localization/translations/tr-json.js → dist/utils/localization/translations/tr.json} +11 -12
  188. package/{esm/utils/localization/translations/zh-Hans-json.js → dist/utils/localization/translations/zh-Hans.json} +11 -12
  189. package/{esm/utils/localization/translations/zh-TW-json.js → dist/utils/localization/translations/zh-TW.json} +11 -23
  190. package/dist/utils/localization/translationsDictionary.d.ts +2 -0
  191. package/dist/utils/localization/translationsDictionary.js +63 -0
  192. package/dist/utils/localization/useLanguage.js +2 -74
  193. package/esm/advancedGroupsFilter/advancedGroupsFilter.js +130 -29
  194. package/esm/advancedGroupsFilter/advancedGroupsFilterForm.js +133 -33
  195. package/esm/advancedGroupsFilter/advancedGroupsFilterFormSection.js +317 -65
  196. package/esm/advancedGroupsFilter/advancedGroupsFilterSectionTooltip.d.ts +0 -1
  197. package/esm/advancedGroupsFilter/advancedGroupsFilterSectionTooltip.js +77 -8
  198. package/esm/alertRaw/alertRaw.js +165 -51
  199. package/esm/banner/bannerMultipLine.js +121 -20
  200. package/esm/banner/bannerSingleLine.js +100 -13
  201. package/esm/betaPill/betaPill.js +105 -19
  202. package/esm/bookmark/bookmark.js +89 -27
  203. package/esm/bulkEditControl/bulkEditControl.js +161 -33
  204. package/esm/calendar/calendar.js +937 -272
  205. package/esm/calendar/calendarUtils.js +151 -84
  206. package/esm/card/card.js +233 -101
  207. package/esm/chart/accessibleChart/accessibleChartNarrative.js +642 -554
  208. package/esm/chart/accessibleChart/accessibleChartTable.js +239 -85
  209. package/esm/chart/chart.js +30 -20
  210. package/esm/chart/chartAxis/chartAxis.js +79 -6
  211. package/esm/checkboxListWithAction/checkboxListWithAction.js +218 -68
  212. package/esm/chip/chip.js +189 -90
  213. package/esm/columnsSelector/columnsSelector.js +91 -11
  214. package/esm/columnsSelector/columnsTab/columnsTab.js +53 -14
  215. package/esm/columnsSelector/columnsTabGroup/columnsTabGroup.js +75 -33
  216. package/esm/comboboxSelected/comboboxSelected.js +1 -3
  217. package/esm/dataFeed/feedExpandControl/feedExpandControl.js +21 -9
  218. package/esm/dataGrid/columns/checkboxColumn/checkboxHeaderCell.js +86 -10
  219. package/esm/dataGrid/dataGrid.js +221 -116
  220. package/esm/dataGrid/emptySearchList/emptySearchList.js +50 -8
  221. package/esm/dataGrid/entitiesListActions/actions/columnsListButton.js +45 -6
  222. package/esm/dataGrid/entitiesListActions/actions/fullscreenButton.js +58 -17
  223. package/esm/dataGrid/withFlexibleColumns/components/columnSettings.js +78 -9
  224. package/esm/dataGrid/withFlexibleColumns/components/columnSettingsSidePanel.js +44 -15
  225. package/esm/dataGrid/withSelectableRows/components/bulkActions/bulkActions.js +217 -31
  226. package/esm/dataGrid/withSelectableRows/withSelectableRows.js +280 -212
  227. package/esm/dataGrid/withSortableColumns/columns/sortableColumnWrapper.js +172 -94
  228. package/esm/dateInputInner/dateInputInner.js +785 -475
  229. package/esm/dateInputInner/dateInputInnerControlBlock.js +119 -21
  230. package/esm/dateInputRaw/dateInputRaw.js +309 -103
  231. package/esm/dateInputRaw/utils/getLabel.js +32 -6
  232. package/esm/dateRangeInner/dateRangeInner.js +167 -58
  233. package/esm/dateRangeRaw/dateRangeRaw.js +595 -238
  234. package/esm/dateRangeRaw/utils/dateRangeUtils.js +622 -239
  235. package/esm/dateSelectionWrapper/dateSelectionWrapper.js +146 -13
  236. package/esm/dialog/dialogContent.js +117 -39
  237. package/esm/dropdownRaw/dropdownHelper.d.ts +2 -2
  238. package/esm/dropdownRaw/dropdownHelper.js +10 -10
  239. package/esm/dropdownRaw/dropdownList.js +412 -56
  240. package/esm/dropdownRaw/dropdownPopup.js +212 -19
  241. package/esm/dropdownRaw/dropdownRaw.js +862 -507
  242. package/esm/dropdownRaw/dropdownSearchableTrigger.js +217 -45
  243. package/esm/dropdownRaw/stateReducer/stateAction.d.ts +5 -1
  244. package/esm/dropdownRaw/stateReducer/stateActionType.d.ts +2 -1
  245. package/esm/dropdownRaw/stateReducer/stateActionType.js +1 -0
  246. package/esm/dropdownRaw/stateReducer/stateReducer.d.ts +6 -1
  247. package/esm/dropdownRaw/stateReducer/stateReducer.js +24 -12
  248. package/esm/dropdownRaw/stateReducer/stateReducerHelper.d.ts +2 -0
  249. package/esm/dropdownRaw/stateReducer/stateReducerHelper.js +18 -0
  250. package/esm/dropdownRaw/stateReducer/stateReducerTestData.d.ts +39 -0
  251. package/esm/dropdownRaw/stateReducer/stateReducerTestData.js +71 -0
  252. package/esm/dropdownRaw/types.d.ts +1 -0
  253. package/esm/favoriteButton/favoriteButton.js +53 -9
  254. package/esm/filters/components/filtersContainer.js +141 -61
  255. package/esm/filters/components/filtersEmptySelectedList.js +24 -3
  256. package/esm/filters/components/filtersSaveModal.js +134 -41
  257. package/esm/filters/components/filtersSavedChipComponent.js +312 -107
  258. package/esm/filters/components/filtersSearchItemData.js +121 -46
  259. package/esm/filters/components/filtersSearchList.js +375 -178
  260. package/esm/filters/components/filtersSelect.js +122 -60
  261. package/esm/filters/components/filtersSelectListItem.js +119 -12
  262. package/esm/filters/components/filtersSidePanel.js +504 -177
  263. package/esm/filters/components/filtersSidePanelDropdown.js +2 -2
  264. package/esm/filters/filters.js +435 -265
  265. package/esm/filtersBar/components/filtersBarPeriodPicker/getRangeOption.js +722 -270
  266. package/esm/filtersBar/components/resetComponentButton.js +39 -4
  267. package/esm/filtersBar/filtersBarActions/filtersBarActions.js +120 -14
  268. package/esm/filtersBar/filtersBarSidePanel/components/filtersBarSidePanelRadioGroup/filtersBarSidePanelRadioGroup.js +2 -2
  269. package/esm/filtersBar/filtersBarSidePanel/components/filtersBarSidePanelRange/filtersBarSidePanelRange.js +149 -48
  270. package/esm/filtersBar/filtersBarSidePanel/filtersBarSidePanel.js +354 -103
  271. package/esm/filtersBar/filtersContainer/filtersContainer.js +198 -133
  272. package/esm/formField/components/formFieldWithLabel.d.ts +2 -1
  273. package/esm/formField/components/formFieldWithLabel.js +3 -2
  274. package/esm/formField/components/formFieldWithoutLabel.d.ts +2 -1
  275. package/esm/formField/components/formFieldWithoutLabel.js +3 -3
  276. package/esm/formField/components/trailingComponent.d.ts +8 -0
  277. package/esm/formField/components/trailingComponent.js +7 -0
  278. package/esm/formField/formField.js +27 -12
  279. package/esm/formField/hooks/useError.js +94 -35
  280. package/esm/formGroup/components/reviewListToggle/reviewListToggle.js +66 -6
  281. package/esm/formGroup/hooks/useToggle.js +31 -10
  282. package/esm/formGroup/utils/getControls.js +8 -7
  283. package/esm/formLayout/hooks/useError.js +49 -14
  284. package/esm/formLayout/hooks/useFormButtons.js +122 -26
  285. package/esm/formSection/components/formSectionModal.js +1 -1
  286. package/esm/formSection/formSection.js +1 -1
  287. package/esm/formSection/hooks/useError.js +64 -21
  288. package/esm/formStepper/components/formStep.js +59 -9
  289. package/esm/formStepper/formStepper.js +123 -32
  290. package/esm/formStepperButtons/formStepperButtons.js +178 -37
  291. package/esm/groupsFilter/groupsHelper.d.ts +1 -1
  292. package/esm/groupsFilterRaw/groupsFilterAdjustmentState.js +152 -14
  293. package/esm/groupsFilterRaw/groupsFilterBox.js +131 -31
  294. package/esm/groupsFilterRaw/groupsFilterCommon.js +69 -7
  295. package/esm/groupsFilterRaw/groupsFilterCurrentlySelectedState.js +178 -24
  296. package/esm/groupsFilterRaw/groupsFilterHelper.d.ts +2 -1
  297. package/esm/groupsFilterRaw/groupsFilterHelper.js +279 -168
  298. package/esm/groupsFilterRaw/groupsFilterInitialState.js +260 -17
  299. package/esm/groupsFilterRaw/groupsFilterMenu.js +118 -8
  300. package/esm/groupsFilterRaw/groupsFilterRaw.d.ts +1 -0
  301. package/esm/groupsFilterRaw/groupsFilterRaw.js +811 -308
  302. package/esm/groupsFilterRaw/groupsFilterTestData.d.ts +155 -2
  303. package/esm/groupsFilterRaw/groupsFilterTestData.js +152 -57
  304. package/esm/groupsFilterRaw/groupsFilterTrigger.js +133 -34
  305. package/esm/groupsFilterRaw/groupsHelper.js +733 -207
  306. package/esm/groupsFilterRaw/stateReducer/stateReducerHelper.js +3 -1
  307. package/esm/groupsFilterRaw/types.d.ts +1 -0
  308. package/esm/header/components/collapsedItemsControl/collapsedItemsControl.js +101 -51
  309. package/esm/header/components/mobileFilterControl/mobileFilterControl.js +56 -8
  310. package/esm/header/components/mobileSearchControl/mobileSearchControl.js +113 -13
  311. package/esm/header/headerBack.js +58 -19
  312. package/esm/index.d.ts +1 -0
  313. package/esm/index.js +1 -0
  314. package/esm/list/itemData/itemDataInternal.js +210 -50
  315. package/esm/list/listItem/listItem.js +162 -54
  316. package/esm/menu/components/menuErrorItem.js +27 -4
  317. package/esm/mobileSheet/mobileSheet.js +189 -68
  318. package/esm/modal/modal.js +265 -120
  319. package/esm/nav/nav.js +1 -1
  320. package/esm/nav/navFooter/navFooter.js +76 -38
  321. package/esm/nav/navFooter/navFooterAction/navFooterAction.js +46 -12
  322. package/esm/nav/navHeader/navHeader.js +80 -35
  323. package/esm/nav/navHeader/navHeaderSearch/navHeaderSearch.js +82 -28
  324. package/esm/nav/navItem/navItem.d.ts +3 -3
  325. package/esm/nav/navItem/navItem.js +35 -33
  326. package/esm/nav/navMobileBar/navMobileBar.js +61 -20
  327. package/esm/notification/notification.js +114 -18
  328. package/esm/pagination/paginationArrow.js +75 -10
  329. package/esm/pagination/paginationText/paginationText.js +39 -10
  330. package/esm/pill/components/pillNonActionable/pillNonActionable.js +87 -23
  331. package/esm/pillBox/components/pillBoxItem.js +46 -8
  332. package/esm/pillBox/pillBox.js +115 -19
  333. package/esm/pillExpandable/pillExpandable.js +327 -138
  334. package/esm/rangeRaw/rangeRaw.js +480 -140
  335. package/esm/rangeRaw/utils/rangeHelper.js +203 -38
  336. package/esm/searchInputRaw/searchInputRaw.js +145 -43
  337. package/esm/skeleton/skeleton.js +45 -5
  338. package/esm/sortControl/sortControl.js +146 -41
  339. package/esm/stepperRaw/stepperRaw.js +112 -41
  340. package/esm/storybookHelpers/dataGridWithDifferentCellOptions/components/EntitiesListAction.js +178 -19
  341. package/esm/summary/summary.js +88 -7
  342. package/esm/table/actions/actionsMenu.js +165 -77
  343. package/esm/table/flexible/columnSettings.js +74 -9
  344. package/esm/table/flexible/columnsList.js +104 -42
  345. package/esm/table/flexible/columnsPopup.js +71 -19
  346. package/esm/table/nested/useNestedRows.js +161 -76
  347. package/esm/table/selectable/selectableHeader.js +174 -40
  348. package/esm/table/selectable/useSelectableRows.js +264 -190
  349. package/esm/table/sortable/sortableHeader.js +147 -74
  350. package/esm/tabs/tabs.js +221 -117
  351. package/esm/timePickerRaw/timePickerRaw.js +272 -57
  352. package/esm/toastRaw/toastRaw.js +132 -31
  353. package/esm/toggleButton/toggleButton.d.ts +0 -1
  354. package/esm/toggleButtonRaw/toggleButtonRaw.d.ts +1 -0
  355. package/esm/toggleButtonRaw/toggleButtonRaw.js +111 -18
  356. package/esm/utils/formatDate.js +995 -116
  357. package/{dist/utils/localization/translations/cs-json.js → esm/utils/localization/translations/cs.json} +11 -15
  358. package/{dist/utils/localization/translations/da-DK-json.js → esm/utils/localization/translations/da-DK.json} +31 -26
  359. package/{dist/utils/localization/translations/de-json.js → esm/utils/localization/translations/de.json} +12 -16
  360. package/esm/utils/localization/translations/en.json +308 -0
  361. package/{dist/utils/localization/translations/es-json.js → esm/utils/localization/translations/es.json} +11 -15
  362. package/{dist/utils/localization/translations/fi-FI-json.js → esm/utils/localization/translations/fi-FI.json} +31 -26
  363. package/{dist/utils/localization/translations/fr-FR-json.js → esm/utils/localization/translations/fr-FR.json} +12 -15
  364. package/{dist/utils/localization/translations/fr-json.js → esm/utils/localization/translations/fr.json} +11 -15
  365. package/{dist/utils/localization/translations/hu-HU-json.js → esm/utils/localization/translations/hu-HU.json} +31 -26
  366. package/{dist/utils/localization/translations/id-json.js → esm/utils/localization/translations/id.json} +11 -16
  367. package/{dist/utils/localization/translations/it-json.js → esm/utils/localization/translations/it.json} +11 -15
  368. package/{dist/utils/localization/translations/ja-json.js → esm/utils/localization/translations/ja.json} +11 -15
  369. package/{dist/utils/localization/translations/ko-KR-json.js → esm/utils/localization/translations/ko-KR.json} +24 -26
  370. package/{dist/utils/localization/translations/ms-json.js → esm/utils/localization/translations/ms.json} +11 -15
  371. package/{dist/utils/localization/translations/nb-NO-json.js → esm/utils/localization/translations/nb-NO.json} +31 -26
  372. package/{dist/utils/localization/translations/nl-json.js → esm/utils/localization/translations/nl.json} +11 -15
  373. package/{dist/utils/localization/translations/pl-json.js → esm/utils/localization/translations/pl.json} +11 -15
  374. package/{dist/utils/localization/translations/pt-BR-json.js → esm/utils/localization/translations/pt-BR.json} +11 -15
  375. package/{dist/utils/localization/translations/sk-SK-json.js → esm/utils/localization/translations/sk-SK.json} +31 -26
  376. package/{dist/utils/localization/translations/sv-json.js → esm/utils/localization/translations/sv.json} +11 -15
  377. package/{dist/utils/localization/translations/th-json.js → esm/utils/localization/translations/th.json} +11 -15
  378. package/{dist/utils/localization/translations/tr-json.js → esm/utils/localization/translations/tr.json} +11 -15
  379. package/{dist/utils/localization/translations/zh-Hans-json.js → esm/utils/localization/translations/zh-Hans.json} +11 -15
  380. package/{dist/utils/localization/translations/zh-TW-json.js → esm/utils/localization/translations/zh-TW.json} +11 -26
  381. package/esm/utils/localization/translationsDictionary.d.ts +2 -0
  382. package/esm/utils/localization/translationsDictionary.js +59 -0
  383. package/esm/utils/localization/useLanguage.js +1 -50
  384. package/package.json +19 -12
  385. package/dist/utils/localization/translations/cs-json.d.ts +0 -251
  386. package/dist/utils/localization/translations/da-DK-json.d.ts +0 -252
  387. package/dist/utils/localization/translations/de-json.d.ts +0 -251
  388. package/dist/utils/localization/translations/en-json.d.ts +0 -314
  389. package/dist/utils/localization/translations/en-json.js +0 -317
  390. package/dist/utils/localization/translations/es-json.d.ts +0 -251
  391. package/dist/utils/localization/translations/fi-FI-json.d.ts +0 -252
  392. package/dist/utils/localization/translations/fr-FR-json.d.ts +0 -250
  393. package/dist/utils/localization/translations/fr-json.d.ts +0 -251
  394. package/dist/utils/localization/translations/hu-HU-json.d.ts +0 -252
  395. package/dist/utils/localization/translations/id-json.d.ts +0 -252
  396. package/dist/utils/localization/translations/it-json.d.ts +0 -251
  397. package/dist/utils/localization/translations/ja-json.d.ts +0 -251
  398. package/dist/utils/localization/translations/ko-KR-json.d.ts +0 -258
  399. package/dist/utils/localization/translations/ms-json.d.ts +0 -251
  400. package/dist/utils/localization/translations/nb-NO-json.d.ts +0 -252
  401. package/dist/utils/localization/translations/nl-json.d.ts +0 -251
  402. package/dist/utils/localization/translations/pl-json.d.ts +0 -251
  403. package/dist/utils/localization/translations/pt-BR-json.d.ts +0 -251
  404. package/dist/utils/localization/translations/sk-SK-json.d.ts +0 -251
  405. package/dist/utils/localization/translations/sv-json.d.ts +0 -251
  406. package/dist/utils/localization/translations/th-json.d.ts +0 -251
  407. package/dist/utils/localization/translations/tr-json.d.ts +0 -251
  408. package/dist/utils/localization/translations/zh-Hans-json.d.ts +0 -251
  409. package/dist/utils/localization/translations/zh-TW-json.d.ts +0 -271
  410. package/esm/utils/localization/translations/cs-json.d.ts +0 -251
  411. package/esm/utils/localization/translations/da-DK-json.d.ts +0 -252
  412. package/esm/utils/localization/translations/de-json.d.ts +0 -251
  413. package/esm/utils/localization/translations/en-json.d.ts +0 -314
  414. package/esm/utils/localization/translations/en-json.js +0 -314
  415. package/esm/utils/localization/translations/es-json.d.ts +0 -251
  416. package/esm/utils/localization/translations/fi-FI-json.d.ts +0 -252
  417. package/esm/utils/localization/translations/fr-FR-json.d.ts +0 -250
  418. package/esm/utils/localization/translations/fr-json.d.ts +0 -251
  419. package/esm/utils/localization/translations/hu-HU-json.d.ts +0 -252
  420. package/esm/utils/localization/translations/id-json.d.ts +0 -252
  421. package/esm/utils/localization/translations/it-json.d.ts +0 -251
  422. package/esm/utils/localization/translations/ja-json.d.ts +0 -251
  423. package/esm/utils/localization/translations/ko-KR-json.d.ts +0 -258
  424. package/esm/utils/localization/translations/ms-json.d.ts +0 -251
  425. package/esm/utils/localization/translations/nb-NO-json.d.ts +0 -252
  426. package/esm/utils/localization/translations/nl-json.d.ts +0 -251
  427. package/esm/utils/localization/translations/pl-json.d.ts +0 -251
  428. package/esm/utils/localization/translations/pt-BR-json.d.ts +0 -251
  429. package/esm/utils/localization/translations/sk-SK-json.d.ts +0 -251
  430. package/esm/utils/localization/translations/sv-json.d.ts +0 -251
  431. package/esm/utils/localization/translations/th-json.d.ts +0 -251
  432. package/esm/utils/localization/translations/tr-json.d.ts +0 -251
  433. package/esm/utils/localization/translations/zh-Hans-json.d.ts +0 -251
  434. package/esm/utils/localization/translations/zh-TW-json.d.ts +0 -271
@@ -1,3 +1,4 @@
1
+ import { injectString } from "../../utils/localization/translationsDictionary";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { useLanguage } from "../../utils/localization/useLanguage";
3
4
  import { useCallback, useContext, useMemo } from "react";
@@ -8,592 +9,679 @@ import { getFormattedLabel } from "../utils/getFormattedLabel";
8
9
  import { truncateDecimals } from "../../utils/truncateDecimals";
9
10
  import { calculateMinTruncationPrecision } from "../utils/calculateMinTruncationPrecision";
10
11
  import { removeTrendDataFromDatasets } from "../utils/removeTrendDataFromDatasets";
12
+ injectString("cs", "Data", "Data");
13
+ injectString("da-DK", "Data", "Data");
14
+ injectString("de", "Data", "Daten");
15
+ injectString("en", "Data", "Data");
16
+ injectString("es", "Data", "Datos");
17
+ injectString("fi-FI", "Data", "Data");
18
+ injectString("fr", "Data", "Donn\xE9es");
19
+ injectString("fr-FR", "Data", "Donn\xE9es");
20
+ injectString("hu-HU", "Data", "Adatok");
21
+ injectString("id", "Data", "Data");
22
+ injectString("it", "Data", "Dati");
23
+ injectString("ja", "Data", "\u30C7\u30FC\u30BF");
24
+ injectString("ko-KR", "Data", "\uB370\uC774\uD130");
25
+ injectString("ms", "Data", "Data");
26
+ injectString("nb-NO", "Data", "Data");
27
+ injectString("nl", "Data", "Gegevens");
28
+ injectString("pl", "Data", "Dane");
29
+ injectString("pt-BR", "Data", "Dados");
30
+ injectString("sk-SK", "Data", "D\xE1ta");
31
+ injectString("sv", "Data", "Data");
32
+ injectString("th", "Data", "\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25");
33
+ injectString("tr", "Data", "Veri");
34
+ injectString("zh-Hans", "Data", "\u6570\u636E");
35
+ injectString("zh-TW", "Data", "\u8CC7\u6599");
36
+ injectString("en", "switched {TRANSITIONS} times between two states: {STATE1} and {STATE2}.", "switched {TRANSITIONS} times between two states: {STATE1} and {STATE2}.");
37
+ injectString("en", "Highest point(s):", "Highest point(s):");
38
+ injectString("en", "Lowest point(s):", "Lowest point(s):");
39
+ injectString("en", "Segment {INDEX} {DIRECTION} from {FROM} at {X0} to {TO} at {X}.", "Segment {INDEX} {DIRECTION} from {FROM} at {X0} to {TO} at {X}.");
40
+ injectString("en", "shows a complex trend with multiple segments:", "shows a complex trend with multiple segments:");
41
+ injectString("cs", "No data available", "Nejsou k\xA0dispozici \u017E\xE1dn\xE1 data");
42
+ injectString("da-DK", "No data available", "Ingen data tilg\xE6ngelige");
43
+ injectString("de", "No data available", "Keine Daten verf\xFCgbar");
44
+ injectString("en", "No data available", "No data available");
45
+ injectString("es", "No data available", "No hay datos disponibles");
46
+ injectString("fi-FI", "No data available", "Tietoja ei saatavilla");
47
+ injectString("fr", "No data available", "Aucune donn\xE9e disponible");
48
+ injectString("fr-FR", "No data available", "Aucune donn\xE9e disponible");
49
+ injectString("hu-HU", "No data available", "Nincsenek rendelkez\xE9sre \xE1ll\xF3 adatok");
50
+ injectString("id", "No data available", "Tidak ada data tersedia");
51
+ injectString("it", "No data available", "Nessun dato disponibile");
52
+ injectString("ja", "No data available", "\u30C7\u30FC\u30BF\u304C\u3042\u308A\u307E\u305B\u3093");
53
+ injectString("ko-KR", "No data available", "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4");
54
+ injectString("ms", "No data available", "Tiada data tersedia");
55
+ injectString("nb-NO", "No data available", "Ingen data tilgjengelig");
56
+ injectString("nl", "No data available", "Geen gegevens beschikbaar");
57
+ injectString("pl", "No data available", "Brak dost\u0119pnych danych");
58
+ injectString("pt-BR", "No data available", "Nenhum dado dispon\xEDvel");
59
+ injectString("sk-SK", "No data available", "Nie s\xFA k dispoz\xEDcii \u017Eiadne \xFAdaje");
60
+ injectString("sv", "No data available", "Det finns inga tillg\xE4ngliga data");
61
+ injectString("th", "No data available", "\u0E44\u0E21\u0E48\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25");
62
+ injectString("tr", "No data available", "Mevcut veri yok");
63
+ injectString("zh-Hans", "No data available", "\u65E0\u53EF\u7528\u6570\u636E");
64
+ injectString("zh-TW", "No data available", "\u6C92\u6709\u53EF\u7528\u8CC7\u6599");
65
+ injectString("en", "Only one data point: value is {VALUE}.", "Only one data point: value is {VALUE}.");
66
+ injectString("en", "Average value is:", "Average value is:");
67
+ injectString("en", "shows a relatively flat or stable trend.", "shows a relatively flat or stable trend.");
68
+ injectString("en", "value changes in the range from {MIN} to {MAX}.", "value changes in the range from {MIN} to {MAX}.");
69
+ injectString("en", "from {FROM} at {X0} to {TO} at {X}.", "from {FROM} at {X0} to {TO} at {X}.");
70
+ injectString("en", "Error generating chart narrative for dataset", "Error generating chart narrative for dataset");
11
71
  export function collectAllPoints(dataset, dateFormatter, miniChartLabels, options) {
12
- if (dataset.data.length === 0) {
13
- return [];
14
- }
15
- // Minichart data processing
16
- if (miniChartLabels) {
17
- return dataset.data.map((value, index) => ({
18
- x: index,
19
- y: typeof value === "number" ? value : 0,
20
- originX: index === 0 ? miniChartLabels[0] : (index === dataset.data.length - 1 ? miniChartLabels[miniChartLabels.length - 1] : index)
21
- }));
22
- }
23
- // Standard chart data processing
24
- const allPoints = [];
25
- for (const [index, point] of dataset.data.entries()) {
26
- if (typeof point !== "object" || point === null || !("y" in point)) {
27
- continue;
28
- }
29
- let xNumeric;
30
- let originX;
31
- const rawXValue = point.x;
32
- if (rawXValue instanceof Date || (typeof rawXValue === "string" && isDateString(rawXValue))) {
33
- xNumeric = new Date(rawXValue).getTime();
34
- originX = getFormattedLabel(rawXValue, dateFormatter, options);
35
- }
36
- else if (typeof rawXValue === "number") {
37
- xNumeric = rawXValue;
38
- originX = rawXValue;
39
- }
40
- else {
41
- xNumeric = index;
42
- originX = rawXValue;
43
- }
44
- allPoints.push({ x: xNumeric, y: point.y || 0, originX });
45
- }
46
- return allPoints;
72
+ if (dataset.data.length === 0) {
73
+ return [];
74
+ }
75
+ // Minichart data processing
76
+ if (miniChartLabels) {
77
+ return dataset.data.map((value, index) => ({
78
+ x: index,
79
+ y: typeof value === "number" ? value : 0,
80
+ originX: index === 0 ? miniChartLabels[0] : index === dataset.data.length - 1 ? miniChartLabels[miniChartLabels.length - 1] : index
81
+ }));
82
+ }
83
+ // Standard chart data processing
84
+ const allPoints = [];
85
+ for (const [index, point] of dataset.data.entries()) {
86
+ if (typeof point !== "object" || point === null || !("y" in point)) {
87
+ continue;
88
+ }
89
+ let xNumeric;
90
+ let originX;
91
+ const rawXValue = point.x;
92
+ if (rawXValue instanceof Date || typeof rawXValue === "string" && isDateString(rawXValue)) {
93
+ xNumeric = new Date(rawXValue).getTime();
94
+ originX = getFormattedLabel(rawXValue, dateFormatter, options);
95
+ } else if (typeof rawXValue === "number") {
96
+ xNumeric = rawXValue;
97
+ originX = rawXValue;
98
+ } else {
99
+ xNumeric = index;
100
+ originX = rawXValue;
101
+ }
102
+ allPoints.push({
103
+ x: xNumeric,
104
+ y: point.y || 0,
105
+ originX
106
+ });
107
+ }
108
+ return allPoints;
47
109
  }
48
110
  export function simplifyData(data) {
49
- // Need at least 3 points to find a peak or valley
50
- if (data.length < 3) {
51
- return data;
52
- }
53
- // Calculate Potential Peak/Valley Height and Dominance for Every Point
54
- const scoredData = data.map((currentPoint, i) => {
55
- if (currentPoint.y === null) {
56
- return {
57
- point: currentPoint,
58
- peakHeight: 0,
59
- valleyHeight: 0,
60
- peakDominance: 0,
61
- valleyDominance: 0
62
- };
63
- }
64
- let peakHeight = 0;
65
- let valleyHeight = 0;
66
- let peakDominance = 0;
67
- let valleyDominance = 0;
68
- let localMinForPeak = currentPoint.y;
69
- // Scan left
70
- for (let j = i - 1; j >= 0; j--) {
71
- const point = data[j];
72
- if (point.y !== null && point.y < currentPoint.y) {
73
- localMinForPeak = Math.min(localMinForPeak, point.y);
74
- peakDominance++;
75
- }
76
- else {
77
- break;
78
- }
79
- }
80
- // Scan right
81
- for (let j = i + 1; j < data.length; j++) {
82
- const point = data[j];
83
- if (point.y !== null && point.y < currentPoint.y) {
84
- localMinForPeak = Math.min(localMinForPeak, point.y);
85
- peakDominance++;
86
- }
87
- else {
88
- break;
89
- }
90
- }
91
- if (peakDominance > 0) {
92
- peakHeight = currentPoint.y - localMinForPeak;
93
- }
94
- // Calculate Valley Height & Dominance
95
- let localMaxForValley = currentPoint.y;
96
- // Scan left
97
- for (let j = i - 1; j >= 0; j--) {
98
- const point = data[j];
99
- if (point.y !== null && point.y > currentPoint.y) {
100
- localMaxForValley = Math.max(localMaxForValley, point.y);
101
- valleyDominance++;
102
- }
103
- else {
104
- break;
105
- }
106
- }
107
- // Scan right
108
- for (let j = i + 1; j < data.length; j++) {
109
- const point = data[j];
110
- if (point.y !== null && point.y > currentPoint.y) {
111
- localMaxForValley = Math.max(localMaxForValley, point.y);
112
- valleyDominance++;
113
- }
114
- else {
115
- break;
116
- }
117
- }
118
- if (valleyDominance > 0) {
119
- valleyHeight = localMaxForValley - currentPoint.y;
120
- }
121
- return { point: currentPoint, peakHeight, valleyHeight, peakDominance, valleyDominance };
122
- });
123
- // Filter points by trend change and relative height
124
- const significantExtremums = [];
125
- const strengthThreshold = 0.50;
126
- const yValues = data.filter(p => p.y !== null).map(p => p.y);
127
- const minY = yValues.reduce((min, current) => Math.min(min, current));
128
- const maxY = yValues.reduce((max, current) => Math.max(max, current));
129
- const datasetRange = maxY - minY;
130
- // Proceed only if there is a range to avoid division by zero
131
- if (datasetRange > 0) {
132
- for (let i = 1; i < scoredData.length - 1; i++) {
133
- const prev = scoredData[i - 1];
134
- const current = scoredData[i];
135
- const next = scoredData[i + 1];
136
- // Check for a peak in the 'peakHeight' values
137
- const isPeak = current.peakHeight > prev.peakHeight && current.peakHeight > next.peakHeight;
138
- // Check for a peak in the 'valleyHeight' values
139
- const isValley = current.valleyHeight > prev.valleyHeight && current.valleyHeight > next.valleyHeight;
140
- if (isPeak) {
141
- const relHeight = current.peakHeight / datasetRange;
142
- if (relHeight >= strengthThreshold && current.peakDominance > 2) {
143
- significantExtremums.push(current.point);
144
- }
145
- }
146
- else if (isValley) {
147
- const relHeight = current.valleyHeight / datasetRange;
148
- if (relHeight >= strengthThreshold && current.valleyDominance > 2) {
149
- significantExtremums.push(current.point);
150
- }
151
- }
152
- }
153
- }
154
- const uniquePointsSet = new Set();
155
- const finalResult = [];
156
- const addPointIfUnique = (point) => {
157
- // using a composite key for uniqueness
158
- const key = `${point.x.toString()}-${point.y || ""}`;
159
- if (!uniquePointsSet.has(key)) {
160
- uniquePointsSet.add(key);
161
- finalResult.push(point);
162
- }
111
+ // Need at least 3 points to find a peak or valley
112
+ if (data.length < 3) {
113
+ return data;
114
+ }
115
+ // Calculate Potential Peak/Valley Height and Dominance for Every Point
116
+ const scoredData = data.map((currentPoint, i) => {
117
+ if (currentPoint.y === null) {
118
+ return {
119
+ point: currentPoint,
120
+ peakHeight: 0,
121
+ valleyHeight: 0,
122
+ peakDominance: 0,
123
+ valleyDominance: 0
124
+ };
125
+ }
126
+ let peakHeight = 0;
127
+ let valleyHeight = 0;
128
+ let peakDominance = 0;
129
+ let valleyDominance = 0;
130
+ let localMinForPeak = currentPoint.y;
131
+ // Scan left
132
+ for (let j = i - 1; j >= 0; j--) {
133
+ const point = data[j];
134
+ if (point.y !== null && point.y < currentPoint.y) {
135
+ localMinForPeak = Math.min(localMinForPeak, point.y);
136
+ peakDominance++;
137
+ } else {
138
+ break;
139
+ }
140
+ }
141
+ // Scan right
142
+ for (let j = i + 1; j < data.length; j++) {
143
+ const point = data[j];
144
+ if (point.y !== null && point.y < currentPoint.y) {
145
+ localMinForPeak = Math.min(localMinForPeak, point.y);
146
+ peakDominance++;
147
+ } else {
148
+ break;
149
+ }
150
+ }
151
+ if (peakDominance > 0) {
152
+ peakHeight = currentPoint.y - localMinForPeak;
153
+ }
154
+ // Calculate Valley Height & Dominance
155
+ let localMaxForValley = currentPoint.y;
156
+ // Scan left
157
+ for (let j = i - 1; j >= 0; j--) {
158
+ const point = data[j];
159
+ if (point.y !== null && point.y > currentPoint.y) {
160
+ localMaxForValley = Math.max(localMaxForValley, point.y);
161
+ valleyDominance++;
162
+ } else {
163
+ break;
164
+ }
165
+ }
166
+ // Scan right
167
+ for (let j = i + 1; j < data.length; j++) {
168
+ const point = data[j];
169
+ if (point.y !== null && point.y > currentPoint.y) {
170
+ localMaxForValley = Math.max(localMaxForValley, point.y);
171
+ valleyDominance++;
172
+ } else {
173
+ break;
174
+ }
175
+ }
176
+ if (valleyDominance > 0) {
177
+ valleyHeight = localMaxForValley - currentPoint.y;
178
+ }
179
+ return {
180
+ point: currentPoint,
181
+ peakHeight,
182
+ valleyHeight,
183
+ peakDominance,
184
+ valleyDominance
163
185
  };
164
- // Add key points to the array
165
- addPointIfUnique(data[0]);
166
- addPointIfUnique(data[data.length - 1]);
167
- const globalMinPoint = data.find(p => p.y === minY);
168
- if (globalMinPoint) {
169
- addPointIfUnique(globalMinPoint);
170
- }
171
- const globalMaxPoint = data.find(p => p.y === maxY);
172
- if (globalMaxPoint) {
173
- addPointIfUnique(globalMaxPoint);
174
- }
175
- // Add other significant extremums
176
- significantExtremums.forEach(addPointIfUnique);
177
- // Sort the final result array chronologically
178
- finalResult.sort((a, b) => new Date(a.x).getTime() - new Date(b.x).getTime());
179
- // Filter out non-significant points based on area under the curve
180
- const getSquare = (p1, p2) => {
181
- const x1 = new Date(p1.x).getTime();
182
- const x2 = new Date(p2.x).getTime();
183
- const y1 = p1.y;
184
- const y2 = p2.y;
185
- return (x2 - x1) * (y1 + (y2 - y1) / 2);
186
- };
187
- const immutablePoints = [data[0], data[data.length - 1], globalMinPoint, globalMaxPoint].filter(Boolean);
188
- for (let i = 1; i < finalResult.length - 1; i++) {
189
- const current = finalResult[i];
190
- // Skip removal if the current point is one of the designated immutable points
191
- if (immutablePoints.some(p => p && p.x === current.x && p.y === current.y)) {
192
- continue;
186
+ });
187
+ // Filter points by trend change and relative height
188
+ const significantExtremums = [];
189
+ const strengthThreshold = 0.50;
190
+ const yValues = data.filter(p => p.y !== null).map(p => p.y);
191
+ const minY = yValues.reduce((min, current) => Math.min(min, current));
192
+ const maxY = yValues.reduce((max, current) => Math.max(max, current));
193
+ const datasetRange = maxY - minY;
194
+ // Proceed only if there is a range to avoid division by zero
195
+ if (datasetRange > 0) {
196
+ for (let i = 1; i < scoredData.length - 1; i++) {
197
+ const prev = scoredData[i - 1];
198
+ const current = scoredData[i];
199
+ const next = scoredData[i + 1];
200
+ // Check for a peak in the 'peakHeight' values
201
+ const isPeak = current.peakHeight > prev.peakHeight && current.peakHeight > next.peakHeight;
202
+ // Check for a peak in the 'valleyHeight' values
203
+ const isValley = current.valleyHeight > prev.valleyHeight && current.valleyHeight > next.valleyHeight;
204
+ if (isPeak) {
205
+ const relHeight = current.peakHeight / datasetRange;
206
+ if (relHeight >= strengthThreshold && current.peakDominance > 2) {
207
+ significantExtremums.push(current.point);
193
208
  }
194
- const prev = finalResult[i - 1];
195
- const next = finalResult[i + 1];
196
- const squareWithPoint = getSquare(prev, current) + getSquare(current, next);
197
- const squareWithoutPoint = getSquare(prev, next);
198
- const squares = [squareWithPoint, squareWithoutPoint].sort((a, b) => a - b);
199
- const squareRatio = squares[0] / squares[1];
200
- if (squareRatio > 0.9) {
201
- // remove insignificant point
202
- finalResult.splice(i, 1);
203
- i--;
209
+ } else if (isValley) {
210
+ const relHeight = current.valleyHeight / datasetRange;
211
+ if (relHeight >= strengthThreshold && current.valleyDominance > 2) {
212
+ significantExtremums.push(current.point);
204
213
  }
205
- }
206
- return finalResult;
214
+ }
215
+ }
216
+ }
217
+ const uniquePointsSet = new Set();
218
+ const finalResult = [];
219
+ const addPointIfUnique = point => {
220
+ // using a composite key for uniqueness
221
+ const key = `${point.x.toString()}-${point.y || ""}`;
222
+ if (!uniquePointsSet.has(key)) {
223
+ uniquePointsSet.add(key);
224
+ finalResult.push(point);
225
+ }
226
+ };
227
+ // Add key points to the array
228
+ addPointIfUnique(data[0]);
229
+ addPointIfUnique(data[data.length - 1]);
230
+ const globalMinPoint = data.find(p => p.y === minY);
231
+ if (globalMinPoint) {
232
+ addPointIfUnique(globalMinPoint);
233
+ }
234
+ const globalMaxPoint = data.find(p => p.y === maxY);
235
+ if (globalMaxPoint) {
236
+ addPointIfUnique(globalMaxPoint);
237
+ }
238
+ // Add other significant extremums
239
+ significantExtremums.forEach(addPointIfUnique);
240
+ // Sort the final result array chronologically
241
+ finalResult.sort((a, b) => new Date(a.x).getTime() - new Date(b.x).getTime());
242
+ // Filter out non-significant points based on area under the curve
243
+ const getSquare = (p1, p2) => {
244
+ const x1 = new Date(p1.x).getTime();
245
+ const x2 = new Date(p2.x).getTime();
246
+ const y1 = p1.y;
247
+ const y2 = p2.y;
248
+ return (x2 - x1) * (y1 + (y2 - y1) / 2);
249
+ };
250
+ const immutablePoints = [data[0], data[data.length - 1], globalMinPoint, globalMaxPoint].filter(Boolean);
251
+ for (let i = 1; i < finalResult.length - 1; i++) {
252
+ const current = finalResult[i];
253
+ // Skip removal if the current point is one of the designated immutable points
254
+ if (immutablePoints.some(p => p && p.x === current.x && p.y === current.y)) {
255
+ continue;
256
+ }
257
+ const prev = finalResult[i - 1];
258
+ const next = finalResult[i + 1];
259
+ const squareWithPoint = getSquare(prev, current) + getSquare(current, next);
260
+ const squareWithoutPoint = getSquare(prev, next);
261
+ const squares = [squareWithPoint, squareWithoutPoint].sort((a, b) => a - b);
262
+ const squareRatio = squares[0] / squares[1];
263
+ if (squareRatio > 0.9) {
264
+ // remove insignificant point
265
+ finalResult.splice(i, 1);
266
+ i--;
267
+ }
268
+ }
269
+ return finalResult;
207
270
  }
208
271
  function getTrendDirectionBySlope(slope) {
209
- const slopeThreshold = 1e-10;
210
- if (slope > slopeThreshold) {
211
- return "increases";
212
- }
213
- if (slope < -slopeThreshold) {
214
- return "decreases";
215
- }
216
- return "flat";
272
+ const slopeThreshold = 1e-10;
273
+ if (slope > slopeThreshold) {
274
+ return "increases";
275
+ }
276
+ if (slope < -slopeThreshold) {
277
+ return "decreases";
278
+ }
279
+ return "flat";
217
280
  }
218
281
  function calculateLinearRegression(points) {
219
- if (points.length < 2) {
220
- return { slope: NaN, yIntercept: NaN };
221
- }
222
- const sumX = points.reduce((sum, p) => sum + p.x, 0);
223
- const sumY = points.reduce((sum, p) => sum + p.y, 0);
224
- const meanX = sumX / points.length;
225
- const meanY = sumY / points.length;
226
- let numerator = 0;
227
- let denominator = 0;
228
- for (const p of points) {
229
- numerator += (p.x - meanX) * (p.y - meanY);
230
- denominator += (p.x - meanX) * (p.x - meanX);
231
- }
232
- const slope = denominator === 0 ? 0 : numerator / denominator;
233
- const yIntercept = meanY - slope * meanX;
234
- return { slope, yIntercept };
282
+ if (points.length < 2) {
283
+ return {
284
+ slope: NaN,
285
+ yIntercept: NaN
286
+ };
287
+ }
288
+ const sumX = points.reduce((sum, p) => sum + p.x, 0);
289
+ const sumY = points.reduce((sum, p) => sum + p.y, 0);
290
+ const meanX = sumX / points.length;
291
+ const meanY = sumY / points.length;
292
+ let numerator = 0;
293
+ let denominator = 0;
294
+ for (const p of points) {
295
+ numerator += (p.x - meanX) * (p.y - meanY);
296
+ denominator += (p.x - meanX) * (p.x - meanX);
297
+ }
298
+ const slope = denominator === 0 ? 0 : numerator / denominator;
299
+ const yIntercept = meanY - slope * meanX;
300
+ return {
301
+ slope,
302
+ yIntercept
303
+ };
235
304
  }
236
305
  function detectTwoStateChart(dataset, translate, dateFormatter, miniChartLabels, options) {
237
- const allPoints = collectAllPoints(dataset, dateFormatter, miniChartLabels, options);
238
- if (allPoints.length <= 2) {
239
- return false;
240
- }
241
- const datasetName = dataset.label || translate("Data");
242
- const uniqueYValues = new Set();
243
- const tolerance = 1e-6;
244
- for (const point of allPoints) {
245
- uniqueYValues.add(point.y);
246
- if (uniqueYValues.size > 2) {
247
- return false;
248
- }
249
- }
250
- const isTwoState = uniqueYValues.size === 2;
251
- if (!isTwoState) {
252
- return false;
253
- }
254
- // Count state transitions
255
- let transitions = 0;
256
- for (let i = 1; i < allPoints.length; i++) {
257
- const prevValue = allPoints[i - 1].y;
258
- const currentValue = allPoints[i].y;
259
- if (Math.abs(prevValue - currentValue) > tolerance) {
260
- transitions++;
261
- }
262
- }
263
- const [val1, val2] = uniqueYValues;
264
- const decimalLength = calculateMinTruncationPrecision([val1, val2]);
265
- const summary = `${datasetName} ${translate("switched {TRANSITIONS} times between two states: {STATE1} and {STATE2}.")
266
- .replace("{STATE1}", truncateDecimals(val1, decimalLength).toString())
267
- .replace("{STATE2}", truncateDecimals(val2, decimalLength).toString())
268
- .replace("{TRANSITIONS}", transitions.toString())}`;
269
- return summary;
306
+ const allPoints = collectAllPoints(dataset, dateFormatter, miniChartLabels, options);
307
+ if (allPoints.length <= 2) {
308
+ return false;
309
+ }
310
+ const datasetName = dataset.label || translate("Data");
311
+ const uniqueYValues = new Set();
312
+ const tolerance = 1e-6;
313
+ for (const point of allPoints) {
314
+ uniqueYValues.add(point.y);
315
+ if (uniqueYValues.size > 2) {
316
+ return false;
317
+ }
318
+ }
319
+ const isTwoState = uniqueYValues.size === 2;
320
+ if (!isTwoState) {
321
+ return false;
322
+ }
323
+ // Count state transitions
324
+ let transitions = 0;
325
+ for (let i = 1; i < allPoints.length; i++) {
326
+ const prevValue = allPoints[i - 1].y;
327
+ const currentValue = allPoints[i].y;
328
+ if (Math.abs(prevValue - currentValue) > tolerance) {
329
+ transitions++;
330
+ }
331
+ }
332
+ const [val1, val2] = uniqueYValues;
333
+ const decimalLength = calculateMinTruncationPrecision([val1, val2]);
334
+ const summary = `${datasetName} ${translate("switched {TRANSITIONS} times between two states: {STATE1} and {STATE2}.").replace("{STATE1}", truncateDecimals(val1, decimalLength).toString()).replace("{STATE2}", truncateDecimals(val2, decimalLength).toString()).replace("{TRANSITIONS}", transitions.toString())}`;
335
+ return summary;
270
336
  }
271
337
  // Calculates the Median Absolute Deviation (MAD) for a given array of numbers.
272
338
  function calculateMAD(data, scaleFactor = 1.4826) {
273
- if (data.length === 0) {
274
- return 0;
275
- }
276
- // Sort the data to find the median
277
- const sortedData = [...data].sort((a, b) => a - b);
278
- const mid = Math.floor(sortedData.length / 2);
279
- let median;
280
- if (sortedData.length % 2 === 0) {
281
- median = (sortedData[mid - 1] + sortedData[mid]) / 2;
282
- }
283
- else {
284
- median = sortedData[mid];
285
- }
286
- // Calculate absolute deviations from the median
287
- const deviations = sortedData.map(d => Math.abs(d - median));
288
- let mad;
289
- const madMid = Math.floor(deviations.length / 2);
290
- if (deviations.length % 2 === 0) {
291
- mad = (deviations[madMid - 1] + deviations[madMid]) / 2;
292
- }
293
- else {
294
- mad = deviations[madMid];
295
- }
296
- return mad * scaleFactor;
339
+ if (data.length === 0) {
340
+ return 0;
341
+ }
342
+ // Sort the data to find the median
343
+ const sortedData = [...data].sort((a, b) => a - b);
344
+ const mid = Math.floor(sortedData.length / 2);
345
+ let median;
346
+ if (sortedData.length % 2 === 0) {
347
+ median = (sortedData[mid - 1] + sortedData[mid]) / 2;
348
+ } else {
349
+ median = sortedData[mid];
350
+ }
351
+ // Calculate absolute deviations from the median
352
+ const deviations = sortedData.map(d => Math.abs(d - median));
353
+ let mad;
354
+ const madMid = Math.floor(deviations.length / 2);
355
+ if (deviations.length % 2 === 0) {
356
+ mad = (deviations[madMid - 1] + deviations[madMid]) / 2;
357
+ } else {
358
+ mad = deviations[madMid];
359
+ }
360
+ return mad * scaleFactor;
297
361
  }
298
362
  function getInliersForSmallSet(allPoints) {
299
- // Only proceed if there are enough points to perform exclusion
300
- if (allPoints.length < 3) {
301
- return allPoints;
302
- }
303
- const initialLr = calculateLinearRegression(allPoints);
304
- const initialResiduals = allPoints.map((point) => ({
305
- point,
306
- residual: Math.abs(point.y - (initialLr.slope * point.x + initialLr.yIntercept))
307
- }));
308
- // Sort to put the worst-fitting point (largest residual) first
309
- initialResiduals.sort((a, b) => b.residual - a.residual);
310
- // Exclude the top residual (the potential outlier) and return the rest as inliers.
311
- return initialResiduals.slice(1).map(r => r.point);
363
+ // Only proceed if there are enough points to perform exclusion
364
+ if (allPoints.length < 3) {
365
+ return allPoints;
366
+ }
367
+ const initialLr = calculateLinearRegression(allPoints);
368
+ const initialResiduals = allPoints.map(point => ({
369
+ point,
370
+ residual: Math.abs(point.y - (initialLr.slope * point.x + initialLr.yIntercept))
371
+ }));
372
+ // Sort to put the worst-fitting point (largest residual) first
373
+ initialResiduals.sort((a, b) => b.residual - a.residual);
374
+ // Exclude the top residual (the potential outlier) and return the rest as inliers.
375
+ return initialResiduals.slice(1).map(r => r.point);
312
376
  }
313
377
  function getInliersForLargeSet(allPoints, initialLr) {
314
- const initialResiduals = allPoints.map(point => {
315
- const predictedY = initialLr.slope * point.x + initialLr.yIntercept;
316
- return point.y - predictedY;
317
- });
318
- const madOfInitialResiduals = calculateMAD(initialResiduals);
319
- const strictInlierFilter = 1.5;
320
- const pointsForRobustLr = [];
321
- if (madOfInitialResiduals > 1e-9) {
322
- for (let i = 0; i < allPoints.length; i++) {
323
- if (Math.abs(initialResiduals[i]) <= strictInlierFilter * madOfInitialResiduals) {
324
- pointsForRobustLr.push(allPoints[i]);
325
- }
326
- }
327
- }
328
- else {
329
- // If no spread, use all points.
330
- pointsForRobustLr.push(...allPoints);
331
- }
332
- return pointsForRobustLr;
378
+ const initialResiduals = allPoints.map(point => {
379
+ const predictedY = initialLr.slope * point.x + initialLr.yIntercept;
380
+ return point.y - predictedY;
381
+ });
382
+ const madOfInitialResiduals = calculateMAD(initialResiduals);
383
+ const strictInlierFilter = 1.5;
384
+ const pointsForRobustLr = [];
385
+ if (madOfInitialResiduals > 1e-9) {
386
+ for (let i = 0; i < allPoints.length; i++) {
387
+ if (Math.abs(initialResiduals[i]) <= strictInlierFilter * madOfInitialResiduals) {
388
+ pointsForRobustLr.push(allPoints[i]);
389
+ }
390
+ }
391
+ } else {
392
+ // If no spread, use all points.
393
+ pointsForRobustLr.push(...allPoints);
394
+ }
395
+ return pointsForRobustLr;
333
396
  }
334
397
  function detectSignificantDeviations(allPoints, translate, decimalLength, deviationThreshold = 2) {
335
- if (allPoints.length < 3) {
336
- return null;
337
- }
338
- let pointsForRobustLr = [];
339
- let initialLr = null;
340
- // If the dataset is tiny (where MAD-based filtering is most fragile)
341
- if (allPoints.length <= 5) {
342
- pointsForRobustLr = getInliersForSmallSet(allPoints);
343
- }
344
- else {
345
- initialLr = calculateLinearRegression(allPoints);
346
- pointsForRobustLr = getInliersForLargeSet(allPoints, initialLr);
347
- }
348
- let finalSlope;
349
- let finalYIntercept;
350
- // Use the determined pointsForRobustLr for the final regression
351
- if (pointsForRobustLr.length < 2) {
352
- console.warn("Not enough points for robust regression, falling back to all points.");
353
- if (!initialLr) {
354
- initialLr = calculateLinearRegression(allPoints);
355
- }
356
- finalSlope = initialLr.slope;
357
- finalYIntercept = initialLr.yIntercept;
358
- }
359
- else {
360
- const robustLr = calculateLinearRegression(pointsForRobustLr);
361
- finalSlope = robustLr.slope;
362
- finalYIntercept = robustLr.yIntercept;
363
- }
364
- // Calculate Final Residuals for ALL Original Points against the Robust Line
365
- const pointsWithFinalResiduals = allPoints.map(point => {
366
- const predictedYRobust = finalSlope * point.x + finalYIntercept;
367
- const residual = point.y - predictedYRobust;
368
- return Object.assign(Object.assign({}, point), { residual: residual, isSignificantlyHigh: false, isSignificantlyLow: false });
398
+ if (allPoints.length < 3) {
399
+ return null;
400
+ }
401
+ let pointsForRobustLr = [];
402
+ let initialLr = null;
403
+ // If the dataset is tiny (where MAD-based filtering is most fragile)
404
+ if (allPoints.length <= 5) {
405
+ pointsForRobustLr = getInliersForSmallSet(allPoints);
406
+ } else {
407
+ initialLr = calculateLinearRegression(allPoints);
408
+ pointsForRobustLr = getInliersForLargeSet(allPoints, initialLr);
409
+ }
410
+ let finalSlope;
411
+ let finalYIntercept;
412
+ // Use the determined pointsForRobustLr for the final regression
413
+ if (pointsForRobustLr.length < 2) {
414
+ console.warn("Not enough points for robust regression, falling back to all points.");
415
+ if (!initialLr) {
416
+ initialLr = calculateLinearRegression(allPoints);
417
+ }
418
+ finalSlope = initialLr.slope;
419
+ finalYIntercept = initialLr.yIntercept;
420
+ } else {
421
+ const robustLr = calculateLinearRegression(pointsForRobustLr);
422
+ finalSlope = robustLr.slope;
423
+ finalYIntercept = robustLr.yIntercept;
424
+ }
425
+ // Calculate Final Residuals for ALL Original Points against the Robust Line
426
+ const pointsWithFinalResiduals = allPoints.map(point => {
427
+ const predictedYRobust = finalSlope * point.x + finalYIntercept;
428
+ const residual = point.y - predictedYRobust;
429
+ return Object.assign(Object.assign({}, point), {
430
+ residual: residual,
431
+ isSignificantlyHigh: false,
432
+ isSignificantlyLow: false
369
433
  });
370
- const finalResidualsForDetection = pointsWithFinalResiduals.map(p => p.residual);
371
- const robustResidualSpread = calculateMAD(finalResidualsForDetection);
372
- const SMALL_TOLERANCE = 1e-9;
373
- let effectiveDeviationMeasure = robustResidualSpread;
374
- const isSmallSet = allPoints.length <= 5;
375
- // Calculate the Y-range of the final data set for absolute thresholding
376
- const yValues = allPoints.map(p => p.y);
377
- const yMin = yValues.reduce((min, current) => Math.min(min, current), Infinity);
378
- const yMax = yValues.reduce((max, current) => Math.max(max, current), -Infinity);
379
- const yRange = yMax - yMin;
380
- // Set a very low absolute residual floor (e.g., 5% of the total range)
381
- const ABS_RESIDUAL_FLOOR = yRange * 0.05; // 5% of range
382
- if (effectiveDeviationMeasure < SMALL_TOLERANCE) {
383
- const maxResidual = finalResidualsForDetection.reduce((max, current) => Math.max(max, Math.abs(current)), 0);
384
- effectiveDeviationMeasure = maxResidual > SMALL_TOLERANCE ? maxResidual / (deviationThreshold + SMALL_TOLERANCE) : 0;
385
- }
386
- else if (isSmallSet) {
387
- // Aggressively reduce the spread measure for small sets with non-zero MAD
388
- effectiveDeviationMeasure = effectiveDeviationMeasure / 1.5;
389
- }
390
- const significantDeviations = [];
391
- // Detect Significant Deviations using the robust spread
392
- if (effectiveDeviationMeasure > SMALL_TOLERANCE) {
393
- for (const pointWithRes of pointsWithFinalResiduals) {
394
- const isSignificantByMAD = Math.abs(pointWithRes.residual) > deviationThreshold * effectiveDeviationMeasure;
395
- const isSignificantByFloor = Math.abs(pointWithRes.residual) > ABS_RESIDUAL_FLOOR;
396
- if (isSignificantByMAD || isSignificantByFloor) {
397
- significantDeviations.push(Object.assign(Object.assign({}, pointWithRes), { isSignificantlyHigh: pointWithRes.residual > 0, isSignificantlyLow: pointWithRes.residual < 0 }));
398
- }
399
- }
400
- }
401
- // Check for Absolute Maximum Inclusion
402
- const maxIsIncluded = significantDeviations.some(sd => sd.y === yMax);
403
- if (!maxIsIncluded) {
404
- const maxPoint = allPoints.find(p => p.y === yMax);
405
- if (maxPoint) {
406
- // Flag the absolute max as a significant deviation if it was missed
407
- significantDeviations.push(Object.assign(Object.assign({}, maxPoint), { residual: maxPoint.y - (finalSlope * maxPoint.x + finalYIntercept), isSignificantlyHigh: true, isSignificantlyLow: false }));
408
- }
409
- }
410
- // Check for Absolute Minimum Inclusion
411
- const minIsIncluded = significantDeviations.some(sd => sd.y === yMin);
412
- if (!minIsIncluded) {
413
- const minPoint = allPoints.find(p => p.y === yMin);
414
- if (minPoint) {
415
- // Flag the absolute min as a significant deviation if it was missed
416
- significantDeviations.push(Object.assign(Object.assign({}, minPoint), { residual: minPoint.y - (finalSlope * minPoint.x + finalYIntercept), isSignificantlyHigh: false, isSignificantlyLow: true // It's the lowest point
417
- }));
418
- }
419
- }
420
- if (significantDeviations.length === 0) {
421
- return null;
422
- }
423
- let summary = "";
424
- const highestPoints = significantDeviations.filter(sd => sd.isSignificantlyHigh);
425
- const lowestPoints = significantDeviations.filter(sd => sd.isSignificantlyLow);
426
- const MAX_POINTS_TO_REPORT = 5;
427
- if (highestPoints.length > 0) {
428
- const uniqueHighestY = [...new Set(highestPoints.map(hp => hp.y))];
429
- uniqueHighestY.sort((a, b) => b - a);
430
- const limitedHighestY = uniqueHighestY.slice(0, MAX_POINTS_TO_REPORT);
431
- const formattedHighestY = limitedHighestY.map(y => truncateDecimals(y, decimalLength)).join(", ");
432
- summary += `${translate("Highest point(s):")} ${formattedHighestY}. `;
433
- }
434
- if (lowestPoints.length > 0) {
435
- const uniqueLowestY = [...new Set(lowestPoints.map(lp => lp.y))];
436
- uniqueLowestY.sort((a, b) => a - b);
437
- const limitedLowestY = uniqueLowestY.slice(0, MAX_POINTS_TO_REPORT);
438
- const formattedLowestY = limitedLowestY.map(y => truncateDecimals(y, decimalLength)).join(", ");
439
- summary += `${translate("Lowest point(s):")} ${formattedLowestY}. `;
440
- }
441
- return summary.trim();
434
+ });
435
+ const finalResidualsForDetection = pointsWithFinalResiduals.map(p => p.residual);
436
+ const robustResidualSpread = calculateMAD(finalResidualsForDetection);
437
+ const SMALL_TOLERANCE = 1e-9;
438
+ let effectiveDeviationMeasure = robustResidualSpread;
439
+ const isSmallSet = allPoints.length <= 5;
440
+ // Calculate the Y-range of the final data set for absolute thresholding
441
+ const yValues = allPoints.map(p => p.y);
442
+ const yMin = yValues.reduce((min, current) => Math.min(min, current), Infinity);
443
+ const yMax = yValues.reduce((max, current) => Math.max(max, current), -Infinity);
444
+ const yRange = yMax - yMin;
445
+ // Set a very low absolute residual floor (e.g., 5% of the total range)
446
+ const ABS_RESIDUAL_FLOOR = yRange * 0.05; // 5% of range
447
+ if (effectiveDeviationMeasure < SMALL_TOLERANCE) {
448
+ const maxResidual = finalResidualsForDetection.reduce((max, current) => Math.max(max, Math.abs(current)), 0);
449
+ effectiveDeviationMeasure = maxResidual > SMALL_TOLERANCE ? maxResidual / (deviationThreshold + SMALL_TOLERANCE) : 0;
450
+ } else if (isSmallSet) {
451
+ // Aggressively reduce the spread measure for small sets with non-zero MAD
452
+ effectiveDeviationMeasure = effectiveDeviationMeasure / 1.5;
453
+ }
454
+ const significantDeviations = [];
455
+ // Detect Significant Deviations using the robust spread
456
+ if (effectiveDeviationMeasure > SMALL_TOLERANCE) {
457
+ for (const pointWithRes of pointsWithFinalResiduals) {
458
+ const isSignificantByMAD = Math.abs(pointWithRes.residual) > deviationThreshold * effectiveDeviationMeasure;
459
+ const isSignificantByFloor = Math.abs(pointWithRes.residual) > ABS_RESIDUAL_FLOOR;
460
+ if (isSignificantByMAD || isSignificantByFloor) {
461
+ significantDeviations.push(Object.assign(Object.assign({}, pointWithRes), {
462
+ isSignificantlyHigh: pointWithRes.residual > 0,
463
+ isSignificantlyLow: pointWithRes.residual < 0
464
+ }));
465
+ }
466
+ }
467
+ }
468
+ // Check for Absolute Maximum Inclusion
469
+ const maxIsIncluded = significantDeviations.some(sd => sd.y === yMax);
470
+ if (!maxIsIncluded) {
471
+ const maxPoint = allPoints.find(p => p.y === yMax);
472
+ if (maxPoint) {
473
+ // Flag the absolute max as a significant deviation if it was missed
474
+ significantDeviations.push(Object.assign(Object.assign({}, maxPoint), {
475
+ residual: maxPoint.y - (finalSlope * maxPoint.x + finalYIntercept),
476
+ isSignificantlyHigh: true,
477
+ isSignificantlyLow: false
478
+ }));
479
+ }
480
+ }
481
+ // Check for Absolute Minimum Inclusion
482
+ const minIsIncluded = significantDeviations.some(sd => sd.y === yMin);
483
+ if (!minIsIncluded) {
484
+ const minPoint = allPoints.find(p => p.y === yMin);
485
+ if (minPoint) {
486
+ // Flag the absolute min as a significant deviation if it was missed
487
+ significantDeviations.push(Object.assign(Object.assign({}, minPoint), {
488
+ residual: minPoint.y - (finalSlope * minPoint.x + finalYIntercept),
489
+ isSignificantlyHigh: false,
490
+ isSignificantlyLow: true // It's the lowest point
491
+ }));
492
+ }
493
+ }
494
+ if (significantDeviations.length === 0) {
495
+ return null;
496
+ }
497
+ let summary = "";
498
+ const highestPoints = significantDeviations.filter(sd => sd.isSignificantlyHigh);
499
+ const lowestPoints = significantDeviations.filter(sd => sd.isSignificantlyLow);
500
+ const MAX_POINTS_TO_REPORT = 5;
501
+ if (highestPoints.length > 0) {
502
+ const uniqueHighestY = [...new Set(highestPoints.map(hp => hp.y))];
503
+ uniqueHighestY.sort((a, b) => b - a);
504
+ const limitedHighestY = uniqueHighestY.slice(0, MAX_POINTS_TO_REPORT);
505
+ const formattedHighestY = limitedHighestY.map(y => truncateDecimals(y, decimalLength)).join(", ");
506
+ summary += `${translate("Highest point(s):")} ${formattedHighestY}. `;
507
+ }
508
+ if (lowestPoints.length > 0) {
509
+ const uniqueLowestY = [...new Set(lowestPoints.map(lp => lp.y))];
510
+ uniqueLowestY.sort((a, b) => a - b);
511
+ const limitedLowestY = uniqueLowestY.slice(0, MAX_POINTS_TO_REPORT);
512
+ const formattedLowestY = limitedLowestY.map(y => truncateDecimals(y, decimalLength)).join(", ");
513
+ summary += `${translate("Lowest point(s):")} ${formattedLowestY}. `;
514
+ }
515
+ return summary.trim();
442
516
  }
443
517
  function calculateNumberOfSegments(points) {
444
- const minPointsForSegmentation = 50;
445
- const maxSegments = 8;
446
- const base = 5;
447
- if (points.length <= minPointsForSegmentation) {
448
- return 1;
449
- }
450
- const calculatedSegments = Math.floor(base * Math.log(points.length));
451
- return Math.min(calculatedSegments, maxSegments);
518
+ const minPointsForSegmentation = 50;
519
+ const maxSegments = 8;
520
+ const base = 5;
521
+ if (points.length <= minPointsForSegmentation) {
522
+ return 1;
523
+ }
524
+ const calculatedSegments = Math.floor(base * Math.log(points.length));
525
+ return Math.min(calculatedSegments, maxSegments);
452
526
  }
453
527
  function splitIntoSegments(points) {
454
- const numSegments = calculateNumberOfSegments(points);
455
- if (numSegments === 1) {
456
- return [points];
457
- }
458
- const segments = [];
459
- const segmentSize = Math.floor(points.length / numSegments);
460
- const remainder = points.length % numSegments;
461
- let startIndex = 0;
462
- for (let i = 0; i < numSegments; i++) {
463
- const currentSegmentSize = segmentSize + (i < remainder ? 1 : 0);
464
- const endIndex = startIndex + currentSegmentSize;
465
- segments.push(points.slice(startIndex, endIndex));
466
- startIndex = endIndex;
467
- }
468
- return segments;
528
+ const numSegments = calculateNumberOfSegments(points);
529
+ if (numSegments === 1) {
530
+ return [points];
531
+ }
532
+ const segments = [];
533
+ const segmentSize = Math.floor(points.length / numSegments);
534
+ const remainder = points.length % numSegments;
535
+ let startIndex = 0;
536
+ for (let i = 0; i < numSegments; i++) {
537
+ const currentSegmentSize = segmentSize + (i < remainder ? 1 : 0);
538
+ const endIndex = startIndex + currentSegmentSize;
539
+ segments.push(points.slice(startIndex, endIndex));
540
+ startIndex = endIndex;
541
+ }
542
+ return segments;
469
543
  }
470
544
  function detectComplexTrend(allPoints, datasetName, decimalLength, translate) {
471
- const segments = splitIntoSegments(allPoints);
472
- const segmentTrends = segments.map(segment => calculateLinearRegression(segment));
473
- const trendDirections = segmentTrends.map(trend => getTrendDirectionBySlope(trend.slope));
474
- const isComplex = new Set(trendDirections).size > 1;
475
- if (isComplex) {
476
- const summaryParts = trendDirections.map((direction, index) => {
477
- const startPoint = segments[index][0];
478
- const endPoint = segments[index][segments[index].length - 1];
479
- return translate(`Segment {INDEX} {DIRECTION} from {FROM} at {X0} to {TO} at {X}.`)
480
- .replace("{INDEX}", (index + 1).toString())
481
- .replace("{DIRECTION}", direction)
482
- .replace("{FROM}", truncateDecimals(startPoint.y, decimalLength).toString())
483
- .replace("{TO}", truncateDecimals(endPoint.y, decimalLength).toString())
484
- .replace("{X0}", startPoint.originX.toString())
485
- .replace("{X}", endPoint.originX.toString());
486
- });
487
- const complexSummary = `${datasetName} ${translate("shows a complex trend with multiple segments:")} ${summaryParts.join(" ")}`;
488
- return complexSummary;
489
- }
490
- return false;
545
+ const segments = splitIntoSegments(allPoints);
546
+ const segmentTrends = segments.map(segment => calculateLinearRegression(segment));
547
+ const trendDirections = segmentTrends.map(trend => getTrendDirectionBySlope(trend.slope));
548
+ const isComplex = new Set(trendDirections).size > 1;
549
+ if (isComplex) {
550
+ const summaryParts = trendDirections.map((direction, index) => {
551
+ const startPoint = segments[index][0];
552
+ const endPoint = segments[index][segments[index].length - 1];
553
+ return translate("Segment {INDEX} {DIRECTION} from {FROM} at {X0} to {TO} at {X}.").replace("{INDEX}", (index + 1).toString()).replace("{DIRECTION}", direction).replace("{FROM}", truncateDecimals(startPoint.y, decimalLength).toString()).replace("{TO}", truncateDecimals(endPoint.y, decimalLength).toString()).replace("{X0}", startPoint.originX.toString()).replace("{X}", endPoint.originX.toString());
554
+ });
555
+ const complexSummary = `${datasetName} ${translate("shows a complex trend with multiple segments:")} ${summaryParts.join(" ")}`;
556
+ return complexSummary;
557
+ }
558
+ return false;
491
559
  }
492
560
  function calculateAverageValue(points) {
493
- const yValues = points.map(point => point.y);
494
- const validNumbers = yValues.filter(n => typeof n === "number");
495
- if (validNumbers.length === 0) {
496
- return null;
497
- }
498
- const sum = validNumbers.reduce((total, current) => total + current, 0);
499
- return sum / validNumbers.length;
561
+ const yValues = points.map(point => point.y);
562
+ const validNumbers = yValues.filter(n => typeof n === "number");
563
+ if (validNumbers.length === 0) {
564
+ return null;
565
+ }
566
+ const sum = validNumbers.reduce((total, current) => total + current, 0);
567
+ return sum / validNumbers.length;
500
568
  }
501
569
  function detectDatasetTrend(dataset, translate, dateFormatter, miniChartLabels, options) {
502
- const datasetName = dataset.label || translate("Data");
503
- const twoState = detectTwoStateChart(dataset, translate, dateFormatter, miniChartLabels, options);
504
- if (twoState) {
505
- return { trendDirection: "flat", summary: twoState };
506
- }
507
- const datasetObj = Object.assign({}, dataset);
508
- if (!miniChartLabels) {
509
- // simplify the data before trend detection
510
- datasetObj.data = simplifyData(datasetObj.data);
511
- }
512
- const allPoints = collectAllPoints(datasetObj, dateFormatter, miniChartLabels, options);
513
- if (allPoints.length === 0) {
514
- return { trendDirection: "undefined", summary: translate("No data available") };
515
- }
516
- const allUniqueYValues = [...new Set(allPoints.map(p => p.y))];
517
- const decimalLength = calculateMinTruncationPrecision(allUniqueYValues);
518
- if (allPoints.length === 1) {
519
- return { trendDirection: "flat", summary: translate("Only one data point: value is {VALUE}.").replace("{VALUE}", truncateDecimals(allPoints[0].y, decimalLength).toString()) };
520
- }
521
- const complexTrend = detectComplexTrend(allPoints, datasetName, decimalLength, translate);
522
- if (complexTrend) {
523
- return { trendDirection: "complex", summary: complexTrend };
524
- }
525
- const { slope } = calculateLinearRegression(allPoints);
526
- let trendDirection = getTrendDirectionBySlope(slope);
527
- const firstPoint = allPoints[0];
528
- const lastPoint = allPoints[allPoints.length - 1];
529
- const yValues = allPoints.map(p => p.y);
530
- const yMin = yValues.reduce((min, current) => Math.min(min, current));
531
- const yMax = yValues.reduce((max, current) => Math.max(max, current));
532
- const yRange = yMax - yMin;
533
- const netChange = lastPoint.y - firstPoint.y;
534
- // A tolerance to decide if the net change is significant enough
535
- // to overrule a weak linear regression. (e.g., a 5% change of the total range).
536
- const NARRATIVE_TREND_THRESHOLD = 0.05;
537
- // If the linear regression detected a non-flat trend, check if it's strong enough.
538
- if (trendDirection !== "flat" && yRange > 1e-9) {
539
- const netChangeRatio = Math.abs(netChange / yRange);
540
- if (netChangeRatio < NARRATIVE_TREND_THRESHOLD) {
541
- trendDirection = "flat";
542
- }
543
- else {
544
- const netDirection = netChange > 1e-9 ? "increases" : "decreases";
545
- if (trendDirection !== netDirection) {
546
- trendDirection = netDirection;
547
- }
548
- }
549
- }
550
- let summary;
551
- if (trendDirection === "flat") {
552
- const averageValue = calculateAverageValue(allPoints);
553
- const averageString = averageValue ? ` ${translate("Average value is:")} ${truncateDecimals(averageValue, decimalLength)}` : "";
554
- const FLAT_TREND_TOLERANCE = 0.25;
555
- if (yMax - yMin < FLAT_TREND_TOLERANCE) {
556
- summary = `${datasetName} ${translate("shows a relatively flat or stable trend.")} ${averageString}`;
557
- }
558
- else {
559
- const formattedYMin = truncateDecimals(yMin, decimalLength);
560
- const formattedYMax = truncateDecimals(yMax, decimalLength);
561
- summary = `${datasetName} ${translate("value changes in the range from {MIN} to {MAX}.")
562
- .replace("{MIN}", formattedYMin.toString())
563
- .replace("{MAX}", formattedYMax.toString())}
570
+ const datasetName = dataset.label || translate("Data");
571
+ const twoState = detectTwoStateChart(dataset, translate, dateFormatter, miniChartLabels, options);
572
+ if (twoState) {
573
+ return {
574
+ trendDirection: "flat",
575
+ summary: twoState
576
+ };
577
+ }
578
+ const datasetObj = Object.assign({}, dataset);
579
+ if (!miniChartLabels) {
580
+ // simplify the data before trend detection
581
+ datasetObj.data = simplifyData(datasetObj.data);
582
+ }
583
+ const allPoints = collectAllPoints(datasetObj, dateFormatter, miniChartLabels, options);
584
+ if (allPoints.length === 0) {
585
+ return {
586
+ trendDirection: "undefined",
587
+ summary: translate("No data available")
588
+ };
589
+ }
590
+ const allUniqueYValues = [...new Set(allPoints.map(p => p.y))];
591
+ const decimalLength = calculateMinTruncationPrecision(allUniqueYValues);
592
+ if (allPoints.length === 1) {
593
+ return {
594
+ trendDirection: "flat",
595
+ summary: translate("Only one data point: value is {VALUE}.").replace("{VALUE}", truncateDecimals(allPoints[0].y, decimalLength).toString())
596
+ };
597
+ }
598
+ const complexTrend = detectComplexTrend(allPoints, datasetName, decimalLength, translate);
599
+ if (complexTrend) {
600
+ return {
601
+ trendDirection: "complex",
602
+ summary: complexTrend
603
+ };
604
+ }
605
+ const {
606
+ slope
607
+ } = calculateLinearRegression(allPoints);
608
+ let trendDirection = getTrendDirectionBySlope(slope);
609
+ const firstPoint = allPoints[0];
610
+ const lastPoint = allPoints[allPoints.length - 1];
611
+ const yValues = allPoints.map(p => p.y);
612
+ const yMin = yValues.reduce((min, current) => Math.min(min, current));
613
+ const yMax = yValues.reduce((max, current) => Math.max(max, current));
614
+ const yRange = yMax - yMin;
615
+ const netChange = lastPoint.y - firstPoint.y;
616
+ // A tolerance to decide if the net change is significant enough
617
+ // to overrule a weak linear regression. (e.g., a 5% change of the total range).
618
+ const NARRATIVE_TREND_THRESHOLD = 0.05;
619
+ // If the linear regression detected a non-flat trend, check if it's strong enough.
620
+ if (trendDirection !== "flat" && yRange > 1e-9) {
621
+ const netChangeRatio = Math.abs(netChange / yRange);
622
+ if (netChangeRatio < NARRATIVE_TREND_THRESHOLD) {
623
+ trendDirection = "flat";
624
+ } else {
625
+ const netDirection = netChange > 1e-9 ? "increases" : "decreases";
626
+ if (trendDirection !== netDirection) {
627
+ trendDirection = netDirection;
628
+ }
629
+ }
630
+ }
631
+ let summary;
632
+ if (trendDirection === "flat") {
633
+ const averageValue = calculateAverageValue(allPoints);
634
+ const averageString = averageValue ? ` ${translate("Average value is:")} ${truncateDecimals(averageValue, decimalLength)}` : "";
635
+ const FLAT_TREND_TOLERANCE = 0.25;
636
+ if (yMax - yMin < FLAT_TREND_TOLERANCE) {
637
+ summary = `${datasetName} ${translate("shows a relatively flat or stable trend.")} ${averageString}`;
638
+ } else {
639
+ const formattedYMin = truncateDecimals(yMin, decimalLength);
640
+ const formattedYMax = truncateDecimals(yMax, decimalLength);
641
+ summary = `${datasetName} ${translate("value changes in the range from {MIN} to {MAX}.").replace("{MIN}", formattedYMin.toString()).replace("{MAX}", formattedYMax.toString())}
564
642
  ${averageString}`;
565
- }
566
- }
567
- else {
568
- summary = `${datasetName} ${trendDirection} ${translate("from {FROM} at {X0} to {TO} at {X}.")
569
- .replace("{FROM}", truncateDecimals(firstPoint.y, decimalLength).toString())
570
- .replace("{TO}", truncateDecimals(lastPoint.y, decimalLength).toString())
571
- .replace("{X0}", firstPoint.originX.toString())
572
- .replace("{X}", lastPoint.originX.toString())}`;
573
- const significantDeviations = detectSignificantDeviations(allPoints, translate, decimalLength);
574
- if (significantDeviations && typeof significantDeviations === "string") {
575
- summary += ` ${significantDeviations}`;
576
- }
577
643
  }
578
- return { trendDirection, summary };
644
+ } else {
645
+ summary = `${datasetName} ${trendDirection} ${translate("from {FROM} at {X0} to {TO} at {X}.").replace("{FROM}", truncateDecimals(firstPoint.y, decimalLength).toString()).replace("{TO}", truncateDecimals(lastPoint.y, decimalLength).toString()).replace("{X0}", firstPoint.originX.toString()).replace("{X}", lastPoint.originX.toString())}`;
646
+ const significantDeviations = detectSignificantDeviations(allPoints, translate, decimalLength);
647
+ if (significantDeviations && typeof significantDeviations === "string") {
648
+ summary += ` ${significantDeviations}`;
649
+ }
650
+ }
651
+ return {
652
+ trendDirection,
653
+ summary
654
+ };
579
655
  }
580
- export const AccessibleChartNarrative = ({ data, options }) => {
581
- const { translate } = useLanguage();
582
- const miniChartLabels = useMemo(() => data.labels, [data]);
583
- const { dateFormat, toLocalDateTime } = useContext(userFormatContext);
584
- const dateFormatter = useCallback((d, format = dateFormat) => formatDate(toLocalDateTime(d), format, translate), [toLocalDateTime, dateFormat, translate]);
585
- const filteredDatasets = removeTrendDataFromDatasets(data.datasets);
586
- const datasetsWithAnalysis = useMemo(() => filteredDatasets.map(dataset => {
587
- const datasetObj = Object.assign({}, dataset);
588
- try {
589
- const trend = detectDatasetTrend(datasetObj, translate, dateFormatter, miniChartLabels, options);
590
- return trend.summary;
591
- }
592
- catch (error) {
593
- const errMsg = `${translate("Error generating chart narrative for dataset")} ${dataset.label || ""}`;
594
- console.error(errMsg, error);
595
- return errMsg;
596
- }
597
- }), [filteredDatasets, translate, dateFormatter, miniChartLabels, options]);
598
- return _jsx("div", { className: "screen-reader-only", children: datasetsWithAnalysis.map((analysis, index) => _jsx("div", { children: analysis }, index)) });
599
- };
656
+ export const AccessibleChartNarrative = ({
657
+ data,
658
+ options
659
+ }) => {
660
+ const {
661
+ translate
662
+ } = useLanguage();
663
+ const miniChartLabels = useMemo(() => data.labels, [data]);
664
+ const {
665
+ dateFormat,
666
+ toLocalDateTime
667
+ } = useContext(userFormatContext);
668
+ const dateFormatter = useCallback((d, format = dateFormat) => formatDate(toLocalDateTime(d), format, translate), [toLocalDateTime, dateFormat, translate]);
669
+ const filteredDatasets = removeTrendDataFromDatasets(data.datasets);
670
+ const datasetsWithAnalysis = useMemo(() => filteredDatasets.map(dataset => {
671
+ const datasetObj = Object.assign({}, dataset);
672
+ try {
673
+ const trend = detectDatasetTrend(datasetObj, translate, dateFormatter, miniChartLabels, options);
674
+ return trend.summary;
675
+ } catch (error) {
676
+ const errMsg = `${translate("Error generating chart narrative for dataset")} ${dataset.label || ""}`;
677
+ console.error(errMsg, error);
678
+ return errMsg;
679
+ }
680
+ }), [filteredDatasets, translate, dateFormatter, miniChartLabels, options]);
681
+ return _jsx("div", {
682
+ className: "screen-reader-only",
683
+ children: datasetsWithAnalysis.map((analysis, index) => _jsx("div", {
684
+ children: analysis
685
+ }, index))
686
+ });
687
+ };