@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.
- package/BarList.d.ts +2 -0
- package/BarList.js +2 -0
- package/README.md +4 -0
- package/SaveableDateInput.d.ts +2 -0
- package/SaveableDateInput.js +2 -0
- package/SvgImage.d.ts +2 -0
- package/SvgImage.js +2 -0
- package/TableCol.d.ts +2 -0
- package/TableCol.js +2 -0
- package/TableHead.d.ts +2 -0
- package/TableHead.js +2 -0
- package/components/actionBarItem/ActionBarItemIcon.js +1 -1
- package/components/actionBarItem/ActionBarOverlay.js +1 -1
- package/components/activity/Activity.d.ts +2 -2
- package/components/applicationHeader/CollapsedNavItem.js +1 -0
- package/components/assetTree/Tree.d.ts +20 -0
- package/components/assetTree/Tree.js +75 -38
- package/components/assetTree/TreeLeaf.js +1 -1
- package/components/assetTree/TreeNodeContainer.d.ts +1 -1
- package/components/assetTree/TreeSearch.js +1 -1
- package/components/assetTree/TreeSummary.js +1 -1
- package/components/assetTree/TypeCounter.d.ts +2 -0
- package/components/assetTree/TypeCounter.js +1 -1
- package/components/assetTree/useTreeExpansion.d.ts +4 -0
- package/components/assetTree/useTreeExpansion.js +25 -0
- package/components/assetTree/useTreeHeight.d.ts +1 -0
- package/components/assetTree/useTreeHeight.js +60 -0
- package/components/assetTree/useTreeScrollPosition.d.ts +3 -0
- package/components/assetTree/useTreeScrollPosition.js +19 -0
- package/components/assetTree/useTreeVirtualization.d.ts +17 -0
- package/components/assetTree/useTreeVirtualization.js +71 -0
- package/components/autosuggest/AutoSuggest.js +2 -1
- package/components/barList/BarList.d.ts +97 -0
- package/components/barList/BarList.js +42 -0
- package/components/barList/useSortedBars.d.ts +2 -0
- package/components/barList/useSortedBars.js +14 -0
- package/components/button/ButtonToolbar.d.ts +1 -1
- package/components/button/ButtonToolbar.js +1 -1
- package/components/button/ToggleButton.js +0 -1
- package/components/charts/PieChart.js +1 -1
- package/components/clearableInput/ClearableInput.d.ts +20 -7
- package/components/clearableInput/ClearableInput.js +68 -8
- package/components/dialog/Dialog.js +1 -1
- package/components/dialog/FrameDialog.js +1 -1
- package/components/dropdown/ButtonDropdown.d.ts +11 -3
- package/components/dropdown/ButtonDropdown.js +79 -64
- package/components/dropdown/DropdownToggleButton.d.ts +7 -4
- package/components/dropdown/DropdownToggleButton.js +11 -3
- package/components/formLabel/FormLabel.d.ts +2 -2
- package/components/formLabel/FormLabel.js +1 -1
- package/components/listMenu/ListMenu.js +4 -1
- package/components/map/components/Map.js +21 -6
- package/components/map/components/constants.d.ts +2 -0
- package/components/map/components/constants.js +3 -0
- package/components/map/components/features/basics/InfoBubble.js +1 -1
- package/components/map/components/features/layers/overlayLayers/IncidentsLayer.js +1 -1
- package/components/map/utils/mapTypes.d.ts +5 -0
- package/components/map/utils/rendering.d.ts +5 -2
- package/components/map/utils/rendering.js +46 -39
- package/components/menuItems/MenuItem.js +1 -1
- package/components/notification/Notification.js +1 -1
- package/components/overlay/OverlayTrigger.js +3 -3
- package/components/saveableInput/SaveableDateInput.d.ts +83 -0
- package/components/saveableInput/SaveableDateInput.js +122 -0
- package/components/selects/BaseSelectDropdown.js +1 -1
- package/components/selects/Multiselect.d.ts +8 -0
- package/components/selects/Multiselect.js +4 -4
- package/components/smoothScrollbars/SmoothScrollbars.d.ts +1 -0
- package/components/smoothScrollbars/SmoothScrollbars.js +3 -3
- package/components/statsWidget/StatsWidget.d.ts +2 -2
- package/components/statsWidget/StatsWidgets.d.ts +2 -2
- package/components/svgImage/SvgElement.d.ts +8 -0
- package/components/svgImage/SvgElement.js +11 -0
- package/components/svgImage/SvgImage.d.ts +30 -0
- package/components/svgImage/SvgImage.js +20 -0
- package/components/svgImage/svgConverter.d.ts +17 -0
- package/components/svgImage/svgConverter.js +78 -0
- package/components/svgImage/useSvgLoader.d.ts +9 -0
- package/components/svgImage/useSvgLoader.js +43 -0
- package/components/switch/Switch.d.ts +4 -0
- package/components/switch/Switch.js +5 -6
- package/components/table/TableCardsSorting.d.ts +0 -1
- package/components/table/TableCol.d.ts +18 -0
- package/components/table/TableCol.js +11 -0
- package/components/table/TableHead.d.ts +33 -0
- package/components/table/TableHead.js +11 -0
- package/components/table/TableSettingsDialog.js +1 -1
- package/components/tag/Tag.js +1 -1
- package/components/timepicker/TimePicker.d.ts +1 -2
- package/components/timepicker/TimePicker.js +35 -8
- package/components/tooltip/SimpleTooltip.d.ts +1 -1
- package/components/virtualList/VirtualList.js +1 -1
- package/hooks/useIsFocusWithin.d.ts +33 -0
- package/hooks/useIsFocusWithin.js +55 -0
- package/hooks/useLocationSuggestions.d.ts +27 -0
- package/hooks/useLocationSuggestions.js +94 -0
- package/hooks/useOnboarding.d.ts +17 -5
- package/hooks/useOnboarding.js +7 -1
- package/hooks/usePostMessage.js +0 -1
- package/hooks/useSearch.d.ts +63 -0
- package/hooks/useSearch.js +73 -0
- package/hooks/useSorting.d.ts +6 -0
- package/hooks/useSorting.js +7 -4
- package/hooks/useTableExport.d.ts +49 -0
- package/hooks/useTableExport.js +57 -0
- package/hooks/useTableSelection.d.ts +166 -0
- package/hooks/useTableSelection.js +201 -0
- package/lib/es/BarList.d.ts +2 -0
- package/lib/es/BarList.js +7 -0
- package/lib/es/SaveableDateInput.d.ts +2 -0
- package/lib/es/SaveableDateInput.js +7 -0
- package/lib/es/SvgImage.d.ts +2 -0
- package/lib/es/SvgImage.js +7 -0
- package/lib/es/TableCol.d.ts +2 -0
- package/lib/es/TableCol.js +7 -0
- package/lib/es/TableHead.d.ts +2 -0
- package/lib/es/TableHead.js +7 -0
- package/lib/es/components/actionBarItem/ActionBarItemIcon.js +1 -1
- package/lib/es/components/actionBarItem/ActionBarOverlay.js +1 -1
- package/lib/es/components/activity/Activity.d.ts +2 -2
- package/lib/es/components/applicationHeader/CollapsedNavItem.js +1 -0
- package/lib/es/components/assetTree/Tree.d.ts +20 -0
- package/lib/es/components/assetTree/Tree.js +74 -37
- package/lib/es/components/assetTree/TreeLeaf.js +1 -1
- package/lib/es/components/assetTree/TreeNodeContainer.d.ts +1 -1
- package/lib/es/components/assetTree/TreeSearch.js +1 -1
- package/lib/es/components/assetTree/TreeSummary.js +1 -1
- package/lib/es/components/assetTree/TypeCounter.d.ts +2 -0
- package/lib/es/components/assetTree/TypeCounter.js +1 -1
- package/lib/es/components/assetTree/useTreeExpansion.d.ts +4 -0
- package/lib/es/components/assetTree/useTreeExpansion.js +29 -0
- package/lib/es/components/assetTree/useTreeHeight.d.ts +1 -0
- package/lib/es/components/assetTree/useTreeHeight.js +64 -0
- package/lib/es/components/assetTree/useTreeScrollPosition.d.ts +3 -0
- package/lib/es/components/assetTree/useTreeScrollPosition.js +23 -0
- package/lib/es/components/assetTree/useTreeVirtualization.d.ts +17 -0
- package/lib/es/components/assetTree/useTreeVirtualization.js +76 -0
- package/lib/es/components/autosuggest/AutoSuggest.js +2 -1
- package/lib/es/components/barList/BarList.d.ts +97 -0
- package/lib/es/components/barList/BarList.js +45 -0
- package/lib/es/components/barList/useSortedBars.d.ts +2 -0
- package/lib/es/components/barList/useSortedBars.js +17 -0
- package/lib/es/components/button/ButtonToolbar.d.ts +1 -1
- package/lib/es/components/button/ButtonToolbar.js +1 -1
- package/lib/es/components/button/ToggleButton.js +0 -1
- package/lib/es/components/charts/PieChart.js +1 -1
- package/lib/es/components/clearableInput/ClearableInput.d.ts +20 -7
- package/lib/es/components/clearableInput/ClearableInput.js +67 -7
- package/lib/es/components/dialog/Dialog.js +1 -1
- package/lib/es/components/dialog/FrameDialog.js +1 -1
- package/lib/es/components/dropdown/ButtonDropdown.d.ts +11 -3
- package/lib/es/components/dropdown/ButtonDropdown.js +79 -64
- package/lib/es/components/dropdown/DropdownToggleButton.d.ts +7 -4
- package/lib/es/components/dropdown/DropdownToggleButton.js +11 -3
- package/lib/es/components/formLabel/FormLabel.d.ts +2 -2
- package/lib/es/components/formLabel/FormLabel.js +1 -1
- package/lib/es/components/listMenu/ListMenu.js +4 -1
- package/lib/es/components/map/components/Map.js +20 -5
- package/lib/es/components/map/components/constants.d.ts +2 -0
- package/lib/es/components/map/components/constants.js +4 -1
- package/lib/es/components/map/components/features/basics/InfoBubble.js +1 -1
- package/lib/es/components/map/components/features/layers/overlayLayers/IncidentsLayer.js +1 -1
- package/lib/es/components/map/utils/mapTypes.d.ts +5 -0
- package/lib/es/components/map/utils/rendering.d.ts +5 -2
- package/lib/es/components/map/utils/rendering.js +46 -39
- package/lib/es/components/menuItems/MenuItem.js +1 -1
- package/lib/es/components/notification/Notification.js +1 -1
- package/lib/es/components/overlay/OverlayTrigger.js +3 -3
- package/lib/es/components/saveableInput/SaveableDateInput.d.ts +83 -0
- package/lib/es/components/saveableInput/SaveableDateInput.js +125 -0
- package/lib/es/components/selects/BaseSelectDropdown.js +1 -1
- package/lib/es/components/selects/Multiselect.d.ts +8 -0
- package/lib/es/components/selects/Multiselect.js +4 -4
- package/lib/es/components/smoothScrollbars/SmoothScrollbars.d.ts +1 -0
- package/lib/es/components/smoothScrollbars/SmoothScrollbars.js +3 -3
- package/lib/es/components/statsWidget/StatsWidget.d.ts +2 -2
- package/lib/es/components/statsWidget/StatsWidgets.d.ts +2 -2
- package/lib/es/components/svgImage/SvgElement.d.ts +8 -0
- package/lib/es/components/svgImage/SvgElement.js +14 -0
- package/lib/es/components/svgImage/SvgImage.d.ts +30 -0
- package/lib/es/components/svgImage/SvgImage.js +23 -0
- package/lib/es/components/svgImage/svgConverter.d.ts +17 -0
- package/lib/es/components/svgImage/svgConverter.js +84 -0
- package/lib/es/components/svgImage/useSvgLoader.d.ts +9 -0
- package/lib/es/components/svgImage/useSvgLoader.js +48 -0
- package/lib/es/components/switch/Switch.d.ts +4 -0
- package/lib/es/components/switch/Switch.js +5 -6
- package/lib/es/components/table/TableCardsSorting.d.ts +0 -1
- package/lib/es/components/table/TableCol.d.ts +18 -0
- package/lib/es/components/table/TableCol.js +13 -0
- package/lib/es/components/table/TableHead.d.ts +33 -0
- package/lib/es/components/table/TableHead.js +14 -0
- package/lib/es/components/table/TableSettingsDialog.js +1 -1
- package/lib/es/components/tag/Tag.js +1 -1
- package/lib/es/components/timepicker/TimePicker.d.ts +1 -2
- package/lib/es/components/timepicker/TimePicker.js +35 -8
- package/lib/es/components/tooltip/SimpleTooltip.d.ts +1 -1
- package/lib/es/components/virtualList/VirtualList.js +1 -1
- package/lib/es/hooks/useIsFocusWithin.d.ts +33 -0
- package/lib/es/hooks/useIsFocusWithin.js +57 -0
- package/lib/es/hooks/useLocationSuggestions.d.ts +27 -0
- package/lib/es/hooks/useLocationSuggestions.js +97 -0
- package/lib/es/hooks/useOnboarding.d.ts +17 -5
- package/lib/es/hooks/useOnboarding.js +7 -1
- package/lib/es/hooks/usePostMessage.js +0 -1
- package/lib/es/hooks/useSearch.d.ts +63 -0
- package/lib/es/hooks/useSearch.js +75 -0
- package/lib/es/hooks/useSorting.d.ts +6 -0
- package/lib/es/hooks/useSorting.js +7 -4
- package/lib/es/hooks/useTableExport.d.ts +49 -0
- package/lib/es/hooks/useTableExport.js +59 -0
- package/lib/es/hooks/useTableSelection.d.ts +166 -0
- package/lib/es/hooks/useTableSelection.js +210 -0
- package/lib/es/themes/Volkswagen/components/applicationHeader/VolkswagenApplicationHeader.js +1 -1
- package/lib/es/useIsFocusWithin.d.ts +2 -0
- package/lib/es/useIsFocusWithin.js +7 -0
- package/lib/es/useLocationSuggestions.d.ts +2 -0
- package/lib/es/useLocationSuggestions.js +7 -0
- package/lib/es/useSearch.d.ts +2 -0
- package/lib/es/useSearch.js +7 -0
- package/lib/es/useTableExport.d.ts +2 -0
- package/lib/es/useTableExport.js +7 -0
- package/lib/es/useTableSelection.d.ts +2 -0
- package/lib/es/useTableSelection.js +7 -0
- package/lib/es/utils/storageUtils.d.ts +2 -2
- package/lib/es/utils/storageUtils.js +2 -0
- package/lib/es/utils/urlFeatureToggles.d.ts +12 -6
- package/lib/es/utils/urlFeatureToggles.js +14 -8
- package/lib/es/utils/useDropDirection.js +1 -0
- package/lib/es/version.json +1 -1
- package/package.json +48 -40
- package/themes/Volkswagen/components/applicationHeader/VolkswagenApplicationHeader.js +1 -1
- package/useIsFocusWithin.d.ts +2 -0
- package/useIsFocusWithin.js +2 -0
- package/useLocationSuggestions.d.ts +2 -0
- package/useLocationSuggestions.js +2 -0
- package/useSearch.d.ts +2 -0
- package/useSearch.js +2 -0
- package/useTableExport.d.ts +2 -0
- package/useTableExport.js +2 -0
- package/useTableSelection.d.ts +2 -0
- package/useTableSelection.js +2 -0
- package/utils/storageUtils.d.ts +2 -2
- package/utils/storageUtils.js +2 -0
- package/utils/urlFeatureToggles.d.ts +12 -6
- package/utils/urlFeatureToggles.js +10 -7
- package/utils/useDropDirection.js +1 -0
- 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
|
|
10
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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:'
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
},
|
|
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
|
-
//
|
|
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';
|