@rio-cloud/rio-uikit 1.7.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/BarList.d.ts +2 -0
  2. package/BarList.js +2 -0
  3. package/README.md +4 -0
  4. package/SaveableDateInput.d.ts +2 -0
  5. package/SaveableDateInput.js +2 -0
  6. package/SvgImage.d.ts +2 -0
  7. package/SvgImage.js +2 -0
  8. package/TableCol.d.ts +2 -0
  9. package/TableCol.js +2 -0
  10. package/TableHead.d.ts +2 -0
  11. package/TableHead.js +2 -0
  12. package/components/actionBarItem/ActionBarItemIcon.js +1 -1
  13. package/components/actionBarItem/ActionBarOverlay.js +1 -1
  14. package/components/activity/Activity.d.ts +2 -2
  15. package/components/applicationHeader/CollapsedNavItem.js +1 -0
  16. package/components/assetTree/Tree.d.ts +20 -0
  17. package/components/assetTree/Tree.js +75 -38
  18. package/components/assetTree/TreeLeaf.js +1 -1
  19. package/components/assetTree/TreeNodeContainer.d.ts +1 -1
  20. package/components/assetTree/TreeSearch.js +1 -1
  21. package/components/assetTree/TreeSummary.js +1 -1
  22. package/components/assetTree/TypeCounter.d.ts +2 -0
  23. package/components/assetTree/TypeCounter.js +1 -1
  24. package/components/assetTree/useTreeExpansion.d.ts +4 -0
  25. package/components/assetTree/useTreeExpansion.js +25 -0
  26. package/components/assetTree/useTreeHeight.d.ts +1 -0
  27. package/components/assetTree/useTreeHeight.js +60 -0
  28. package/components/assetTree/useTreeScrollPosition.d.ts +3 -0
  29. package/components/assetTree/useTreeScrollPosition.js +19 -0
  30. package/components/assetTree/useTreeVirtualization.d.ts +17 -0
  31. package/components/assetTree/useTreeVirtualization.js +71 -0
  32. package/components/autosuggest/AutoSuggest.js +2 -1
  33. package/components/barList/BarList.d.ts +97 -0
  34. package/components/barList/BarList.js +42 -0
  35. package/components/barList/useSortedBars.d.ts +2 -0
  36. package/components/barList/useSortedBars.js +14 -0
  37. package/components/button/ButtonToolbar.d.ts +1 -1
  38. package/components/button/ButtonToolbar.js +1 -1
  39. package/components/button/ToggleButton.js +0 -1
  40. package/components/charts/PieChart.js +1 -1
  41. package/components/clearableInput/ClearableInput.d.ts +20 -7
  42. package/components/clearableInput/ClearableInput.js +68 -8
  43. package/components/dialog/Dialog.js +1 -1
  44. package/components/dialog/FrameDialog.js +1 -1
  45. package/components/dropdown/ButtonDropdown.d.ts +11 -3
  46. package/components/dropdown/ButtonDropdown.js +79 -64
  47. package/components/dropdown/DropdownToggleButton.d.ts +7 -4
  48. package/components/dropdown/DropdownToggleButton.js +11 -3
  49. package/components/formLabel/FormLabel.d.ts +2 -2
  50. package/components/formLabel/FormLabel.js +1 -1
  51. package/components/listMenu/ListMenu.js +4 -1
  52. package/components/map/components/Map.js +21 -6
  53. package/components/map/components/constants.d.ts +2 -0
  54. package/components/map/components/constants.js +3 -0
  55. package/components/map/components/features/basics/InfoBubble.js +1 -1
  56. package/components/map/components/features/layers/overlayLayers/IncidentsLayer.js +1 -1
  57. package/components/map/utils/mapTypes.d.ts +5 -0
  58. package/components/map/utils/rendering.d.ts +5 -2
  59. package/components/map/utils/rendering.js +46 -39
  60. package/components/menuItems/MenuItem.js +1 -1
  61. package/components/notification/Notification.js +1 -1
  62. package/components/overlay/OverlayTrigger.js +3 -3
  63. package/components/saveableInput/SaveableDateInput.d.ts +83 -0
  64. package/components/saveableInput/SaveableDateInput.js +122 -0
  65. package/components/selects/BaseSelectDropdown.js +1 -1
  66. package/components/selects/Multiselect.d.ts +8 -0
  67. package/components/selects/Multiselect.js +4 -4
  68. package/components/smoothScrollbars/SmoothScrollbars.d.ts +1 -0
  69. package/components/smoothScrollbars/SmoothScrollbars.js +3 -3
  70. package/components/statsWidget/StatsWidget.d.ts +2 -2
  71. package/components/statsWidget/StatsWidgets.d.ts +2 -2
  72. package/components/svgImage/SvgElement.d.ts +8 -0
  73. package/components/svgImage/SvgElement.js +11 -0
  74. package/components/svgImage/SvgImage.d.ts +30 -0
  75. package/components/svgImage/SvgImage.js +20 -0
  76. package/components/svgImage/svgConverter.d.ts +17 -0
  77. package/components/svgImage/svgConverter.js +78 -0
  78. package/components/svgImage/useSvgLoader.d.ts +9 -0
  79. package/components/svgImage/useSvgLoader.js +43 -0
  80. package/components/switch/Switch.d.ts +4 -0
  81. package/components/switch/Switch.js +5 -6
  82. package/components/table/TableCardsSorting.d.ts +0 -1
  83. package/components/table/TableCol.d.ts +18 -0
  84. package/components/table/TableCol.js +11 -0
  85. package/components/table/TableHead.d.ts +33 -0
  86. package/components/table/TableHead.js +11 -0
  87. package/components/table/TableSettingsDialog.js +1 -1
  88. package/components/tag/Tag.js +1 -1
  89. package/components/timepicker/TimePicker.d.ts +1 -2
  90. package/components/timepicker/TimePicker.js +35 -8
  91. package/components/tooltip/SimpleTooltip.d.ts +1 -1
  92. package/components/virtualList/VirtualList.js +1 -1
  93. package/hooks/useIsFocusWithin.d.ts +33 -0
  94. package/hooks/useIsFocusWithin.js +55 -0
  95. package/hooks/useLocationSuggestions.d.ts +27 -0
  96. package/hooks/useLocationSuggestions.js +94 -0
  97. package/hooks/useOnboarding.d.ts +17 -5
  98. package/hooks/useOnboarding.js +7 -1
  99. package/hooks/usePostMessage.js +0 -1
  100. package/hooks/useSearch.d.ts +63 -0
  101. package/hooks/useSearch.js +73 -0
  102. package/hooks/useSorting.d.ts +6 -0
  103. package/hooks/useSorting.js +7 -4
  104. package/hooks/useTableExport.d.ts +49 -0
  105. package/hooks/useTableExport.js +57 -0
  106. package/hooks/useTableSelection.d.ts +166 -0
  107. package/hooks/useTableSelection.js +201 -0
  108. package/lib/es/BarList.d.ts +2 -0
  109. package/lib/es/BarList.js +7 -0
  110. package/lib/es/SaveableDateInput.d.ts +2 -0
  111. package/lib/es/SaveableDateInput.js +7 -0
  112. package/lib/es/SvgImage.d.ts +2 -0
  113. package/lib/es/SvgImage.js +7 -0
  114. package/lib/es/TableCol.d.ts +2 -0
  115. package/lib/es/TableCol.js +7 -0
  116. package/lib/es/TableHead.d.ts +2 -0
  117. package/lib/es/TableHead.js +7 -0
  118. package/lib/es/components/actionBarItem/ActionBarItemIcon.js +1 -1
  119. package/lib/es/components/actionBarItem/ActionBarOverlay.js +1 -1
  120. package/lib/es/components/activity/Activity.d.ts +2 -2
  121. package/lib/es/components/applicationHeader/CollapsedNavItem.js +1 -0
  122. package/lib/es/components/assetTree/Tree.d.ts +20 -0
  123. package/lib/es/components/assetTree/Tree.js +74 -37
  124. package/lib/es/components/assetTree/TreeLeaf.js +1 -1
  125. package/lib/es/components/assetTree/TreeNodeContainer.d.ts +1 -1
  126. package/lib/es/components/assetTree/TreeSearch.js +1 -1
  127. package/lib/es/components/assetTree/TreeSummary.js +1 -1
  128. package/lib/es/components/assetTree/TypeCounter.d.ts +2 -0
  129. package/lib/es/components/assetTree/TypeCounter.js +1 -1
  130. package/lib/es/components/assetTree/useTreeExpansion.d.ts +4 -0
  131. package/lib/es/components/assetTree/useTreeExpansion.js +29 -0
  132. package/lib/es/components/assetTree/useTreeHeight.d.ts +1 -0
  133. package/lib/es/components/assetTree/useTreeHeight.js +64 -0
  134. package/lib/es/components/assetTree/useTreeScrollPosition.d.ts +3 -0
  135. package/lib/es/components/assetTree/useTreeScrollPosition.js +23 -0
  136. package/lib/es/components/assetTree/useTreeVirtualization.d.ts +17 -0
  137. package/lib/es/components/assetTree/useTreeVirtualization.js +76 -0
  138. package/lib/es/components/autosuggest/AutoSuggest.js +2 -1
  139. package/lib/es/components/barList/BarList.d.ts +97 -0
  140. package/lib/es/components/barList/BarList.js +45 -0
  141. package/lib/es/components/barList/useSortedBars.d.ts +2 -0
  142. package/lib/es/components/barList/useSortedBars.js +17 -0
  143. package/lib/es/components/button/ButtonToolbar.d.ts +1 -1
  144. package/lib/es/components/button/ButtonToolbar.js +1 -1
  145. package/lib/es/components/button/ToggleButton.js +0 -1
  146. package/lib/es/components/charts/PieChart.js +1 -1
  147. package/lib/es/components/clearableInput/ClearableInput.d.ts +20 -7
  148. package/lib/es/components/clearableInput/ClearableInput.js +67 -7
  149. package/lib/es/components/dialog/Dialog.js +1 -1
  150. package/lib/es/components/dialog/FrameDialog.js +1 -1
  151. package/lib/es/components/dropdown/ButtonDropdown.d.ts +11 -3
  152. package/lib/es/components/dropdown/ButtonDropdown.js +79 -64
  153. package/lib/es/components/dropdown/DropdownToggleButton.d.ts +7 -4
  154. package/lib/es/components/dropdown/DropdownToggleButton.js +11 -3
  155. package/lib/es/components/formLabel/FormLabel.d.ts +2 -2
  156. package/lib/es/components/formLabel/FormLabel.js +1 -1
  157. package/lib/es/components/listMenu/ListMenu.js +4 -1
  158. package/lib/es/components/map/components/Map.js +20 -5
  159. package/lib/es/components/map/components/constants.d.ts +2 -0
  160. package/lib/es/components/map/components/constants.js +4 -1
  161. package/lib/es/components/map/components/features/basics/InfoBubble.js +1 -1
  162. package/lib/es/components/map/components/features/layers/overlayLayers/IncidentsLayer.js +1 -1
  163. package/lib/es/components/map/utils/mapTypes.d.ts +5 -0
  164. package/lib/es/components/map/utils/rendering.d.ts +5 -2
  165. package/lib/es/components/map/utils/rendering.js +46 -39
  166. package/lib/es/components/menuItems/MenuItem.js +1 -1
  167. package/lib/es/components/notification/Notification.js +1 -1
  168. package/lib/es/components/overlay/OverlayTrigger.js +3 -3
  169. package/lib/es/components/saveableInput/SaveableDateInput.d.ts +83 -0
  170. package/lib/es/components/saveableInput/SaveableDateInput.js +125 -0
  171. package/lib/es/components/selects/BaseSelectDropdown.js +1 -1
  172. package/lib/es/components/selects/Multiselect.d.ts +8 -0
  173. package/lib/es/components/selects/Multiselect.js +4 -4
  174. package/lib/es/components/smoothScrollbars/SmoothScrollbars.d.ts +1 -0
  175. package/lib/es/components/smoothScrollbars/SmoothScrollbars.js +3 -3
  176. package/lib/es/components/statsWidget/StatsWidget.d.ts +2 -2
  177. package/lib/es/components/statsWidget/StatsWidgets.d.ts +2 -2
  178. package/lib/es/components/svgImage/SvgElement.d.ts +8 -0
  179. package/lib/es/components/svgImage/SvgElement.js +14 -0
  180. package/lib/es/components/svgImage/SvgImage.d.ts +30 -0
  181. package/lib/es/components/svgImage/SvgImage.js +23 -0
  182. package/lib/es/components/svgImage/svgConverter.d.ts +17 -0
  183. package/lib/es/components/svgImage/svgConverter.js +84 -0
  184. package/lib/es/components/svgImage/useSvgLoader.d.ts +9 -0
  185. package/lib/es/components/svgImage/useSvgLoader.js +48 -0
  186. package/lib/es/components/switch/Switch.d.ts +4 -0
  187. package/lib/es/components/switch/Switch.js +5 -6
  188. package/lib/es/components/table/TableCardsSorting.d.ts +0 -1
  189. package/lib/es/components/table/TableCol.d.ts +18 -0
  190. package/lib/es/components/table/TableCol.js +13 -0
  191. package/lib/es/components/table/TableHead.d.ts +33 -0
  192. package/lib/es/components/table/TableHead.js +14 -0
  193. package/lib/es/components/table/TableSettingsDialog.js +1 -1
  194. package/lib/es/components/tag/Tag.js +1 -1
  195. package/lib/es/components/timepicker/TimePicker.d.ts +1 -2
  196. package/lib/es/components/timepicker/TimePicker.js +35 -8
  197. package/lib/es/components/tooltip/SimpleTooltip.d.ts +1 -1
  198. package/lib/es/components/virtualList/VirtualList.js +1 -1
  199. package/lib/es/hooks/useIsFocusWithin.d.ts +33 -0
  200. package/lib/es/hooks/useIsFocusWithin.js +57 -0
  201. package/lib/es/hooks/useLocationSuggestions.d.ts +27 -0
  202. package/lib/es/hooks/useLocationSuggestions.js +97 -0
  203. package/lib/es/hooks/useOnboarding.d.ts +17 -5
  204. package/lib/es/hooks/useOnboarding.js +7 -1
  205. package/lib/es/hooks/usePostMessage.js +0 -1
  206. package/lib/es/hooks/useSearch.d.ts +63 -0
  207. package/lib/es/hooks/useSearch.js +75 -0
  208. package/lib/es/hooks/useSorting.d.ts +6 -0
  209. package/lib/es/hooks/useSorting.js +7 -4
  210. package/lib/es/hooks/useTableExport.d.ts +49 -0
  211. package/lib/es/hooks/useTableExport.js +59 -0
  212. package/lib/es/hooks/useTableSelection.d.ts +166 -0
  213. package/lib/es/hooks/useTableSelection.js +210 -0
  214. package/lib/es/themes/Volkswagen/components/applicationHeader/VolkswagenApplicationHeader.js +1 -1
  215. package/lib/es/useIsFocusWithin.d.ts +2 -0
  216. package/lib/es/useIsFocusWithin.js +7 -0
  217. package/lib/es/useLocationSuggestions.d.ts +2 -0
  218. package/lib/es/useLocationSuggestions.js +7 -0
  219. package/lib/es/useSearch.d.ts +2 -0
  220. package/lib/es/useSearch.js +7 -0
  221. package/lib/es/useTableExport.d.ts +2 -0
  222. package/lib/es/useTableExport.js +7 -0
  223. package/lib/es/useTableSelection.d.ts +2 -0
  224. package/lib/es/useTableSelection.js +7 -0
  225. package/lib/es/utils/storageUtils.d.ts +2 -2
  226. package/lib/es/utils/storageUtils.js +2 -0
  227. package/lib/es/utils/urlFeatureToggles.d.ts +12 -6
  228. package/lib/es/utils/urlFeatureToggles.js +14 -8
  229. package/lib/es/utils/useDropDirection.js +1 -0
  230. package/lib/es/version.json +1 -1
  231. package/package.json +48 -40
  232. package/themes/Volkswagen/components/applicationHeader/VolkswagenApplicationHeader.js +1 -1
  233. package/useIsFocusWithin.d.ts +2 -0
  234. package/useIsFocusWithin.js +2 -0
  235. package/useLocationSuggestions.d.ts +2 -0
  236. package/useLocationSuggestions.js +2 -0
  237. package/useSearch.d.ts +2 -0
  238. package/useSearch.js +2 -0
  239. package/useTableExport.d.ts +2 -0
  240. package/useTableExport.js +2 -0
  241. package/useTableSelection.d.ts +2 -0
  242. package/useTableSelection.js +2 -0
  243. package/utils/storageUtils.d.ts +2 -2
  244. package/utils/storageUtils.js +2 -0
  245. package/utils/urlFeatureToggles.d.ts +12 -6
  246. package/utils/urlFeatureToggles.js +10 -7
  247. package/utils/useDropDirection.js +1 -0
  248. package/version.json +1 -1
@@ -4,9 +4,17 @@ import classNames from 'classnames';
4
4
  import Button from '../button/Button';
5
5
  import useMergeRefs from '../../hooks/useMergeRefs';
6
6
  const DropdownToggleButton = forwardRef((props, ref) => {
7
- const { id, disabled, bsSize, bsStyle, variant, splitButton = false, onClick, className = '', children, outerRef, ...remainingProps } = props;
7
+ const { id, disabled, bsSize, bsStyle, variant, splitButton = false, onClick, toggleButtonType = 'button', className = '', children, outerRef, iconOnly, ...remainingProps } = props;
8
8
  const toggleRef = useMergeRefs(outerRef, ref);
9
- const labelButtonClasses = classNames(!splitButton && 'dropdown-toggle', !splitButton && className);
10
- return (_jsx(Button, { ...remainingProps, id: splitButton ? `button-${id}` : id, type: 'button', ref: toggleRef, disabled: disabled, bsStyle: bsStyle, bsSize: bsSize, variant: variant, onClick: onClick, className: labelButtonClasses, children: children }));
9
+ const buttonClasses = classNames(!splitButton && 'dropdown-toggle', !splitButton && className);
10
+ const labelClasses = classNames('dropdown-toggle', 'label', `label-${bsStyle}`, 'display-flex align-items-center', 'user-select-none cursor-pointer', disabled && 'pointer-events-none', className);
11
+ const tagClasses = classNames('dropdown-toggle', 'tag', 'clickable height-auto', bsSize === 'sm' && 'tag-small', 'display-flex align-items-center', 'user-select-none cursor-pointer', disabled && 'pointer-events-none', className);
12
+ if (toggleButtonType === 'tag') {
13
+ return (_jsx("div", { ...remainingProps, id: id, ref: toggleRef, onClick: onClick, className: tagClasses, children: children }));
14
+ }
15
+ if (toggleButtonType === 'label') {
16
+ return (_jsx("div", { ...remainingProps, id: id, ref: toggleRef, onClick: onClick, className: labelClasses, children: children }));
17
+ }
18
+ return (_jsx(Button, { ...remainingProps, id: splitButton ? `button-${id}` : id, type: 'button', ref: toggleRef, disabled: disabled, bsStyle: bsStyle, bsSize: bsSize, variant: variant, onClick: onClick, iconOnly: iconOnly, className: buttonClasses, children: children }));
11
19
  });
12
20
  export default DropdownToggleButton;
@@ -1,9 +1,9 @@
1
- import { type PropsWithChildren, type ComponentProps } from 'react';
1
+ import React, { type PropsWithChildren, type ComponentProps } from 'react';
2
2
  export type FormLabelProps = ComponentProps<'label'> & {
3
3
  /**
4
4
  * Additional supporting text displayed next to the label.
5
5
  */
6
- supportingText?: string;
6
+ supportingText?: string | React.ReactElement;
7
7
  /**
8
8
  * Additional classes set to the outer element.
9
9
  */
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // @ts-ignore-next-line importsNotUsedAsValues
2
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import classNames from 'classnames';
5
5
  const FormLabel = (props) => {
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // biome-ignore lint/style/useImportType: <explanation>
2
3
  import { useEffect, useRef, useState } from 'react';
3
4
  import isEmpty from 'lodash/fp/isEmpty';
4
5
  import debounce from 'lodash/fp/debounce';
@@ -73,7 +74,9 @@ const ListMenu = (props) => {
73
74
  }
74
75
  };
75
76
  const formClassNames = classNames('form-group', 'margin-bottom-5', 'padding-left-15', 'padding-right-15', 'padding-bottom-15', 'position-sticky', 'top-0', 'z-index-1');
76
- const listMenu = (_jsxs("div", { ...remainingProps, className: `ListMenu ${className} ${filterValue ? 'filtered' : ''}`, ref: listRef, children: [enableFilter && (_jsx("div", { className: formClassNames, children: _jsxs("div", { className: 'input-group width-100pct', children: [_jsx("span", { className: 'input-group-addon', children: _jsx("span", { className: 'rioglyph rioglyph-search', "aria-hidden": 'true' }) }), _jsx(ClearableInput, { value: filterValue, inputRef: inputRef, placeholder: filterPlaceholder, onChange: handleFilterChange, onClear: handleClear }), trailingInputAddon && trailingInputAddon] }) })), !hasMenuItems(filteredMenuItems) && (_jsx("div", { className: 'padding-top-25 text-center text-color-gray', children: notFoundMessage })), filteredMenuItems.map(menuGroup => (_jsx(ListMenuGroup, { className: groupClassName, menuGroup: menuGroup }, crypto.randomUUID())))] }));
77
+ const listMenu = (_jsxs("div", { ...remainingProps, className: `ListMenu ${className} ${filterValue ? 'filtered' : ''}`, ref: listRef, children: [enableFilter && (_jsx("div", { className: formClassNames, children: _jsxs("div", { className: 'input-group width-100pct', children: [_jsx("span", { className: 'input-group-addon', children: _jsx("span", { className: 'rioglyph rioglyph-search', "aria-hidden": 'true' }) }), _jsx(ClearableInput, { value: filterValue, inputRef: inputRef, placeholder: filterPlaceholder, onChange: handleFilterChange, onClear: handleClear }), trailingInputAddon && trailingInputAddon] }) })), !hasMenuItems(filteredMenuItems) && (_jsx("div", { className: 'padding-top-25 text-center text-color-gray', children: notFoundMessage })), filteredMenuItems.map((menuGroup, index) => (
78
+ // biome-ignore lint/suspicious/noArrayIndexKey: using a random key would re-render the list every time the parent updates
79
+ _jsx(ListMenuGroup, { className: groupClassName, menuGroup: menuGroup }, index)))] }));
77
80
  if (responsive && isMobileMode) {
78
81
  return (_jsx(ExpanderPanel, { title: mobileHeader, bsStyle: 'default', open: isExpanderOpen, onToggle: () => setIsExpanderOpen(!isExpanderOpen), unmountOnExit: false, className: 'shadow-default', children: _jsx("div", { onClick: handleExpanderBodyClick, children: listMenu }) }));
79
82
  }
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef, useState, useEffect } from 'react';
3
3
  import isEmpty from 'lodash/fp/isEmpty';
4
4
  import isEqual from 'lodash/fp/isEqual';
5
- import { MAP_TYPE_DEFAULT, MAP_TYPE_FLEET_STYLE, MAP_TYPE_SATELLITE, MAP_TYPE_TERRAIN, MAP_TYPE_NIGHT, MAP_LAYER_INCIDENTS, MAP_LAYER_TRAFFIC, MAP_LAYER_ROAD_RESTRICTIONS, } from './constants';
5
+ import { MAP_TYPE_DEFAULT, MAP_TYPE_FLEET_STYLE, MAP_TYPE_SATELLITE, MAP_TYPE_TERRAIN, MAP_TYPE_NIGHT, MAP_LAYER_INCIDENTS, MAP_LAYER_TRAFFIC, MAP_LAYER_ROAD_RESTRICTIONS, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, } from './constants';
6
6
  import { addEventListenerMap, removeEventListenerMap } from '../utils/eventHandling';
7
7
  import { MapContext } from './MapContext';
8
8
  import { createUtils, getMapBounds } from '../utils/mapUtils';
@@ -16,7 +16,13 @@ import MapElements from './MapElements';
16
16
  import { getPlatform, getEngineType, getBaseLayer } from '../utils/rendering';
17
17
  // biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
18
18
  const Map = (props) => {
19
- const { children, credentials, width, height, language: lang = 'en', center, zoom, zoomAnimation = false, boundingBox, hideClusterSettings = false, hideMapLayerSettings = false, disableMapEvents, disableBehavior = false, eventListenerMap, mapType = MAP_TYPE_DEFAULT, mapLayer = [], showCluster: externalShowCluster = true, showScaleBar = false, onMapLayerChange = () => { }, onMapTypeChange = () => { }, onShowClusterChange = () => { }, onZoomIn = () => { }, onZoomOut = () => { }, onIncidentsChange = () => { }, mapSettingsTooltip, mapSettings, enableWebGL = true, enableDevicePixelRatio = true, } = props;
19
+ const { children, credentials, width, height, language: lang = 'en', center, zoom, zoomAnimation = false, boundingBox, hideClusterSettings = false, hideMapLayerSettings = false, disableMapEvents, disableBehavior = false, disableZoomMomentum = true, eventListenerMap, mapType = MAP_TYPE_DEFAULT, mapLayer = [], showCluster: externalShowCluster = true, showScaleBar = false, onMapLayerChange = () => { }, onMapTypeChange = () => { }, onShowClusterChange = () => { }, onZoomIn = () => { }, onZoomOut = () => { }, onIncidentsChange = () => { }, mapSettingsTooltip, mapSettings, enableWebGL = true, enableDevicePixelRatio = true, minZoom = DEFAULT_MIN_ZOOM, maxZoom = DEFAULT_MAX_ZOOM, } = props;
20
+ let safeMaxZoom = maxZoom;
21
+ if (minZoom > maxZoom) {
22
+ // Avoid crashing the map for invalid zoom limits
23
+ safeMaxZoom = DEFAULT_MAX_ZOOM;
24
+ console.error('Invalid condition for zoom limits: min <= max)');
25
+ }
20
26
  const mapRef = useRef(null);
21
27
  const [isMapInitialized, setMapInitialized] = useState(false);
22
28
  const [api, setApi] = useState();
@@ -25,7 +31,7 @@ const Map = (props) => {
25
31
  const [showCluster, setShowCluster] = useState(externalShowCluster);
26
32
  const devicePixelRatio = enableDevicePixelRatio ? window.devicePixelRatio || 1 : 1;
27
33
  // Only short locale is supported by HERE Map
28
- const language = lang.split('-')[0];
34
+ const language = (locale => (locale === 'nb' ? 'no' : locale))(lang.split('-')[0].toLowerCase());
29
35
  useEffect(() => {
30
36
  let allMapObjects = [];
31
37
  if (api?.map) {
@@ -55,6 +61,8 @@ const Map = (props) => {
55
61
  engineType,
56
62
  vehicleRestrictions: activeLayers.includes(MAP_LAYER_ROAD_RESTRICTIONS),
57
63
  language,
64
+ minZoom,
65
+ maxZoom: safeMaxZoom,
58
66
  });
59
67
  // Initialize the map
60
68
  const hereMap = new H.Map(mapRef.current, defaultLayerMap.baseLayer, {
@@ -65,8 +73,8 @@ const Map = (props) => {
65
73
  pixelRatio: devicePixelRatio,
66
74
  fixedCenter: true,
67
75
  });
68
- // Render street labels and road signs as well as additional pois as vector data
69
76
  if (defaultLayerMap.overlayLayer) {
77
+ // Special case for satellite map where we render street labels and road signs as well as additional pois as vector data
70
78
  hereMap.addLayer(defaultLayerMap.overlayLayer);
71
79
  }
72
80
  setMapInitialized(true);
@@ -76,11 +84,16 @@ const Map = (props) => {
76
84
  if (!disableMapEvents) {
77
85
  hereMapEvents = new H.mapevents.MapEvents(hereMap);
78
86
  }
79
- // Enable map interaction (pan, zoom, pinch-to-zoom)
80
87
  if (!(disableMapEvents || disableBehavior)) {
81
88
  hereBehavior = new H.mapevents.Behavior(hereMapEvents);
82
89
  // @ts-ignore-next-line
83
90
  hereBehavior.disable(H.mapevents.Behavior.Feature.FRACTIONAL_ZOOM);
91
+ if (disableZoomMomentum) {
92
+ // Disable the inertia that occurs on WebGL maps when scrolling/zooming.
93
+ // This resulted in a very high scroll sensitivity.
94
+ // @ts-ignore-next-line
95
+ hereBehavior.disable(H.mapevents.Behavior.Feature.ZOOM_MOMENTUM);
96
+ }
84
97
  }
85
98
  addEventListenerMap(hereMap, eventListenerMap, hereMap);
86
99
  const hereUi = getHereUi(hereMap, defaultLayers, showScaleBar, language);
@@ -102,6 +115,8 @@ const Map = (props) => {
102
115
  baseLayer,
103
116
  activeLayers,
104
117
  showCluster,
118
+ minZoom,
119
+ maxZoom: safeMaxZoom,
105
120
  },
106
121
  });
107
122
  // If the base layer changed, re-add all previous objects again,
@@ -118,7 +133,7 @@ const Map = (props) => {
118
133
  hereMap.dispose();
119
134
  }
120
135
  };
121
- }, [enableWebGL, enableDevicePixelRatio, language, baseLayer]);
136
+ }, [enableWebGL, enableDevicePixelRatio, language, baseLayer, minZoom, maxZoom]);
122
137
  const isDarkMode = useDarkMode();
123
138
  useEffect(() => {
124
139
  // Toggle night map only if current map type is default
@@ -15,3 +15,5 @@ export declare const ENGINE_TYPE_HARP: any;
15
15
  export declare const ENGINE_TYPE_P2D = H.Map.EngineType.P2D;
16
16
  export declare const DEFAULT_TILE_SIZE = 512;
17
17
  export declare const DEFAULT_RASTER_LAYER_FORMAT = "png";
18
+ export declare const DEFAULT_MIN_ZOOM = 3;
19
+ export declare const DEFAULT_MAX_ZOOM = 20;
@@ -12,7 +12,10 @@ export const TILE_SIZE = 256;
12
12
  export const TILE_TYPE = 'maptile';
13
13
  export const NO_CREDENTIALS_ERROR_MESSAGE = 'Cannot instantiate the Map. Missing credentials property.';
14
14
  // @ts-ignore-next-line
15
+ // biome-ignore lint/complexity/useLiteralKeys: <explanation>
15
16
  export const ENGINE_TYPE_HARP = H.Map.EngineType['HARP'];
16
17
  export const ENGINE_TYPE_P2D = H.Map.EngineType.P2D;
17
18
  export const DEFAULT_TILE_SIZE = 512;
18
19
  export const DEFAULT_RASTER_LAYER_FORMAT = 'png'; // 'jpeg';
20
+ export const DEFAULT_MIN_ZOOM = 3;
21
+ export const DEFAULT_MAX_ZOOM = 20;
@@ -1,4 +1,4 @@
1
- // @ts-ignore-next-line importsNotUsedAsValues
1
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
2
2
  import React, { useEffect } from 'react';
3
3
  import ReactDomServer from 'react-dom/server';
4
4
  import noop from 'lodash/fp/noop';
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable max-len */
2
2
  /* eslint-disable prefer-arrow/prefer-arrow-functions */
3
- // @ts-ignore-next-line importsNotUsedAsValues
3
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
4
4
  import { useEffect, useState } from 'react';
5
5
  import { useMapContext } from '../../../MapContext';
6
6
  import { MAP_LAYER_INCIDENTS } from '../../../constants';
@@ -57,6 +57,7 @@ export type MapProps = {
57
57
  boundingBox?: BoundingBox;
58
58
  disableMapEvents?: boolean;
59
59
  disableBehavior?: boolean;
60
+ disableZoomMomentum?: boolean;
60
61
  eventListenerMap?: EventListenerMap;
61
62
  hideMapSettings?: boolean;
62
63
  hideClusterSettings?: boolean;
@@ -76,6 +77,8 @@ export type MapProps = {
76
77
  zoomAnimation?: boolean;
77
78
  enableWebGL?: boolean;
78
79
  enableDevicePixelRatio?: boolean;
80
+ minZoom?: number;
81
+ maxZoom?: number;
79
82
  children?: React.ReactNode | ((api: MapApi, incidents?: MapIncidents) => React.ReactNode);
80
83
  };
81
84
  export type MapApi = {
@@ -93,6 +96,8 @@ export type MapApi = {
93
96
  baseLayer?: MapType;
94
97
  activeLayers?: MapLayer[];
95
98
  showCluster?: boolean;
99
+ minZoom?: number;
100
+ maxZoom?: number;
96
101
  };
97
102
  };
98
103
  export type MapIncidents = {
@@ -2,7 +2,7 @@ import type { MapCredentials, MapType } from './mapTypes';
2
2
  export declare const getPPI: () => 100 | 200;
3
3
  export declare const isVectorBased: (enableWebGL: boolean, baseLayer: MapType) => boolean;
4
4
  export declare const getPlatform: (credentials: MapCredentials) => H.service.Platform;
5
- export declare const getBaseTileLayer: <T>(rasterTileService: T, engineType: H.Map.EngineType) => H.map.layer.TileLayer;
5
+ export declare const getBaseTileLayer: <T>(rasterTileService: T, engineType: H.Map.EngineType, minZoom: number, maxZoom: number) => H.map.layer.TileLayer;
6
6
  export declare const getBaseRasterTileService: (platform: H.service.Platform, style: string, features: string, lang: string) => any;
7
7
  export declare const getEngineType: (baseLayerName: string, enableWebGL: boolean) => any;
8
8
  export type BaseLayer = {
@@ -13,8 +13,11 @@ export type BaseLayer = {
13
13
  engineType: H.Map.EngineType;
14
14
  language: string;
15
15
  vehicleRestrictions: boolean;
16
+ minZoom: number;
17
+ maxZoom: number;
16
18
  };
17
- export declare const getBaseLayer: ({ baseLayerName, defaultLayers, enableWebGL, platform, engineType, language, vehicleRestrictions, }: BaseLayer) => {
19
+ export type BaseLayerResponse = {
18
20
  baseLayer: H.map.layer.Layer;
19
21
  overlayLayer?: H.map.layer.Layer;
20
22
  };
23
+ export declare const getBaseLayer: ({ baseLayerName, defaultLayers, enableWebGL, platform, engineType, language, vehicleRestrictions, minZoom, maxZoom, }: BaseLayer) => BaseLayerResponse;
@@ -10,15 +10,19 @@ export const getPlatform = (credentials) =>
10
10
  // Initiate and authenticate your connection to the HERE platform:
11
11
  new H.service.Platform({
12
12
  apikey: credentials.apikey ?? '',
13
- useHTTPS: location.protocol === 'https:' ? true : false,
13
+ useHTTPS: location.protocol === 'https:',
14
14
  });
15
- export const getBaseTileLayer = (rasterTileService, engineType) => {
15
+ export const getBaseTileLayer = (rasterTileService, engineType, minZoom, maxZoom) => {
16
16
  // @ts-ignore-next-line "H.service.rasterTile" is newer that the types
17
17
  const rasterTileProvider = new H.service.rasterTile.Provider(rasterTileService, {
18
18
  engineType,
19
19
  tileSize: 512,
20
20
  });
21
- return new H.map.layer.TileLayer(rasterTileProvider);
21
+ const rasterTileLayer = new H.map.layer.TileLayer(rasterTileProvider);
22
+ // force the tile layer minimum zoom (furthest away possible) and maximum zoom (nearest) to the desired level
23
+ rasterTileLayer.setMin(minZoom);
24
+ rasterTileLayer.setMax(maxZoom);
25
+ return rasterTileLayer;
22
26
  };
23
27
  export const getBaseRasterTileService = (platform, style, features, lang) => {
24
28
  // @ts-ignore-next-line "getRasterTileService" is newer that the types
@@ -42,79 +46,82 @@ export const getEngineType = (baseLayerName, enableWebGL) => {
42
46
  return enableWebGL ? ENGINE_TYPE_HARP : ENGINE_TYPE_P2D;
43
47
  }
44
48
  };
45
- export const getBaseLayer = ({ baseLayerName, defaultLayers, enableWebGL, platform, engineType, language, vehicleRestrictions, }) => {
49
+ export const getBaseLayer = ({ baseLayerName, defaultLayers, enableWebGL, platform, engineType, language, vehicleRestrictions, minZoom, maxZoom, }) => {
46
50
  const restriction = 'disabled'; // vehicleRestrictions ? 'active_and_inactive' : 'disabled';
47
51
  const features = `pois:all,environmental_zones:all,congestion_zones:all,vehicle_restrictions:${restriction}`;
48
52
  if (enableWebGL && baseLayerName === MAP_TYPE_DEFAULT) {
49
- // const rasterTileService = platform?.getRasterTileService({
50
- // format: 'png', // 'png', // or jpg, png8
51
- // queryParams: {
52
- // lang: 'en',
53
- // ppi: window.devicePixelRatio >= 1.7 ? 400 : 100,
54
- // // style: 'explore.day',
55
- // features: 'environmental_zones:all',
56
- // },
57
- // });
58
- // const rasterTileProvider = new H.service.rasterTile.Provider(rasterTileService, {
59
- // engineType: enableWebGL ? H.Map.EngineType['HARP'] : H.Map.EngineType['P2D'],
60
- // tileSize: 512,
61
- // });
62
- // const rasterTileLayer = new H.map.layer.TileLayer(rasterTileProvider);
63
- // const envLayer = rasterTileLayer;
64
- // // create a service that calls the custom endpoint
65
- // const service = platform.getOMVService({ path: 'v2/vectortiles/core/mc' });
66
- // // create a provider and a layer that use the custom service and a custom style
67
- // const provider = new H.service.omv.Provider(
68
- // service,
69
- // new H.map.Style('https://js.api.here.com/v3/3.1/styles/omv/oslo/japan/normal.day.yaml')
70
- // );
71
- // const layer = new H.map.layer.TileLayer(provider, { max: 22 });
53
+ defaultLayers?.vector.normal.map.setMin(minZoom);
54
+ defaultLayers?.vector.normal.map.setMax(maxZoom);
72
55
  return {
73
56
  baseLayer: defaultLayers?.vector.normal.map,
74
- // baseLayer: layer,
75
- // overlayLayer: envLayer,
76
57
  };
77
58
  }
78
59
  switch (baseLayerName) {
79
60
  case MAP_TYPE_SATELLITE: {
80
61
  if (enableWebGL) {
62
+ // This is a workaround for setting the min and max zoom limit for satellite map
63
+ // since the setMin/setMax cannot be set on the "defaultLayers.hybrid.liteday.raster".
64
+ // The HERE support suggests to create a hybrid layer for that and set the zoom limits
65
+ // on the two layers.
66
+ const hybridStyleConfig = {
67
+ base: {
68
+ style: 'oslo',
69
+ scheme: 'hybrid.day',
70
+ },
71
+ };
72
+ let baseLayer;
73
+ let overlayLayer;
74
+ // This method creates hybrid map layers. A hybrid map consists of raster satellite base layer and vector layers on top.
75
+ // Layers created by this method can be used only when the Map is instantiated with H.Map.EngineType.HARP engineType.
76
+ // See https://www.here.com/docs/bundle/maps-api-for-javascript-api-reference/page/H.service.Platform_4.html#createHybridLayers
77
+ // @ts-ignore-next-line
78
+ platform.createHybridLayers(hybridStyleConfig, (layers) => {
79
+ layers.raster.setMin(minZoom);
80
+ layers.raster.setMax(maxZoom);
81
+ layers.vector.setMin(minZoom);
82
+ layers.vector.setMax(maxZoom);
83
+ baseLayer = layers.raster;
84
+ overlayLayer = layers.vector;
85
+ }, console.error);
81
86
  return {
82
- // @ts-ignore-next-line "hybrid.liteday.raster" is newer that the types
83
- baseLayer: defaultLayers.hybrid.liteday.raster,
84
- // @ts-ignore-next-line "hybrid.day.vector" is newer that the types
85
- overlayLayer: defaultLayers.hybrid.day.vector,
87
+ baseLayer,
88
+ overlayLayer,
86
89
  };
87
90
  }
88
91
  return {
89
- baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'explore.satellite.day', features, language), engineType),
92
+ baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'explore.satellite.day', features, language), engineType, minZoom, maxZoom),
90
93
  };
91
94
  }
92
95
  case MAP_TYPE_TERRAIN: {
93
96
  return {
94
- baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'topo.day', features, language), engineType),
97
+ baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'topo.day', features, language), engineType, minZoom, maxZoom),
95
98
  };
96
99
  }
97
100
  case MAP_TYPE_FLEET_STYLE: {
98
101
  return {
99
- baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'logistics.day', features, language), ENGINE_TYPE_P2D),
102
+ baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'logistics.day', features, language), ENGINE_TYPE_P2D, minZoom, maxZoom),
100
103
  };
101
104
  }
102
105
  case MAP_TYPE_NIGHT: {
103
106
  if (enableWebGL) {
107
+ // @ts-ignore-next-line "setMin" is working on "vector.normal.mapnight"
108
+ defaultLayers?.vector.normal.mapnight.setMin(minZoom);
109
+ // @ts-ignore-next-line "setMax" is working on "vector.normal.mapnight"
110
+ defaultLayers?.vector.normal.mapnight.setMax(maxZoom);
104
111
  return {
105
- // @ts-ignore-next-line "vector.normal.mapnight" is newer that the types
112
+ // @ts-ignore-next-line "vector.normal.mapnight" is newer than the types
106
113
  baseLayer: defaultLayers.vector.normal.mapnight,
107
114
  };
108
115
  }
109
116
  return {
110
- baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'explore.night', features, language), engineType),
117
+ baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'explore.night', features, language), engineType, minZoom, maxZoom),
111
118
  };
112
119
  }
113
120
  default: {
114
121
  // As a replacement for "defaultLayers?.raster.normal.map" which uses the old and deprecated
115
122
  // H.service.MapTileService, we use the Raster Tile Service v3 instead
116
123
  return {
117
- baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'explore.day', features, language), engineType),
124
+ baseLayer: getBaseTileLayer(getBaseRasterTileService(platform, 'explore.day', features, language), engineType, minZoom, maxZoom),
118
125
  };
119
126
  }
120
127
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // @ts-ignore-next-line importsNotUsedAsValues
2
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
3
3
  import React from 'react';
4
4
  import classNames from 'classnames';
5
5
  import noop from 'lodash/fp/noop';
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // @ts-ignore-next-line importsNotUsedAsValues
2
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
3
3
  import React from 'react';
4
4
  import { toast, Slide } from 'react-toastify';
5
5
  export const DEFAULT_NOTIFICATION_TIMEOUT_IN_MS = 5_000;
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- // @ts-ignore-next-line importsNotUsedAsValues
2
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
3
3
  import React, { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import ReactDOM from 'react-dom';
5
5
  import { usePopper } from 'react-popper';
@@ -32,7 +32,7 @@ const OverlayTrigger = (props) => {
32
32
  const handleShow = () => {
33
33
  timeout.clear();
34
34
  hoverStateRef.current = 'show';
35
- if (typeof delay === 'number' || !delay?.show) {
35
+ if (delay === 0 || (typeof delay !== 'number' && !delay?.show)) {
36
36
  setShow(true);
37
37
  return;
38
38
  }
@@ -53,7 +53,7 @@ const OverlayTrigger = (props) => {
53
53
  if (hoverStateRef.current === 'hide') {
54
54
  setShow(false);
55
55
  }
56
- }, typeof delay === 'number' ? delay : delay?.hide);
56
+ }, delay?.hide);
57
57
  };
58
58
  const handleFocus = useCallback((...args) => {
59
59
  handleShow();
@@ -0,0 +1,83 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import type { Moment } from 'moment';
3
+ import { type DatePickerProps } from '../datepicker/DatePicker';
4
+ export type SaveableDateInputProps = {
5
+ /**
6
+ * The input placeholder.
7
+ */
8
+ placeholder?: string;
9
+ /**
10
+ * The actual input value.
11
+ */
12
+ value?: Date | Moment | string;
13
+ /**
14
+ * Used to control the save button from the outside to disable it in case
15
+ * the entered value is not valid.
16
+ *
17
+ * @default true
18
+ */
19
+ isValid?: boolean;
20
+ /**
21
+ * Callback function triggered when the value changes and is saved.
22
+ * @param value
23
+ * @param previousValue
24
+ * @returns
25
+ */
26
+ onValueChanged?: (value: Moment | string, previousValue: Moment | string | Date) => void;
27
+ /**
28
+ * Callback function that gets triggered on every input change. Use this to control the component
29
+ * or when implementing key validation.
30
+ * @param keyValue the key value that has been entered
31
+ * @returns
32
+ */
33
+ onInputChange?: (value: Moment | string, isValid: boolean) => void;
34
+ /**
35
+ * Callback function that gets triggered when the input is in edit mode. Use this
36
+ * to control the component and to handle the previous value on the outside.
37
+ * @returns
38
+ */
39
+ onEnterEdit?: () => void;
40
+ /**
41
+ * Callback function that gets triggered when edit mode is exited (either saved or cancelled).
42
+ * @param wasSaved - true if value was saved, false if cancelled/reset
43
+ * @returns
44
+ */
45
+ onExitEdit?: (wasSaved: boolean) => void;
46
+ /**
47
+ * Callback function that gets triggered when the user aborts the edit mode. Use this
48
+ * to control the component and handle the resetting of previous value on the outside.
49
+ * @returns
50
+ */
51
+ onCancel?: () => void;
52
+ /**
53
+ * Behavior when trying to exit edit mode with invalid input:
54
+ * - 'stay-open': Keep edit mode open until valid input is provided
55
+ * - 'reset-and-close': Close edit mode and reset to initial value
56
+ *
57
+ * @default 'stay-open'
58
+ */
59
+ invalidExitBehavior?: 'stay-open' | 'reset-and-close';
60
+ /**
61
+ * Defines the button style: `default` or `primary`.
62
+ */
63
+ buttonStyle?: 'primary' | 'default';
64
+ /**
65
+ * Additional HTML attributes to be set on the input element.
66
+ */
67
+ inputProps?: HTMLAttributes<HTMLInputElement>;
68
+ datePickerProps?: DatePickerProps;
69
+ /**
70
+ * Disables the component so the user cannot enter the edit mode.
71
+ */
72
+ disabled?: boolean;
73
+ /**
74
+ * Additional classes to be set on the input itself.
75
+ */
76
+ inputClassName?: string;
77
+ /**
78
+ * Additional classes to be set on the wrapper element.
79
+ */
80
+ className?: string;
81
+ };
82
+ declare const SaveableDateInput: (props: SaveableDateInputProps) => import("react/jsx-runtime").JSX.Element;
83
+ export default SaveableDateInput;
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import classNames from 'classnames';
4
+ import noop from 'lodash/fp/noop';
5
+ import Button from '../../Button';
6
+ import DatePicker from '../datepicker/DatePicker';
7
+ import useIsFocusWithin from '../../hooks/useIsFocusWithin';
8
+ import useEsc from '../../hooks/useEsc';
9
+ const DEFAULT_BUTTON_STYLE = 'primary';
10
+ // Validate date outside the component (via form library) if controlled usage
11
+ // Use as controlled component: value, is Valid, and change callback
12
+ // - if is valid, on save, call callback "onValueChanged"
13
+ // - if not valid, keep edit mode open, outside is showing error message
14
+ // - close edit mode only if date is valid - or close edit mode and reset to initial value -> customizable via prop
15
+ // Keep picker open until user has clicked save, otherwise he might forget to click save if the dropdown closes automatically
16
+ const SaveableDateInput = (props) => {
17
+ const { placeholder, value: externalValue = '', isValid = true, onValueChanged = noop, onInputChange, // for controlled usage
18
+ onEnterEdit = noop, onExitEdit = noop, onCancel = noop, buttonStyle = DEFAULT_BUTTON_STYLE, inputClassName, inputProps, invalidExitBehavior = 'stay-open', datePickerProps = {}, disabled = false, className, ...remainingProps } = props;
19
+ // if callback is provided, assume it is controlled case
20
+ const isControlledCase = onInputChange !== undefined;
21
+ const [inputValue, setInputValue] = useState(externalValue);
22
+ const [editInput, setEditInput] = useState(false);
23
+ const [isFocused, setIsFocused] = useState(false);
24
+ const [isPickerOpen, setIsPickerOpen] = useState(false);
25
+ const wrapperRef = useRef(null);
26
+ const buttonRef = useRef(null);
27
+ const initialInputValueRef = useRef(inputValue);
28
+ useIsFocusWithin({
29
+ ref: wrapperRef,
30
+ onFocusWithin: () => {
31
+ setIsFocused(true);
32
+ },
33
+ onBlurWithin: () => {
34
+ setIsFocused(false);
35
+ },
36
+ });
37
+ // Handle escape key to cancel edit mode
38
+ useEsc(() => {
39
+ if (isFocused && editInput) {
40
+ handleCancelEdit();
41
+ }
42
+ });
43
+ // Update internal state in a controlled environment
44
+ const [previousExternalValue, setPreviousExternalValue] = useState(externalValue);
45
+ if (previousExternalValue !== externalValue) {
46
+ setInputValue(externalValue);
47
+ setPreviousExternalValue(externalValue);
48
+ // Update initial value ref when external value changes while not in edit mode
49
+ if (!editInput) {
50
+ initialInputValueRef.current = externalValue;
51
+ }
52
+ }
53
+ const handleToggleInput = () => {
54
+ if (!editInput) {
55
+ // Enter edit mode
56
+ setEditInput(true);
57
+ initialInputValueRef.current = inputValue;
58
+ // open the dropdown
59
+ setIsPickerOpen(true);
60
+ onEnterEdit();
61
+ }
62
+ else {
63
+ // Try to exit edit mode
64
+ handleSaveAttempt();
65
+ }
66
+ };
67
+ const handleSaveAttempt = () => {
68
+ if (isValid) {
69
+ // Save the value (only if valid)
70
+ setEditInput(false);
71
+ onValueChanged(inputValue, initialInputValueRef.current);
72
+ onExitEdit(true);
73
+ // close the dropdown
74
+ setIsPickerOpen(false);
75
+ }
76
+ else {
77
+ // Handle invalid input based on behavior setting
78
+ if (invalidExitBehavior === 'reset-and-close') {
79
+ handleCancelEdit();
80
+ }
81
+ // If 'stay-open', do nothing - keep edit mode open
82
+ }
83
+ };
84
+ const handleCancelEdit = () => {
85
+ setEditInput(false);
86
+ // Reset to initial value
87
+ if (isControlledCase) {
88
+ // In controlled mode, trigger change to reset to initial value
89
+ onInputChange(initialInputValueRef.current, true);
90
+ }
91
+ else {
92
+ setInputValue(initialInputValueRef.current);
93
+ }
94
+ // Close the dropdown
95
+ setIsPickerOpen(false);
96
+ onCancel();
97
+ onExitEdit(false);
98
+ };
99
+ const handleDateChange = (value, isValid) => {
100
+ if (isControlledCase) {
101
+ onInputChange(value, isValid);
102
+ }
103
+ else {
104
+ setInputValue(value);
105
+ }
106
+ };
107
+ const handleCloseDropdown = () => setIsPickerOpen(false);
108
+ const wrapperClasses = classNames('form-group', className);
109
+ const buttonIconClasses = classNames('rioglyph', editInput ? 'rioglyph-ok' : 'rioglyph-pencil');
110
+ const dateInputClasses = classNames('margin-0 width-100pct', inputClassName);
111
+ // Button should be disabled if:
112
+ // - Component is disabled, OR
113
+ // - In edit mode and invalid input (and behavior is stay-open)
114
+ const disableButton = disabled || (editInput && !isValid && invalidExitBehavior === 'stay-open');
115
+ return (_jsx("div", { ref: wrapperRef, ...remainingProps, className: wrapperClasses, children: _jsxs("div", { className: 'input-group', children: [_jsx(DatePicker, { ...datePickerProps, open: isPickerOpen, className: dateInputClasses, inputProps: {
116
+ ...inputProps,
117
+ placeholder,
118
+ disabled: !editInput,
119
+ style: { borderTopRightRadius: 0, borderBottomRightRadius: 0 },
120
+ }, value: inputValue, onChange: handleDateChange, onClose: handleCloseDropdown }), _jsx("div", { className: 'input-group-btn', children: _jsx(Button, { ref: buttonRef, bsStyle: buttonStyle, iconOnly: true, onClick: handleToggleInput, disabled: disableButton, children: _jsx("span", { className: buttonIconClasses }) }) })] }) }));
121
+ };
122
+ export default SaveableDateInput;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- // @ts-ignore-next-line importsNotUsedAsValues
2
+ // biome-ignore lint/style/useImportType: required for JSX runtime compatibility with older toolchains
3
3
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
4
4
  import classNames from 'classnames';
5
5
  import isEmpty from 'lodash/fp/isEmpty';