@carbon/react 1.90.0-rc.0 → 1.91.0-rc.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 (320) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +923 -964
  2. package/es/components/AILabel/index.js +8 -6
  3. package/es/components/Breadcrumb/BreadcrumbItem.js +2 -0
  4. package/es/components/Button/Button.js +3 -0
  5. package/es/components/ChatButton/ChatButton.js +1 -0
  6. package/es/components/CheckboxGroup/CheckboxGroup.js +21 -2
  7. package/es/components/CodeSnippet/CodeSnippet.d.ts +1 -1
  8. package/es/components/CodeSnippet/CodeSnippet.js +1 -1
  9. package/es/components/ComboBox/ComboBox.js +18 -13
  10. package/es/components/ComboButton/index.js +2 -1
  11. package/es/components/ComposedModal/ComposedModal.js +5 -2
  12. package/es/components/Copy/Copy.d.ts +1 -1
  13. package/es/components/Copy/Copy.js +1 -1
  14. package/es/components/CopyButton/CopyButton.d.ts +1 -1
  15. package/es/components/CopyButton/CopyButton.js +1 -1
  16. package/es/components/DataTable/DataTable.d.ts +60 -15
  17. package/es/components/DataTable/DataTable.js +119 -178
  18. package/es/components/DataTable/Table.d.ts +2 -2
  19. package/es/components/DataTable/Table.js +4 -4
  20. package/es/components/DataTable/TableExpandHeader.d.ts +1 -1
  21. package/es/components/DataTable/TableExpandHeader.js +6 -2
  22. package/es/components/DataTable/TableExpandRow.js +1 -0
  23. package/es/components/DataTable/TableHeader.js +2 -0
  24. package/es/components/DataTable/TableRow.js +5 -0
  25. package/es/components/DataTable/TableSlugRow.js +1 -0
  26. package/es/components/DataTable/TableToolbarMenu.js +3 -0
  27. package/es/components/DataTable/state/sorting.d.ts +3 -1
  28. package/es/components/DataTable/state/sorting.js +2 -0
  29. package/es/components/DataTable/tools/sorting.js +1 -0
  30. package/es/components/DatePicker/DatePicker.d.ts +0 -12
  31. package/es/components/DatePicker/DatePicker.js +17 -6
  32. package/es/components/DatePicker/plugins/rangePlugin.d.ts +19 -2
  33. package/es/components/DatePicker/plugins/rangePlugin.js +18 -14
  34. package/es/components/DatePickerInput/DatePickerInput.js +6 -0
  35. package/es/components/Dialog/Dialog.js +10 -2
  36. package/es/components/Dropdown/Dropdown.js +17 -16
  37. package/es/components/ErrorBoundary/ErrorBoundaryContext.js +1 -0
  38. package/es/components/FeatureFlags/index.js +1 -0
  39. package/es/components/FileUploader/FileUploader.js +9 -2
  40. package/es/components/FileUploader/FileUploaderButton.js +1 -1
  41. package/es/components/FileUploader/FileUploaderDropContainer.js +1 -1
  42. package/es/components/FileUploader/FileUploaderItem.js +3 -0
  43. package/es/components/FluidMultiSelect/FluidMultiSelect.js +2 -0
  44. package/es/components/FluidNumberInput/FluidNumberInput.js +3 -2
  45. package/es/components/FluidSearch/FluidSearch.js +3 -2
  46. package/es/components/FluidSelect/FluidSelect.js +3 -2
  47. package/es/components/FluidTextInput/FluidTextInput.js +3 -2
  48. package/es/components/FluidTimePicker/FluidTimePicker.js +11 -4
  49. package/es/components/FluidTimePickerSelect/FluidTimePickerSelect.js +3 -2
  50. package/es/components/Grid/CSSGrid.js +5 -0
  51. package/es/components/Grid/Column.js +3 -0
  52. package/es/components/Grid/ColumnHang.js +1 -0
  53. package/es/components/Grid/FlexGrid.js +1 -0
  54. package/es/components/Grid/Row.js +1 -0
  55. package/es/components/Heading/index.js +3 -1
  56. package/es/components/IconButton/index.js +5 -3
  57. package/es/components/IconIndicator/index.js +4 -2
  58. package/es/components/InlineCheckbox/InlineCheckbox.js +3 -2
  59. package/es/components/Layout/index.js +6 -4
  60. package/es/components/LayoutDirection/LayoutDirection.js +2 -0
  61. package/es/components/Link/Link.js +3 -0
  62. package/es/components/ListBox/ListBoxMenuItem.js +4 -1
  63. package/es/components/ListBox/next/ListBoxTrigger.js +3 -2
  64. package/es/components/Menu/Menu.js +6 -9
  65. package/es/components/Menu/MenuContext.js +1 -0
  66. package/es/components/Menu/MenuItem.d.ts +1 -1
  67. package/es/components/Menu/MenuItem.js +10 -18
  68. package/es/components/MenuButton/index.js +7 -2
  69. package/es/components/Modal/Modal.js +4 -1
  70. package/es/components/ModalWrapper/ModalWrapper.js +2 -0
  71. package/es/components/MultiSelect/FilterableMultiSelect.js +25 -6
  72. package/es/components/MultiSelect/MultiSelect.js +22 -17
  73. package/es/components/Notification/Notification.d.ts +6 -6
  74. package/es/components/Notification/Notification.js +7 -7
  75. package/es/components/NumberInput/NumberInput.js +7 -1
  76. package/es/components/OverflowMenu/OverflowMenu.js +3 -1
  77. package/es/components/OverflowMenu/next/index.js +4 -3
  78. package/es/components/OverflowMenuItem/OverflowMenuItem.js +2 -0
  79. package/es/components/PageHeader/PageHeader.d.ts +3 -3
  80. package/es/components/PageHeader/PageHeader.js +22 -8
  81. package/es/components/Pagination/Pagination.js +6 -2
  82. package/es/components/PaginationNav/PaginationNav.js +2 -5
  83. package/es/components/Popover/index.js +15 -5
  84. package/es/components/ProgressBar/ProgressBar.js +4 -2
  85. package/es/components/RadioTile/RadioTile.js +2 -2
  86. package/es/components/Search/Search.d.ts +4 -2
  87. package/es/components/Search/Search.js +7 -6
  88. package/es/components/Select/Select.js +2 -3
  89. package/es/components/ShapeIndicator/index.js +4 -2
  90. package/es/components/SkeletonPlaceholder/SkeletonPlaceholder.d.ts +4 -4
  91. package/es/components/SkeletonPlaceholder/SkeletonPlaceholder.js +1 -2
  92. package/es/components/SkeletonText/SkeletonText.js +0 -2
  93. package/es/components/Slider/Slider.d.ts +144 -188
  94. package/es/components/Slider/Slider.js +798 -726
  95. package/es/components/Slider/index.d.ts +2 -2
  96. package/es/components/Stack/HStack.js +1 -0
  97. package/es/components/Stack/Stack.js +6 -9
  98. package/es/components/Stack/VStack.js +1 -0
  99. package/es/components/StructuredList/StructuredList.js +3 -0
  100. package/es/components/Switch/Switch.js +1 -1
  101. package/es/components/Tabs/Tabs.d.ts +4 -0
  102. package/es/components/Tabs/Tabs.js +28 -17
  103. package/es/components/Tag/DismissibleTag.js +2 -0
  104. package/es/components/Tag/OperationalTag.js +2 -0
  105. package/es/components/Tag/SelectableTag.js +2 -0
  106. package/es/components/Tag/Tag.js +4 -0
  107. package/es/components/Tag/isEllipsisActive.js +1 -0
  108. package/es/components/Text/Text.js +1 -0
  109. package/es/components/TextArea/TextArea.js +13 -6
  110. package/es/components/TextInput/ControlledPasswordInput.js +4 -6
  111. package/es/components/TextInput/PasswordInput.js +9 -4
  112. package/es/components/TextInput/TextInput.js +6 -4
  113. package/es/components/TextInput/util.d.ts +17 -5
  114. package/es/components/TextInput/util.js +2 -7
  115. package/es/components/Theme/index.js +8 -3
  116. package/es/components/Tile/Tile.js +14 -17
  117. package/es/components/TimePicker/TimePicker.js +1 -0
  118. package/es/components/Toggle/Toggle.js +1 -1
  119. package/es/components/Toggletip/index.js +4 -0
  120. package/es/components/Tooltip/DefinitionTooltip.js +1 -0
  121. package/es/components/Tooltip/Tooltip.js +30 -23
  122. package/es/components/TreeView/TreeNode.js +13 -5
  123. package/es/components/TreeView/TreeView.js +7 -0
  124. package/es/components/UIShell/HeaderContainer.js +3 -1
  125. package/es/components/UIShell/HeaderGlobalAction.js +2 -2
  126. package/es/components/UIShell/HeaderMenu.js +3 -3
  127. package/es/components/UIShell/HeaderPanel.d.ts +1 -1
  128. package/es/components/UIShell/HeaderPanel.js +2 -2
  129. package/es/components/UIShell/Link.js +2 -0
  130. package/es/components/UIShell/SideNavItems.js +1 -0
  131. package/es/components/UIShell/SideNavMenu.js +2 -2
  132. package/es/components/UIShell/SideNavMenuItem.js +1 -1
  133. package/es/components/UIShell/SideNavSwitcher.js +1 -1
  134. package/es/components/UIShell/Switcher.js +2 -1
  135. package/es/components/UIShell/SwitcherItem.js +4 -2
  136. package/es/index.d.ts +12 -13
  137. package/es/index.js +25 -24
  138. package/es/internal/FloatingMenu.js +7 -1
  139. package/es/internal/Selection.js +12 -3
  140. package/es/internal/defaultItemToString.d.ts +7 -0
  141. package/es/internal/defaultItemToString.js +17 -0
  142. package/es/internal/index.d.ts +1 -0
  143. package/es/internal/useMergedRefs.js +1 -0
  144. package/es/internal/useNoInteractiveChildren.js +4 -0
  145. package/es/internal/useOutsideClick.js +1 -0
  146. package/es/internal/useOverflowItems.js +6 -0
  147. package/es/internal/useResizeObserver.js +4 -0
  148. package/es/internal/useSavedCallback.js +1 -0
  149. package/es/internal/warning.js +1 -0
  150. package/es/prop-types/deprecateValuesWithin.d.ts +8 -1
  151. package/es/prop-types/deprecateValuesWithin.js +6 -6
  152. package/es/prop-types/isRequiredOneOf.js +4 -1
  153. package/es/prop-types/requiredIfGivenPropIsTruthy.d.ts +8 -7
  154. package/es/prop-types/requiredIfGivenPropIsTruthy.js +10 -10
  155. package/es/tools/events.js +3 -1
  156. package/es/tools/wrapComponent.js +1 -0
  157. package/lib/components/AILabel/index.js +8 -6
  158. package/lib/components/Breadcrumb/BreadcrumbItem.js +2 -0
  159. package/lib/components/Button/Button.js +3 -0
  160. package/lib/components/ChatButton/ChatButton.js +1 -0
  161. package/lib/components/CheckboxGroup/CheckboxGroup.js +20 -1
  162. package/lib/components/CodeSnippet/CodeSnippet.d.ts +1 -1
  163. package/lib/components/CodeSnippet/CodeSnippet.js +1 -1
  164. package/lib/components/ComboBox/ComboBox.js +20 -15
  165. package/lib/components/ComboButton/index.js +2 -1
  166. package/lib/components/ComposedModal/ComposedModal.js +5 -2
  167. package/lib/components/Copy/Copy.d.ts +1 -1
  168. package/lib/components/Copy/Copy.js +1 -1
  169. package/lib/components/CopyButton/CopyButton.d.ts +1 -1
  170. package/lib/components/CopyButton/CopyButton.js +1 -1
  171. package/lib/components/DataTable/DataTable.d.ts +60 -15
  172. package/lib/components/DataTable/DataTable.js +119 -178
  173. package/lib/components/DataTable/Table.d.ts +2 -2
  174. package/lib/components/DataTable/Table.js +4 -4
  175. package/lib/components/DataTable/TableExpandHeader.d.ts +1 -1
  176. package/lib/components/DataTable/TableExpandHeader.js +6 -2
  177. package/lib/components/DataTable/TableExpandRow.js +1 -0
  178. package/lib/components/DataTable/TableHeader.js +2 -0
  179. package/lib/components/DataTable/TableRow.js +5 -0
  180. package/lib/components/DataTable/TableSlugRow.js +1 -0
  181. package/lib/components/DataTable/TableToolbarMenu.js +3 -0
  182. package/lib/components/DataTable/state/sorting.d.ts +3 -1
  183. package/lib/components/DataTable/state/sorting.js +2 -0
  184. package/lib/components/DataTable/tools/sorting.js +1 -0
  185. package/lib/components/DatePicker/DatePicker.d.ts +0 -12
  186. package/lib/components/DatePicker/DatePicker.js +16 -5
  187. package/lib/components/DatePicker/plugins/rangePlugin.d.ts +19 -2
  188. package/lib/components/DatePicker/plugins/rangePlugin.js +18 -16
  189. package/lib/components/DatePickerInput/DatePickerInput.js +6 -0
  190. package/lib/components/Dialog/Dialog.js +10 -2
  191. package/lib/components/Dropdown/Dropdown.js +19 -18
  192. package/lib/components/ErrorBoundary/ErrorBoundaryContext.js +1 -0
  193. package/lib/components/FeatureFlags/index.js +1 -0
  194. package/lib/components/FileUploader/FileUploader.js +9 -2
  195. package/lib/components/FileUploader/FileUploaderButton.js +1 -1
  196. package/lib/components/FileUploader/FileUploaderDropContainer.js +1 -1
  197. package/lib/components/FileUploader/FileUploaderItem.js +3 -0
  198. package/lib/components/FluidMultiSelect/FluidMultiSelect.js +2 -0
  199. package/lib/components/FluidNumberInput/FluidNumberInput.js +3 -2
  200. package/lib/components/FluidSearch/FluidSearch.js +3 -2
  201. package/lib/components/FluidSelect/FluidSelect.js +3 -2
  202. package/lib/components/FluidTextInput/FluidTextInput.js +3 -2
  203. package/lib/components/FluidTimePicker/FluidTimePicker.js +11 -4
  204. package/lib/components/FluidTimePickerSelect/FluidTimePickerSelect.js +3 -2
  205. package/lib/components/Grid/CSSGrid.js +5 -0
  206. package/lib/components/Grid/Column.js +3 -0
  207. package/lib/components/Grid/ColumnHang.js +1 -0
  208. package/lib/components/Grid/FlexGrid.js +1 -0
  209. package/lib/components/Grid/Row.js +1 -0
  210. package/lib/components/Heading/index.js +3 -1
  211. package/lib/components/IconButton/index.js +5 -3
  212. package/lib/components/IconIndicator/index.js +4 -2
  213. package/lib/components/InlineCheckbox/InlineCheckbox.js +3 -2
  214. package/lib/components/Layout/index.js +6 -4
  215. package/lib/components/LayoutDirection/LayoutDirection.js +2 -0
  216. package/lib/components/Link/Link.js +3 -0
  217. package/lib/components/ListBox/ListBoxMenuItem.js +4 -1
  218. package/lib/components/ListBox/next/ListBoxTrigger.js +3 -2
  219. package/lib/components/Menu/Menu.js +6 -9
  220. package/lib/components/Menu/MenuContext.js +1 -0
  221. package/lib/components/Menu/MenuItem.d.ts +1 -1
  222. package/lib/components/Menu/MenuItem.js +11 -19
  223. package/lib/components/MenuButton/index.js +7 -2
  224. package/lib/components/Modal/Modal.js +4 -1
  225. package/lib/components/ModalWrapper/ModalWrapper.js +2 -0
  226. package/lib/components/MultiSelect/FilterableMultiSelect.js +32 -13
  227. package/lib/components/MultiSelect/MultiSelect.js +23 -18
  228. package/lib/components/Notification/Notification.d.ts +6 -6
  229. package/lib/components/Notification/Notification.js +7 -7
  230. package/lib/components/NumberInput/NumberInput.js +7 -1
  231. package/lib/components/OverflowMenu/OverflowMenu.js +3 -1
  232. package/lib/components/OverflowMenu/next/index.js +4 -3
  233. package/lib/components/OverflowMenuItem/OverflowMenuItem.js +2 -0
  234. package/lib/components/PageHeader/PageHeader.d.ts +3 -3
  235. package/lib/components/PageHeader/PageHeader.js +22 -8
  236. package/lib/components/Pagination/Pagination.js +6 -2
  237. package/lib/components/PaginationNav/PaginationNav.js +2 -5
  238. package/lib/components/Popover/index.js +15 -5
  239. package/lib/components/ProgressBar/ProgressBar.js +4 -2
  240. package/lib/components/RadioTile/RadioTile.js +2 -2
  241. package/lib/components/Search/Search.d.ts +4 -2
  242. package/lib/components/Search/Search.js +7 -6
  243. package/lib/components/Select/Select.js +2 -3
  244. package/lib/components/ShapeIndicator/index.js +4 -2
  245. package/lib/components/SkeletonPlaceholder/SkeletonPlaceholder.d.ts +4 -4
  246. package/lib/components/SkeletonPlaceholder/SkeletonPlaceholder.js +1 -2
  247. package/lib/components/SkeletonText/SkeletonText.js +0 -2
  248. package/lib/components/Slider/Slider.d.ts +144 -188
  249. package/lib/components/Slider/Slider.js +795 -725
  250. package/lib/components/Slider/index.d.ts +2 -2
  251. package/lib/components/Stack/HStack.js +1 -0
  252. package/lib/components/Stack/Stack.js +6 -9
  253. package/lib/components/Stack/VStack.js +1 -0
  254. package/lib/components/StructuredList/StructuredList.js +3 -0
  255. package/lib/components/Switch/Switch.js +1 -1
  256. package/lib/components/Tabs/Tabs.d.ts +4 -0
  257. package/lib/components/Tabs/Tabs.js +28 -17
  258. package/lib/components/Tag/DismissibleTag.js +2 -0
  259. package/lib/components/Tag/OperationalTag.js +2 -0
  260. package/lib/components/Tag/SelectableTag.js +2 -0
  261. package/lib/components/Tag/Tag.js +4 -0
  262. package/lib/components/Tag/isEllipsisActive.js +1 -0
  263. package/lib/components/Text/Text.js +1 -0
  264. package/lib/components/TextArea/TextArea.js +13 -6
  265. package/lib/components/TextInput/ControlledPasswordInput.js +3 -5
  266. package/lib/components/TextInput/PasswordInput.js +8 -3
  267. package/lib/components/TextInput/TextInput.js +5 -3
  268. package/lib/components/TextInput/util.d.ts +17 -5
  269. package/lib/components/TextInput/util.js +2 -7
  270. package/lib/components/Theme/index.js +8 -3
  271. package/lib/components/Tile/Tile.js +14 -17
  272. package/lib/components/TimePicker/TimePicker.js +1 -0
  273. package/lib/components/Toggle/Toggle.js +1 -1
  274. package/lib/components/Toggletip/index.js +4 -0
  275. package/lib/components/Tooltip/DefinitionTooltip.js +1 -0
  276. package/lib/components/Tooltip/Tooltip.js +30 -23
  277. package/lib/components/TreeView/TreeNode.js +13 -5
  278. package/lib/components/TreeView/TreeView.js +7 -0
  279. package/lib/components/UIShell/HeaderContainer.js +3 -1
  280. package/lib/components/UIShell/HeaderGlobalAction.js +2 -2
  281. package/lib/components/UIShell/HeaderMenu.js +3 -3
  282. package/lib/components/UIShell/HeaderPanel.d.ts +1 -1
  283. package/lib/components/UIShell/HeaderPanel.js +2 -2
  284. package/lib/components/UIShell/Link.js +2 -0
  285. package/lib/components/UIShell/SideNavItems.js +1 -0
  286. package/lib/components/UIShell/SideNavMenu.js +2 -2
  287. package/lib/components/UIShell/SideNavMenuItem.js +1 -1
  288. package/lib/components/UIShell/SideNavSwitcher.js +1 -1
  289. package/lib/components/UIShell/Switcher.js +2 -1
  290. package/lib/components/UIShell/SwitcherItem.js +4 -2
  291. package/lib/index.d.ts +12 -13
  292. package/lib/index.js +51 -28
  293. package/lib/internal/FloatingMenu.js +7 -1
  294. package/lib/internal/Selection.js +12 -3
  295. package/lib/internal/defaultItemToString.d.ts +7 -0
  296. package/lib/internal/defaultItemToString.js +19 -0
  297. package/lib/internal/index.d.ts +1 -0
  298. package/lib/internal/useMergedRefs.js +1 -0
  299. package/lib/internal/useNoInteractiveChildren.js +4 -0
  300. package/lib/internal/useOutsideClick.js +1 -0
  301. package/lib/internal/useOverflowItems.js +6 -0
  302. package/lib/internal/useResizeObserver.js +4 -0
  303. package/lib/internal/useSavedCallback.js +1 -0
  304. package/lib/internal/warning.js +1 -0
  305. package/lib/prop-types/deprecateValuesWithin.d.ts +8 -1
  306. package/lib/prop-types/deprecateValuesWithin.js +6 -8
  307. package/lib/prop-types/isRequiredOneOf.js +4 -1
  308. package/lib/prop-types/requiredIfGivenPropIsTruthy.d.ts +8 -7
  309. package/lib/prop-types/requiredIfGivenPropIsTruthy.js +10 -12
  310. package/lib/tools/events.js +3 -1
  311. package/lib/tools/wrapComponent.js +1 -0
  312. package/package.json +10 -9
  313. package/es/components/MultiSelect/tools/itemToString.d.ts +0 -1
  314. package/es/components/MultiSelect/tools/itemToString.js +0 -21
  315. package/es/components/Slider/index.js +0 -14
  316. package/es/internal/createClassWrapper.js +0 -23
  317. package/lib/components/MultiSelect/tools/itemToString.d.ts +0 -1
  318. package/lib/components/MultiSelect/tools/itemToString.js +0 -23
  319. package/lib/components/Slider/index.js +0 -20
  320. package/lib/internal/createClassWrapper.js +0 -25
@@ -5,15 +5,14 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import { defineProperty as _defineProperty, extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React, { PureComponent, createRef } from 'react';
8
+ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
+ import React, { useReducer, useRef, useEffect, useMemo } from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import cx from 'classnames';
12
- import { ArrowDown, ArrowLeft, ArrowUp, ArrowRight, Enter } from '../../internal/keyboard/keys.js';
12
+ import { Enter, ArrowDown, ArrowLeft, ArrowUp, ArrowRight } from '../../internal/keyboard/keys.js';
13
13
  import { matches } from '../../internal/keyboard/match.js';
14
14
  import { PrefixContext } from '../../internal/usePrefix.js';
15
15
  import { deprecate } from '../../prop-types/deprecate.js';
16
- import { FeatureFlagContext } from '../FeatureFlags/index.js';
17
16
  import { WarningFilled, WarningAltFilled } from '@carbon/icons-react';
18
17
  import '../Text/index.js';
19
18
  import '../Tooltip/DefinitionTooltip.js';
@@ -31,23 +30,15 @@ const ThumbWrapper = ({
31
30
  ...rest
32
31
  }) => {
33
32
  if (hasTooltip) {
34
- return (
35
- /*#__PURE__*/
36
- // eslint-disable-next-line react/forbid-component-props
37
- React.createElement(Tooltip, _extends({
38
- className: className,
39
- style: style
40
- }, rest), children)
41
- );
33
+ return /*#__PURE__*/React.createElement(Tooltip, _extends({
34
+ className: className,
35
+ style: style
36
+ }, rest), children);
42
37
  } else {
43
- return (
44
- /*#__PURE__*/
45
- // eslint-disable-next-line react/forbid-dom-props
46
- React.createElement("div", {
47
- className: className,
48
- style: style
49
- }, children)
50
- );
38
+ return /*#__PURE__*/React.createElement("div", {
39
+ className: className,
40
+ style: style
41
+ }, children);
51
42
  }
52
43
  };
53
44
  const translationIds = {
@@ -84,752 +75,834 @@ var HandlePosition = /*#__PURE__*/function (HandlePosition) {
84
75
  HandlePosition["LOWER"] = "lower";
85
76
  HandlePosition["UPPER"] = "upper";
86
77
  return HandlePosition;
87
- }(HandlePosition || {});
88
- class Slider extends PureComponent {
89
- constructor(props) {
90
- super(props);
91
- _defineProperty(this, "state", {
92
- value: this.props.value,
93
- valueUpper: this.props.unstable_valueUpper,
94
- left: 0,
95
- leftUpper: 0,
96
- needsOnRelease: false,
97
- isValid: true,
98
- isValidUpper: true,
99
- activeHandle: undefined,
100
- correctedValue: null,
101
- correctedPosition: null,
102
- isRtl: false
103
- });
104
- _defineProperty(this, "thumbRef", void 0);
105
- _defineProperty(this, "thumbRefUpper", void 0);
106
- _defineProperty(this, "filledTrackRef", void 0);
107
- _defineProperty(this, "element", null);
108
- _defineProperty(this, "inputId", '');
109
- _defineProperty(this, "track", void 0);
110
- _defineProperty(this, "handleDrag", event => {
111
- if (event instanceof globalThis.MouseEvent || event instanceof globalThis.TouchEvent) {
112
- this.onDrag(event);
113
- }
114
- });
115
- /**
116
- * Sets up "drag" event handlers and calls `this.onDrag` in case dragging
117
- * started on somewhere other than the thumb without a corresponding "move"
118
- * event.
119
- */
120
- _defineProperty(this, "onDragStart", evt => {
121
- // Do nothing if component is disabled
122
- if (this.props.disabled || this.props.readOnly) {
123
- return;
124
- }
125
-
126
- // We're going to force focus on one of the handles later on here, b/c we're
127
- // firing on a mousedown event, we need to call event.preventDefault() to
128
- // keep the focus from leaving the HTMLElement.
129
- // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#notes
130
- evt.preventDefault();
131
-
132
- // Add drag stop handlers
133
- DRAG_STOP_EVENT_TYPES.forEach(element => {
134
- this.element?.ownerDocument.addEventListener(element, this.onDragStop);
135
- });
136
-
137
- // Add drag handlers
138
- DRAG_EVENT_TYPES.forEach(element => {
139
- this.element?.ownerDocument.addEventListener(element, this.handleDrag);
140
- });
141
- const clientX = this.getClientXFromEvent(evt.nativeEvent);
142
- let activeHandle;
143
- if (this.hasTwoHandles()) {
144
- if (evt.target == this.thumbRef.current) {
145
- activeHandle = HandlePosition.LOWER;
146
- } else if (evt.target == this.thumbRefUpper.current) {
147
- activeHandle = HandlePosition.UPPER;
148
- } else if (clientX) {
149
- const distanceToLower = this.calcDistanceToHandle(HandlePosition.LOWER, clientX);
150
- const distanceToUpper = this.calcDistanceToHandle(HandlePosition.UPPER, clientX);
151
- if (distanceToLower <= distanceToUpper) {
152
- activeHandle = HandlePosition.LOWER;
153
- } else {
154
- activeHandle = HandlePosition.UPPER;
155
- }
156
- }
157
- }
78
+ }(HandlePosition || {}); // TODO: Delete this type and directory type the properties in the function.
79
+ const Slider = props => {
80
+ // TODO: Move destructured `props` from the IIFE to here.
158
81
 
159
- // Force focus to the appropriate handle.
160
- const focusOptions = {
161
- preventScroll: true
162
- };
163
- if (this.hasTwoHandles()) {
164
- if (this.thumbRef.current && activeHandle === HandlePosition.LOWER) {
165
- this.thumbRef.current.focus(focusOptions);
166
- } else if (this.thumbRefUpper.current && activeHandle === HandlePosition.UPPER) {
167
- this.thumbRefUpper.current.focus(focusOptions);
168
- }
169
- } else if (this.thumbRef.current) {
170
- this.thumbRef.current.focus(focusOptions);
171
- }
172
- this.setState({
173
- activeHandle
174
- });
82
+ const initialState = {
83
+ value: props.value,
84
+ valueUpper: props.unstable_valueUpper,
85
+ left: 0,
86
+ leftUpper: 0,
87
+ needsOnRelease: false,
88
+ isValid: true,
89
+ isValidUpper: true,
90
+ activeHandle: undefined,
91
+ correctedValue: null,
92
+ correctedPosition: null,
93
+ isRtl: false
94
+ };
175
95
 
176
- // Perform first recalculation since we probably didn't click exactly in the
177
- // middle of the thumb.
178
- this.onDrag(evt.nativeEvent, activeHandle);
179
- });
180
- /**
181
- * Removes "drag" and "drag stop" event handlers and calls sets the flag
182
- * indicating that the `onRelease` callback should be called.
183
- */
184
- _defineProperty(this, "onDragStop", () => {
185
- // Do nothing if component is disabled
186
- if (this.props.disabled || this.props.readOnly) {
187
- return;
188
- }
96
+ // TODO: Investigate using generics on the hook.
97
+ const [state, setState] = useReducer((prev, args) => ({
98
+ ...prev,
99
+ ...args
100
+ }), initialState);
189
101
 
190
- // Remove drag stop handlers
191
- DRAG_STOP_EVENT_TYPES.forEach(element => {
192
- this.element?.ownerDocument.removeEventListener(element, this.onDragStop);
193
- });
102
+ // TODO: Investigate getting rid of these references.
103
+ const stateRef = useRef(state);
104
+ useEffect(() => {
105
+ stateRef.current = state;
106
+ }, [state]);
107
+ const propsRef = useRef(props);
108
+ useEffect(() => {
109
+ propsRef.current = props;
110
+ }, [props]);
111
+ const thumbRef = useRef(null);
112
+ const thumbRefUpper = useRef(null);
113
+ const filledTrackRef = useRef(null);
114
+ const elementRef = useRef(null);
115
+ const trackRef = useRef(null);
116
+ const inputIdRef = useRef('');
194
117
 
195
- // Remove drag handlers
196
- DRAG_EVENT_TYPES.forEach(element => {
197
- this.element?.ownerDocument.removeEventListener(element, this.handleDrag);
198
- });
118
+ // TODO: Delete this function and set its return value as the value of
119
+ // `twoHandles`.
120
+ const hasTwoHandles = () => {
121
+ return typeof state.valueUpper !== 'undefined';
122
+ };
123
+ const twoHandles = hasTwoHandles();
199
124
 
200
- // Set needsOnRelease flag so event fires on next update.
201
- this.setState({
202
- needsOnRelease: true,
203
- isValid: true,
204
- isValidUpper: true
205
- });
206
- });
207
- /**
208
- * Handles a "drag" event by recalculating the value/thumb and setting state
209
- * accordingly.
210
- *
211
- * @param evt The event.
212
- * @param activeHandle The first drag event call, we may have an explicit
213
- * activeHandle value, which is to be used before state is used.
214
- */
215
- _defineProperty(this, "_onDrag", (evt, activeHandle) => {
216
- activeHandle = activeHandle ?? this.state.activeHandle;
217
- // Do nothing if component is disabled, or we have no event.
218
- if (this.props.disabled || this.props.readOnly || !evt) {
219
- return;
220
- }
221
- const clientX = this.getClientXFromEvent(evt);
222
- const {
223
- value,
224
- left
225
- } = this.calcValue({
226
- clientX,
227
- value: this.state.value
228
- });
229
- // If we're set to two handles, negotiate which drag handle is closest to
230
- // the users' interaction.
231
- if (this.hasTwoHandles() && activeHandle) {
232
- this.setValueLeftForHandle(activeHandle, {
233
- value: this.nearestStepValue(value),
125
+ /**
126
+ * Sets up initial slider position and value in response to component mount.
127
+ */
128
+ useEffect(() => {
129
+ if (elementRef.current) {
130
+ const isRtl = document?.dir === 'rtl';
131
+ if (hasTwoHandles()) {
132
+ const {
133
+ value,
234
134
  left
135
+ } = calcValue({
136
+ value: stateRef.current.value,
137
+ useRawValue: true
235
138
  });
236
- } else {
237
- this.setState({
238
- value: this.nearestStepValue(value),
139
+ const {
140
+ value: valueUpper,
141
+ left: leftUpper
142
+ } = calcValue({
143
+ value: stateRef.current.valueUpper,
144
+ useRawValue: true
145
+ });
146
+ setState({
147
+ isRtl,
148
+ value,
239
149
  left,
240
- isValid: true
150
+ valueUpper,
151
+ leftUpper
241
152
  });
242
- }
243
- this.setState({
244
- correctedValue: null,
245
- correctedPosition: null
246
- });
247
- });
248
- /**
249
- * Throttles calls to `this._onDrag` by limiting events to being processed at
250
- * most once every `EVENT_THROTTLE` milliseconds.
251
- */
252
- _defineProperty(this, "onDrag", throttle(this._onDrag, EVENT_THROTTLE, {
253
- leading: true,
254
- trailing: false
255
- }));
256
- /**
257
- * Handles a `keydown` event by recalculating the value/thumb and setting
258
- * state accordingly.
259
- */
260
- _defineProperty(this, "onKeyDown", evt => {
261
- // Do nothing if component is disabled, or we don't have a valid event
262
- if (this.props.disabled || this.props.readOnly) {
263
- return;
264
- }
265
- const {
266
- step = 1,
267
- stepMultiplier = 4
268
- } = this.props;
269
- let delta = 0;
270
- if (matches(evt, [ArrowDown, ArrowLeft])) {
271
- delta = -step;
272
- } else if (matches(evt, [ArrowUp, ArrowRight])) {
273
- delta = step;
274
153
  } else {
275
- // Ignore keys we don't want to handle
276
- return;
277
- }
278
-
279
- // If shift was held, account for the stepMultiplier
280
- if (evt.shiftKey) {
281
- delta *= stepMultiplier;
282
- }
283
- if (this.hasTwoHandles() && this.state.activeHandle) {
284
- const currentValue = this.state.activeHandle === HandlePosition.LOWER ? this.state.value : this.state.valueUpper;
285
154
  const {
286
155
  value,
287
156
  left
288
- } = this.calcValue({
289
- value: this.calcValueForDelta(currentValue ?? this.props.min, delta, this.props.step)
290
- });
291
- this.setValueLeftForHandle(this.state.activeHandle, {
292
- value: this.nearestStepValue(value),
293
- left
157
+ } = calcValue({
158
+ value: stateRef.current.value,
159
+ useRawValue: true
294
160
  });
295
- } else {
296
- const {
161
+ setState({
162
+ isRtl,
297
163
  value,
298
164
  left
299
- } = this.calcValue({
300
- // Ensures custom value from `<input>` won't cause skipping next stepping
301
- // point with right arrow key, e.g. Typing 51 in `<input>`, moving focus
302
- // onto the thumb and the hitting right arrow key should yield 52 instead
303
- // of 54.
304
- value: this.calcValueForDelta(this.state.value, delta, this.props.step)
305
- });
306
- this.setState({
307
- value: this.nearestStepValue(value),
308
- left,
309
- isValid: true
310
165
  });
311
166
  }
312
- this.setState({
313
- correctedValue: null,
314
- correctedPosition: null
315
- });
316
- });
317
- /**
318
- * Provides the two-way binding for the input field of the Slider. It also
319
- * Handles a change to the input field by recalculating the value/thumb and
320
- * setting state accordingly.
321
- */
322
- _defineProperty(this, "onChange", evt => {
323
- // Do nothing if component is disabled
324
- if (this.props.disabled || this.props.readOnly) {
325
- return;
326
- }
327
-
328
- // Do nothing if we have no valid event, target, or value
329
- if (!evt || !('target' in evt) || typeof evt.target.value !== 'string') {
330
- return;
331
- }
167
+ }
168
+ return () => {
169
+ DRAG_STOP_EVENT_TYPES.forEach(element => elementRef.current?.ownerDocument.removeEventListener(element, onDragStop));
170
+ DRAG_EVENT_TYPES.forEach(element => elementRef.current?.ownerDocument.removeEventListener(element, handleDrag));
171
+ };
172
+ // eslint-disable-next-line react-hooks/exhaustive-deps
173
+ }, []);
174
+ useEffect(() => {
175
+ // TODO: Uncomment this code and delete all of the `filledTrackRef.current`
176
+ // checks.
177
+ // const el = filledTrackRef.current;
178
+ //
179
+ // if (!el) return;
332
180
 
333
- // Avoid calling calcValue for invalid numbers, but still update the state.
334
- const activeHandle = evt.target.dataset.handlePosition ?? HandlePosition.LOWER;
335
- const targetValue = Number.parseFloat(evt.target.value);
336
- if (this.hasTwoHandles()) {
337
- if (isNaN(targetValue)) {
338
- this.setValueForHandle(activeHandle, evt.target.value);
339
- } else if (this.isValidValueForPosition({
340
- handle: activeHandle,
341
- value: targetValue,
342
- min: this.props.min,
343
- max: this.props.max
344
- })) {
345
- this.processNewInputValue(evt.target);
346
- } else {
347
- this.setValueForHandle(activeHandle, targetValue);
348
- }
349
- } else {
350
- if (isNaN(targetValue)) {
351
- this.setState({
352
- value: evt.target.value
353
- });
354
- } else if (this.isValidValue({
355
- value: targetValue,
356
- min: this.props.min,
357
- max: this.props.max
358
- })) {
359
- this.processNewInputValue(evt.target);
360
- } else {
361
- this.setState({
362
- value: targetValue
363
- });
364
- }
365
- }
366
- });
367
- /**
368
- * Checks for validity of input value after clicking out of the input. It also
369
- * Handles state change to isValid state.
370
- */
371
- _defineProperty(this, "onBlur", evt => {
372
- // Do nothing if we have no valid event, target, or value
373
- if (!evt || !('target' in evt) || typeof evt.target.value !== 'string') {
374
- return;
181
+ // Fire onChange event handler if present, if there's a usable value, and
182
+ // if the value is different from the last one
183
+ if (hasTwoHandles()) {
184
+ if (filledTrackRef.current) {
185
+ filledTrackRef.current.style.transform = state.isRtl ? `translate(${100 - state.leftUpper}%, -50%) scaleX(${(state.leftUpper - state.left) / 100})` : `translate(${state.left}%, -50%) scaleX(${(state.leftUpper - state.left) / 100})`;
375
186
  }
376
- const {
377
- value: targetValue
378
- } = evt.target;
379
- this.processNewInputValue(evt.target);
380
- this.props.onBlur?.({
381
- value: targetValue,
382
- handlePosition: evt.target.dataset.handlePosition
383
- });
384
- });
385
- _defineProperty(this, "onInputKeyDown", evt => {
386
- // Do nothing if component is disabled, or we don't have a valid event.
387
- if (this.props.disabled || this.props.readOnly || !(evt.target instanceof HTMLInputElement)) {
388
- return;
187
+ } else {
188
+ if (filledTrackRef.current) {
189
+ filledTrackRef.current.style.transform = state.isRtl ? `translate(100%, -50%) scaleX(-${state.left / 100})` : `translate(0%, -50%) scaleX(${state.left / 100})`;
389
190
  }
191
+ }
192
+ // TODO: Investigate whether the missing dependency should be added.
193
+ //
194
+ // eslint-disable-next-line react-hooks/exhaustive-deps
195
+ }, [state.left, state.leftUpper, state.isRtl]);
390
196
 
391
- // Do nothing if we have no valid event, target, or value.
392
- if (!evt || !('target' in evt) || typeof evt.target.value !== 'string') {
393
- return;
394
- }
395
- if (matches(evt, [Enter])) {
396
- this.processNewInputValue(evt.target);
397
- }
398
- });
399
- _defineProperty(this, "processNewInputValue", input => {
400
- this.setState({
401
- correctedValue: null,
402
- correctedPosition: null
197
+ // Fire onChange when value(s) change
198
+ const prevValsRef = useRef(null);
199
+ useEffect(() => {
200
+ const prev = prevValsRef.current;
201
+ if (prev && (prev.value !== state.value || prev.valueUpper !== state.valueUpper) && typeof props.onChange === 'function') {
202
+ props.onChange({
203
+ value: state.value,
204
+ valueUpper: state.valueUpper
403
205
  });
404
- const targetValue = Number.parseFloat(input.value);
405
- const validity = !isNaN(targetValue);
406
-
407
- // When there are two handles, we'll also have the data-handle-position
408
- // attribute to consider the other value before settling on the validity to
409
- // set.
410
- const handlePosition = input.dataset.handlePosition;
411
- if (handlePosition === HandlePosition.LOWER) {
412
- this.setState({
413
- isValid: validity
414
- });
415
- } else if (handlePosition === HandlePosition.UPPER) {
416
- this.setState({
417
- isValidUpper: validity
418
- });
419
- }
420
- this.setState({
421
- isValid: validity
206
+ }
207
+ prevValsRef.current = {
208
+ value: state.value,
209
+ valueUpper: state.valueUpper
210
+ };
211
+ // TODO: Investigate whether the missing dependency should be added.
212
+ //
213
+ // eslint-disable-next-line react-hooks/exhaustive-deps
214
+ }, [state.value, state.valueUpper, props.onChange]);
215
+ useEffect(() => {
216
+ // Fire onRelease event handler if present and if needed
217
+ if (state.needsOnRelease && typeof props.onRelease === 'function') {
218
+ props.onRelease({
219
+ value: state.value,
220
+ valueUpper: state.valueUpper
422
221
  });
423
- if (validity) {
424
- const adjustedValue = handlePosition ? this.getAdjustedValueForPosition({
425
- handle: handlePosition,
426
- value: targetValue,
427
- min: this.props.min,
428
- max: this.props.max
429
- }) : this.getAdjustedValue({
430
- value: targetValue,
431
- min: this.props.min,
432
- max: this.props.max
433
- });
434
- if (adjustedValue !== targetValue) {
435
- this.setState({
436
- correctedValue: targetValue.toString(),
437
- correctedPosition: handlePosition
438
- });
439
- } else {
440
- this.setState({
441
- correctedValue: null,
442
- correctedPosition: null
443
- });
444
- }
222
+ // Reset the flag
223
+ setState({
224
+ needsOnRelease: false
225
+ });
226
+ }
227
+ // TODO: Investigate whether the missing dependency should be added.
228
+ //
229
+ // eslint-disable-next-line react-hooks/exhaustive-deps
230
+ }, [state.needsOnRelease, state.value, state.valueUpper, props.onRelease]);
231
+ const prevSyncKeysRef = useRef(null);
232
+ useEffect(() => {
233
+ const prev = prevSyncKeysRef.current;
234
+ const next = [props.value, props.unstable_valueUpper, props.max, props.min];
235
+
236
+ // If value from props does not change, do nothing here.
237
+ // Otherwise, do prop -> state sync without "value capping".
238
+ if (!prev || prev[0] !== next[0] || prev[1] !== next[1] || prev[2] !== next[2] || prev[3] !== next[3]) {
239
+ setState(calcValue({
240
+ value: props.value,
241
+ useRawValue: true
242
+ }));
243
+ if (typeof props.unstable_valueUpper !== 'undefined') {
445
244
  const {
446
- value,
447
- left
448
- } = this.calcValue({
449
- value: adjustedValue,
245
+ value: valueUpper,
246
+ left: leftUpper
247
+ } = calcValue({
248
+ value: props.unstable_valueUpper,
450
249
  useRawValue: true
451
250
  });
452
- if (handlePosition) {
453
- this.setValueLeftForHandle(handlePosition, {
454
- value: this.nearestStepValue(value),
455
- left
456
- });
457
- } else {
458
- this.setState({
459
- value,
460
- left
461
- });
462
- }
251
+ setState({
252
+ valueUpper,
253
+ leftUpper
254
+ });
255
+ } else {
256
+ setState({
257
+ valueUpper: undefined,
258
+ leftUpper: undefined
259
+ });
463
260
  }
261
+ prevSyncKeysRef.current = next;
262
+ }
263
+ // TODO: Investigate whether the missing dependency should be added.
264
+ //
265
+ // eslint-disable-next-line react-hooks/exhaustive-deps
266
+ }, [props.value, props.unstable_valueUpper, props.max, props.min]);
267
+
268
+ /**
269
+ * Rounds a given value to the nearest step defined by the slider's `step`
270
+ * prop.
271
+ *
272
+ * @param value - The value to adjust to the nearest step. Defaults to `0`.
273
+ * @returns The value rounded to the precision determined by the step.
274
+ */
275
+ const nearestStepValue = (value = 0) => {
276
+ // TODO: Use a nullish coalescing operator.
277
+ const decimals = (props.step?.toString().split('.')[1] || '').length;
278
+ return Number(value.toFixed(decimals));
279
+ };
280
+ const handleDrag = event => {
281
+ if (event instanceof globalThis.MouseEvent || event instanceof globalThis.TouchEvent) {
282
+ onDrag(event);
283
+ }
284
+ };
285
+
286
+ /**
287
+ * Sets up "drag" event handlers and calls `onDrag` in case dragging
288
+ * started on somewhere other than the thumb without a corresponding "move"
289
+ * event.
290
+ */
291
+ const onDragStart = evt => {
292
+ // Do nothing if component is disabled
293
+ if (props.disabled || props.readOnly) {
294
+ return;
295
+ }
296
+
297
+ // We're going to force focus on one of the handles later on here, b/c we're
298
+ // firing on a mousedown event, we need to call event.preventDefault() to
299
+ // keep the focus from leaving the HTMLElement.
300
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#notes
301
+ evt.preventDefault();
302
+
303
+ // TODO: Abstract `elementRef.current?.ownerDocument` to a variable that can
304
+ // be used here and everywhere else in this file.
305
+
306
+ // Add drag stop handlers
307
+ DRAG_STOP_EVENT_TYPES.forEach(element => {
308
+ elementRef.current?.ownerDocument.addEventListener(element, onDragStop);
464
309
  });
465
- _defineProperty(this, "calcLeftPercent", ({
466
- clientX,
467
- value,
468
- range
469
- }) => {
470
- const boundingRect = this.element?.getBoundingClientRect?.();
471
- let width = boundingRect ? boundingRect.right - boundingRect.left : 0;
472
310
 
473
- // Enforce a minimum width of at least 1 for calculations
474
- if (width <= 0) {
475
- width = 1;
311
+ // Add drag handlers
312
+ DRAG_EVENT_TYPES.forEach(element => {
313
+ elementRef.current?.ownerDocument.addEventListener(element, handleDrag);
314
+ });
315
+ const clientX = getClientXFromEvent(evt.nativeEvent);
316
+ let activeHandle;
317
+ if (hasTwoHandles()) {
318
+ if (evt.target == thumbRef.current) {
319
+ activeHandle = HandlePosition.LOWER;
320
+ } else if (evt.target == thumbRefUpper.current) {
321
+ activeHandle = HandlePosition.UPPER;
322
+ } else if (clientX) {
323
+ const distanceToLower = calcDistanceToHandle(HandlePosition.LOWER, clientX);
324
+ const distanceToUpper = calcDistanceToHandle(HandlePosition.UPPER, clientX);
325
+ if (distanceToLower <= distanceToUpper) {
326
+ activeHandle = HandlePosition.LOWER;
327
+ } else {
328
+ activeHandle = HandlePosition.UPPER;
329
+ }
476
330
  }
331
+ }
477
332
 
478
- // If a clientX is specified, use it to calculate the leftPercent. If not,
479
- // use the provided value to calculate it instead.
480
- if (clientX) {
481
- const leftOffset = this.state.isRtl ? (boundingRect?.right ?? 0) - clientX : clientX - (boundingRect?.left ?? 0);
482
- return leftOffset / width;
483
- } else if (value !== null && typeof value !== 'undefined' && range) {
484
- // Prevent NaN calculation if the range is 0.
485
- return range === 0 ? 0 : (value - this.props.min) / range;
333
+ // Force focus to the appropriate handle.
334
+ const focusOptions = {
335
+ preventScroll: true
336
+ };
337
+ if (hasTwoHandles()) {
338
+ if (thumbRef.current && activeHandle === HandlePosition.LOWER) {
339
+ thumbRef.current.focus(focusOptions);
340
+ } else if (thumbRefUpper.current && activeHandle === HandlePosition.UPPER) {
341
+ thumbRefUpper.current.focus(focusOptions);
486
342
  }
487
- // We should never end up in this scenario, but in case we do, and to
488
- // re-assure Typescript, return 0.
489
- return 0;
343
+ } else if (thumbRef.current) {
344
+ thumbRef.current.focus(focusOptions);
345
+ }
346
+ setState({
347
+ activeHandle
490
348
  });
491
- /**
492
- * Calculates the discrete value (snapped to the nearest step) along
493
- * with the corresponding handle position percentage.
494
- */
495
- _defineProperty(this, "calcDiscreteValueAndPercent", ({
496
- leftPercent
497
- }) => {
498
- const {
499
- step = 1,
500
- min,
501
- max
502
- } = this.props;
503
- const numSteps = Math.floor((max - min) / step) + ((max - min) % step === 0 ? 1 : 2);
504
- /** Index of the step that corresponds to `leftPercent`. */
505
- const stepIndex = Math.round(leftPercent * (numSteps - 1));
506
- const discreteValue = stepIndex === numSteps - 1 ? max : min + step * stepIndex;
507
- /** Percentage corresponding to the step index. */
508
- const discretePercent = stepIndex / (numSteps - 1);
509
- return {
510
- discreteValue,
511
- discretePercent
512
- };
349
+
350
+ // Perform first recalculation since we probably didn't click exactly in the
351
+ // middle of the thumb.
352
+ onDrag(evt.nativeEvent, activeHandle);
353
+ };
354
+
355
+ /**
356
+ * Removes "drag" and "drag stop" event handlers and calls sets the flag
357
+ * indicating that the `onRelease` callback should be called.
358
+ */
359
+ const onDragStop = () => {
360
+ // Do nothing if component is disabled
361
+ if (props.disabled || props.readOnly) {
362
+ return;
363
+ }
364
+
365
+ // TODO: Rename parameters in `DRAG_*` loops to `type`.
366
+ // Remove drag stop handlers
367
+ DRAG_STOP_EVENT_TYPES.forEach(element => {
368
+ elementRef.current?.ownerDocument.removeEventListener(element, onDragStop);
513
369
  });
514
- /**
515
- * Calculates the slider's value and handle position based on either a
516
- * mouse/touch event or an explicit value.
517
- */
518
- _defineProperty(this, "calcValue", ({
519
- clientX,
370
+
371
+ // Remove drag handlers
372
+ DRAG_EVENT_TYPES.forEach(element => {
373
+ elementRef.current?.ownerDocument.removeEventListener(element, handleDrag);
374
+ });
375
+
376
+ // Set needsOnRelease flag so event fires on next update.
377
+ setState({
378
+ needsOnRelease: true,
379
+ isValid: true,
380
+ isValidUpper: true
381
+ });
382
+ };
383
+
384
+ // TODO: Rename this reference.
385
+ /**
386
+ * Handles a "drag" event by recalculating the value/thumb and setting state
387
+ * accordingly.
388
+ *
389
+ * @param evt The event.
390
+ * @param activeHandle The first drag event call, we may have an explicit
391
+ * activeHandle value, which is to be used before state is used.
392
+ */
393
+ const _onDragRef = useRef(null);
394
+ _onDragRef.current = (evt, activeHandle) => {
395
+ activeHandle = activeHandle ?? stateRef.current.activeHandle;
396
+ // Do nothing if component is disabled, or we have no event.
397
+ if (propsRef.current.disabled || propsRef.current.readOnly || !evt) {
398
+ return;
399
+ }
400
+ const clientX = getClientXFromEvent(evt);
401
+ const {
520
402
  value,
521
- useRawValue
522
- }) => {
523
- const range = this.props.max - this.props.min;
524
- const leftPercentRaw = this.calcLeftPercent({
525
- clientX,
526
- value,
527
- range
403
+ left
404
+ } = calcValue({
405
+ clientX,
406
+ value: stateRef.current.value
407
+ });
408
+ // If we're set to two handles, negotiate which drag handle is closest to
409
+ // the users' interaction.
410
+ if (hasTwoHandles() && activeHandle) {
411
+ setValueLeftForHandle(activeHandle, {
412
+ value: nearestStepValue(value),
413
+ left
528
414
  });
529
- /** `leftPercentRaw` clamped between 0 and 1. */
530
- const leftPercent = clamp(leftPercentRaw, 0, 1);
531
- if (useRawValue) {
532
- return {
533
- value,
534
- left: leftPercent * 100
535
- };
536
- }
415
+ } else {
416
+ setState({
417
+ value: nearestStepValue(value),
418
+ left,
419
+ isValid: true
420
+ });
421
+ }
422
+ // TODO: Investigate if it would be better to not call `setState`
423
+ // back-to-back here and in other places.
424
+ setState({
425
+ correctedValue: null,
426
+ correctedPosition: null
427
+ });
428
+ };
429
+
430
+ /**
431
+ * Throttles calls to `_onDrag` by limiting events to being processed at
432
+ * most once every `EVENT_THROTTLE` milliseconds.
433
+ */
434
+ const onDrag = useMemo(() => throttle((evt, activeHandle) => {
435
+ _onDragRef.current?.(evt, activeHandle);
436
+ }, EVENT_THROTTLE, {
437
+ leading: true,
438
+ trailing: false
439
+ }), []);
537
440
 
538
- // Use the discrete value and percentage for snapping.
441
+ /**
442
+ * Handles a `keydown` event by recalculating the value/thumb and setting
443
+ * state accordingly.
444
+ */
445
+ const onKeyDown = evt => {
446
+ // Do nothing if component is disabled, or we don't have a valid event
447
+ if (props.disabled || props.readOnly) {
448
+ return;
449
+ }
450
+ const {
451
+ step = 1,
452
+ stepMultiplier = 4
453
+ } = props;
454
+ let delta = 0;
455
+ if (matches(evt, [ArrowDown, ArrowLeft])) {
456
+ delta = -step;
457
+ } else if (matches(evt, [ArrowUp, ArrowRight])) {
458
+ delta = step;
459
+ } else {
460
+ // Ignore keys we don't want to handle
461
+ return;
462
+ }
463
+
464
+ // If shift was held, account for the stepMultiplier
465
+ if (evt.shiftKey) {
466
+ delta *= stepMultiplier;
467
+ }
468
+ if (hasTwoHandles() && state.activeHandle) {
469
+ const currentValue = state.activeHandle === HandlePosition.LOWER ? state.value : state.valueUpper;
539
470
  const {
540
- discreteValue,
541
- discretePercent
542
- } = this.calcDiscreteValueAndPercent({
543
- leftPercent
471
+ value,
472
+ left
473
+ } = calcValue({
474
+ value: calcValueForDelta(currentValue ?? props.min, delta, props.step)
544
475
  });
545
- return {
546
- value: discreteValue,
547
- left: discretePercent * 100
548
- };
549
- });
550
- _defineProperty(this, "calcDistanceToHandle", (handle, clientX) => {
551
- const handleBoundingRect = this.getHandleBoundingRect(handle);
552
- // x co-ordinate of the midpoint.
553
- const handleX = handleBoundingRect.left + handleBoundingRect.width / 2;
554
- return Math.abs(handleX - clientX);
555
- });
556
- /**
557
- * Calculates a new slider value based on the current value, a change delta,
558
- * and a step.
559
- *
560
- * @param currentValue - The starting value from which the slider is moving.
561
- * @param delta - The amount to adjust the current value by.
562
- * @param step - The step. Defaults to `1`.
563
- * @returns The new slider value, rounded to the same number of decimal places
564
- * as the step.
565
- */
566
- _defineProperty(this, "calcValueForDelta", (currentValue, delta, step = 1) => {
567
- const base = delta > 0 ? Math.floor(currentValue / step) * step : currentValue;
568
- const newValue = base + delta;
569
- const decimals = (step.toString().split('.')[1] || '').length;
570
- return Number(newValue.toFixed(decimals));
571
- });
572
- /**
573
- * Sets state relevant to the given handle position.
574
- *
575
- * Guards against setting either lower or upper values beyond its counterpart.
576
- */
577
- _defineProperty(this, "setValueLeftForHandle", (handle, {
578
- value: newValue,
579
- left: newLeft
580
- }) => {
476
+ setValueLeftForHandle(state.activeHandle, {
477
+ value: nearestStepValue(value),
478
+ left
479
+ });
480
+ } else {
581
481
  const {
582
482
  value,
583
- valueUpper,
483
+ left
484
+ } = calcValue({
485
+ // Ensures custom value from `<input>` won't cause skipping next stepping
486
+ // point with right arrow key, e.g. Typing 51 in `<input>`, moving focus
487
+ // onto the thumb and the hitting right arrow key should yield 52 instead
488
+ // of 54.
489
+ value: calcValueForDelta(state.value, delta, props.step)
490
+ });
491
+ setState({
492
+ value: nearestStepValue(value),
584
493
  left,
585
- leftUpper
586
- } = this.state;
587
- if (handle === HandlePosition.LOWER) {
588
- // Don't allow higher than the upper handle.
589
- this.setState({
590
- value: valueUpper && newValue > valueUpper ? valueUpper : newValue,
591
- left: valueUpper && newValue > valueUpper ? leftUpper : newLeft,
592
- isValid: true
494
+ isValid: true
495
+ });
496
+ }
497
+ setState({
498
+ correctedValue: null,
499
+ correctedPosition: null
500
+ });
501
+ };
502
+
503
+ /**
504
+ * Provides the two-way binding for the input field of the Slider. It also
505
+ * Handles a change to the input field by recalculating the value/thumb and
506
+ * setting state accordingly.
507
+ */
508
+ const onChangeInput = evt => {
509
+ // Do nothing if component is disabled
510
+ if (props.disabled || props.readOnly) {
511
+ return;
512
+ }
513
+
514
+ // Do nothing if we have no valid event, target, or value
515
+ if (!evt || !('target' in evt) || typeof evt.target.value !== 'string') {
516
+ return;
517
+ }
518
+
519
+ // Avoid calling calcValue for invalid numbers, but still update the state.
520
+ const activeHandle = evt.target.dataset.handlePosition ?? HandlePosition.LOWER;
521
+ const targetValue = Number.parseFloat(evt.target.value);
522
+ if (hasTwoHandles()) {
523
+ if (isNaN(targetValue)) {
524
+ setValueForHandle(activeHandle, evt.target.value);
525
+ } else if (isValidValueForPosition({
526
+ handle: activeHandle,
527
+ value: targetValue,
528
+ min: props.min,
529
+ max: props.max
530
+ })) {
531
+ processNewInputValue(evt.target);
532
+ } else {
533
+ setValueForHandle(activeHandle, targetValue);
534
+ }
535
+ } else {
536
+ if (isNaN(targetValue)) {
537
+ // TODO: Address this error
538
+ //
539
+ // @ts-expect-error - Passing a string to something that expects a
540
+ // number.
541
+ setState({
542
+ value: evt.target.value
593
543
  });
544
+ } else if (isValidValue({
545
+ value: targetValue,
546
+ min: props.min,
547
+ max: props.max
548
+ })) {
549
+ processNewInputValue(evt.target);
594
550
  } else {
595
- this.setState({
596
- valueUpper: value && newValue < value ? value : newValue,
597
- leftUpper: value && newValue < value ? left : newLeft,
598
- isValidUpper: true
551
+ setState({
552
+ value: targetValue
599
553
  });
600
554
  }
555
+ }
556
+ };
557
+
558
+ /**
559
+ * Checks for validity of input value after clicking out of the input. It also
560
+ * Handles state change to isValid state.
561
+ */
562
+ const onBlurInput = evt => {
563
+ // Do nothing if we have no valid event, target, or value
564
+ if (!evt || !('target' in evt) || typeof evt.target.value !== 'string') {
565
+ return;
566
+ }
567
+ const {
568
+ value: targetValue
569
+ } = evt.target;
570
+ processNewInputValue(evt.target);
571
+ props.onBlur?.({
572
+ value: targetValue,
573
+ handlePosition: evt.target.dataset.handlePosition
601
574
  });
602
- _defineProperty(this, "setValueForHandle", (handle, value) => {
603
- if (handle === HandlePosition.LOWER) {
604
- this.setState({
605
- value,
606
- isValid: true
575
+ };
576
+ const onInputKeyDown = evt => {
577
+ // Do nothing if component is disabled, or we don't have a valid event.
578
+ if (props.disabled || props.readOnly || !(evt.target instanceof HTMLInputElement)) {
579
+ return;
580
+ }
581
+
582
+ // Do nothing if we have no valid event, target, or value.
583
+ if (!evt || !('target' in evt) || typeof evt.target.value !== 'string') {
584
+ return;
585
+ }
586
+ if (matches(evt, [Enter])) {
587
+ processNewInputValue(evt.target);
588
+ }
589
+ };
590
+ const processNewInputValue = input => {
591
+ setState({
592
+ correctedValue: null,
593
+ correctedPosition: null
594
+ });
595
+ const targetValue = Number.parseFloat(input.value);
596
+ const validity = !isNaN(targetValue);
597
+
598
+ // When there are two handles, we'll also have the data-handle-position
599
+ // attribute to consider the other value before settling on the validity to
600
+ // set.
601
+ const handlePosition = input.dataset.handlePosition;
602
+ if (handlePosition === HandlePosition.LOWER) {
603
+ setState({
604
+ isValid: validity
605
+ });
606
+ } else if (handlePosition === HandlePosition.UPPER) {
607
+ setState({
608
+ isValidUpper: validity
609
+ });
610
+ }
611
+ setState({
612
+ isValid: validity
613
+ });
614
+ if (validity) {
615
+ const adjustedValue = handlePosition ? getAdjustedValueForPosition({
616
+ handle: handlePosition,
617
+ value: targetValue,
618
+ min: props.min,
619
+ max: props.max
620
+ }) : getAdjustedValue({
621
+ value: targetValue,
622
+ min: props.min,
623
+ max: props.max
624
+ });
625
+ if (adjustedValue !== targetValue) {
626
+ setState({
627
+ correctedValue: targetValue.toString(),
628
+ correctedPosition: handlePosition ?? null
607
629
  });
608
630
  } else {
609
- this.setState({
610
- valueUpper: value,
611
- isValidUpper: true
631
+ setState({
632
+ correctedValue: null,
633
+ correctedPosition: null
612
634
  });
613
635
  }
614
- });
615
- _defineProperty(this, "isValidValueForPosition", ({
616
- handle,
617
- value: newValue,
618
- min,
619
- max
620
- }) => {
621
636
  const {
622
637
  value,
623
- valueUpper
624
- } = this.state;
625
- if (!this.isValidValue({
626
- value: newValue,
627
- min,
628
- max
629
- })) {
630
- return false;
631
- }
632
- if (handle === HandlePosition.LOWER) {
633
- return !valueUpper || newValue <= valueUpper;
634
- } else if (handle === HandlePosition.UPPER) {
635
- return !value || newValue >= value;
638
+ left
639
+ } = calcValue({
640
+ value: adjustedValue,
641
+ useRawValue: true
642
+ });
643
+ if (handlePosition) {
644
+ setValueLeftForHandle(handlePosition, {
645
+ value: nearestStepValue(value),
646
+ left
647
+ });
648
+ } else {
649
+ setState({
650
+ value,
651
+ left
652
+ });
636
653
  }
637
- return false;
638
- });
639
- _defineProperty(this, "isValidValue", ({
640
- value,
641
- min,
642
- max
643
- }) => {
644
- return !(value < min || value > max);
645
- });
646
- _defineProperty(this, "getAdjustedValueForPosition", ({
647
- handle,
648
- value: newValue,
654
+ }
655
+ };
656
+ const calcLeftPercent = ({
657
+ clientX,
658
+ value,
659
+ range
660
+ }) => {
661
+ // TODO: Delete the optional chaining operator after `getBoundingClientRect`.
662
+ const boundingRect = elementRef.current?.getBoundingClientRect?.();
663
+ let width = boundingRect ? boundingRect.right - boundingRect.left : 0;
664
+
665
+ // Enforce a minimum width of at least 1 for calculations
666
+ if (width <= 0) {
667
+ width = 1;
668
+ }
669
+
670
+ // If a clientX is specified, use it to calculate the leftPercent. If not,
671
+ // use the provided value to calculate it instead.
672
+ if (clientX) {
673
+ const leftOffset = state.isRtl ? (boundingRect?.right ?? 0) - clientX : clientX - (boundingRect?.left ?? 0);
674
+ return leftOffset / width;
675
+ } else if (value !== null && typeof value !== 'undefined' && range) {
676
+ // Prevent NaN calculation if the range is 0.
677
+ return range === 0 ? 0 : (value - props.min) / range;
678
+ }
679
+ // We should never end up in this scenario, but in case we do, and to
680
+ // re-assure Typescript, return 0.
681
+ return 0;
682
+ };
683
+
684
+ /**
685
+ * Calculates the discrete value (snapped to the nearest step) along
686
+ * with the corresponding handle position percentage.
687
+ */
688
+ const calcDiscreteValueAndPercent = ({
689
+ leftPercent
690
+ }) => {
691
+ const {
692
+ step = 1,
649
693
  min,
650
694
  max
651
- }) => {
652
- const {
653
- value,
654
- valueUpper
655
- } = this.state;
656
- newValue = this.getAdjustedValue({
657
- value: newValue,
658
- min,
659
- max
660
- });
695
+ } = props;
696
+ const numSteps = Math.floor((max - min) / step) + ((max - min) % step === 0 ? 1 : 2);
697
+ /** Index of the step that corresponds to `leftPercent`. */
698
+ const stepIndex = Math.round(leftPercent * (numSteps - 1));
699
+ const discreteValue = stepIndex === numSteps - 1 ? max : min + step * stepIndex;
700
+ /** Percentage corresponding to the step index. */
701
+ const discretePercent = stepIndex / (numSteps - 1);
702
+ return {
703
+ discreteValue,
704
+ discretePercent
705
+ };
706
+ };
661
707
 
662
- // Next adjust to the opposite handle.
663
- if (handle === HandlePosition.LOWER && valueUpper) {
664
- newValue = newValue > valueUpper ? valueUpper : newValue;
665
- } else if (handle === HandlePosition.UPPER && value) {
666
- newValue = newValue < value ? value : newValue;
667
- }
668
- return newValue;
669
- });
670
- _defineProperty(this, "getAdjustedValue", ({
708
+ /**
709
+ * Calculates the slider's value and handle position based on either a
710
+ * mouse/touch event or an explicit value.
711
+ */
712
+ const calcValue = ({
713
+ clientX,
714
+ value,
715
+ useRawValue
716
+ }) => {
717
+ const range = props.max - props.min;
718
+ const leftPercentRaw = calcLeftPercent({
719
+ clientX,
671
720
  value,
672
- min,
673
- max
674
- }) => {
675
- if (value < min) {
676
- value = min;
677
- }
678
- if (value > max) {
679
- value = max;
680
- }
681
- return value;
721
+ range
682
722
  });
683
- /**
684
- * Get the bounding rect for the requested handles' DOM element.
685
- *
686
- * If the bounding rect is not available, a new, empty DOMRect is returned.
687
- */
688
- _defineProperty(this, "getHandleBoundingRect", handle => {
689
- let boundingRect;
690
- if (handle === HandlePosition.LOWER) {
691
- boundingRect = this.thumbRef.current?.getBoundingClientRect();
692
- } else {
693
- boundingRect = this.thumbRefUpper.current?.getBoundingClientRect();
694
- }
695
- return boundingRect ?? new DOMRect();
723
+ /** `leftPercentRaw` clamped between 0 and 1. */
724
+ const leftPercent = clamp(leftPercentRaw, 0, 1);
725
+ if (useRawValue) {
726
+ return {
727
+ value,
728
+ left: leftPercent * 100
729
+ };
730
+ }
731
+
732
+ // Use the discrete value and percentage for snapping.
733
+ const {
734
+ discreteValue,
735
+ discretePercent
736
+ } = calcDiscreteValueAndPercent({
737
+ leftPercent
696
738
  });
697
- this.thumbRef = /*#__PURE__*/createRef();
698
- this.thumbRefUpper = /*#__PURE__*/createRef();
699
- this.filledTrackRef = /*#__PURE__*/createRef();
700
- }
739
+ return {
740
+ value: discreteValue,
741
+ left: discretePercent * 100
742
+ };
743
+ };
744
+ const calcDistanceToHandle = (handle, clientX) => {
745
+ const handleBoundingRect = getHandleBoundingRect(handle);
746
+ /** x-coordinate of the midpoint. */
747
+ const handleX = handleBoundingRect.left + handleBoundingRect.width / 2;
748
+ return Math.abs(handleX - clientX);
749
+ };
701
750
 
702
751
  /**
703
- * Sets up initial slider position and value in response to component mount.
752
+ * Calculates a new slider value based on the current value, a change delta,
753
+ * and a step.
754
+ *
755
+ * @param currentValue - The starting value from which the slider is moving.
756
+ * @param delta - The amount to adjust the current value by.
757
+ * @param step - The step. Defaults to `1`.
758
+ * @returns The new slider value, rounded to the same number of decimal places
759
+ * as the step.
704
760
  */
705
- componentDidMount() {
706
- if (this.element) {
707
- const isRtl = document?.dir === 'rtl';
708
- if (this.hasTwoHandles()) {
709
- const {
710
- value,
711
- left
712
- } = this.calcValue({
713
- value: this.state.value,
714
- useRawValue: true
715
- });
716
- const {
717
- value: valueUpper,
718
- left: leftUpper
719
- } = this.calcValue({
720
- value: this.state.valueUpper,
721
- useRawValue: true
722
- });
723
- this.setState({
724
- isRtl,
725
- value,
726
- left,
727
- valueUpper,
728
- leftUpper
729
- });
730
- if (this.filledTrackRef.current) {
731
- this.filledTrackRef.current.style.transform = this.state.isRtl ? `translate(${100 - this.state.leftUpper}%, -50%) scaleX(${(this.state.leftUpper - this.state.left) / 100})` : `translate(${this.state.left}%, -50%) scaleX(${(this.state.leftUpper - this.state.left) / 100})`;
732
- }
733
- } else {
734
- const {
735
- value,
736
- left
737
- } = this.calcValue({
738
- value: this.state.value,
739
- useRawValue: true
740
- });
741
- this.setState({
742
- isRtl,
743
- value,
744
- left
745
- });
746
- if (this.filledTrackRef.current) {
747
- this.filledTrackRef.current.style.transform = this.state.isRtl ? `translate(100%, -50%) scaleX(-${this.state.left / 100})` : `translate(0%, -50%) scaleX(${this.state.left / 100})`;
748
- }
749
- }
750
- }
751
- }
761
+ const calcValueForDelta = (currentValue, delta, step = 1) => {
762
+ const base = delta > 0 ? Math.floor(currentValue / step) * step : currentValue;
763
+ const newValue = base + delta;
764
+ // TODO: Why is the logical OR needed here?
765
+ const decimals = (step.toString().split('.')[1] || '').length;
766
+ return Number(newValue.toFixed(decimals));
767
+ };
752
768
 
753
769
  /**
754
- * Handles firing of `onChange` and `onRelease` callbacks to parent in
755
- * response to state changes.
770
+ * Sets state relevant to the given handle position.
756
771
  *
757
- * @param {*} prevProps prevProps
758
- * @param {*} prevState The previous Slider state, used to see if callbacks
759
- * should be called.
772
+ * Guards against setting either lower or upper values beyond its counterpart.
760
773
  */
761
- componentDidUpdate(prevProps, prevState) {
762
- // Fire onChange event handler if present, if there's a usable value, and
763
- // if the value is different from the last one
764
- if (this.hasTwoHandles()) {
765
- if (this.filledTrackRef.current) {
766
- this.filledTrackRef.current.style.transform = this.state.isRtl ? `translate(${100 - this.state.leftUpper}%, -50%) scaleX(${(this.state.leftUpper - this.state.left) / 100})` : `translate(${this.state.left}%, -50%) scaleX(${(this.state.leftUpper - this.state.left) / 100})`;
767
- }
774
+ const setValueLeftForHandle = (handle, {
775
+ value: newValue,
776
+ left: newLeft
777
+ }) => {
778
+ const {
779
+ value,
780
+ valueUpper,
781
+ left,
782
+ leftUpper
783
+ } = state;
784
+ if (handle === HandlePosition.LOWER) {
785
+ // Don't allow higher than the upper handle.
786
+ setState({
787
+ value: valueUpper && newValue > valueUpper ? valueUpper : newValue,
788
+ left: valueUpper && newValue > valueUpper ? leftUpper : newLeft,
789
+ isValid: true
790
+ });
768
791
  } else {
769
- if (this.filledTrackRef.current) {
770
- this.filledTrackRef.current.style.transform = this.state.isRtl ? `translate(100%, -50%) scaleX(-${this.state.left / 100})` : `translate(0%, -50%) scaleX(${this.state.left / 100})`;
771
- }
772
- }
773
- if ((prevState.value !== this.state.value || prevState.valueUpper !== this.state.valueUpper) && typeof this.props.onChange === 'function') {
774
- this.props.onChange({
775
- value: this.state.value,
776
- valueUpper: this.state.valueUpper
792
+ setState({
793
+ valueUpper: value && newValue < value ? value : newValue,
794
+ leftUpper: value && newValue < value ? left : newLeft,
795
+ isValidUpper: true
777
796
  });
778
797
  }
779
-
780
- // Fire onRelease event handler if present and if needed
781
- if (this.state.needsOnRelease && typeof this.props.onRelease === 'function') {
782
- this.props.onRelease({
783
- value: this.state.value,
784
- valueUpper: this.state.valueUpper
798
+ };
799
+ const setValueForHandle = (handle, value) => {
800
+ if (handle === HandlePosition.LOWER) {
801
+ setState({
802
+ // TODO: Address this error
803
+ //
804
+ // @ts-expect-error - Passing a string to something that expects a
805
+ // number.
806
+ value,
807
+ isValid: true
785
808
  });
786
- // Reset the flag
787
- this.setState({
788
- needsOnRelease: false
809
+ } else {
810
+ setState({
811
+ // TODO: Address this error
812
+ //
813
+ // @ts-expect-error - Passing a string to something that expects a
814
+ // number.
815
+ valueUpper: value,
816
+ isValidUpper: true
789
817
  });
790
818
  }
819
+ };
820
+ const isValidValueForPosition = ({
821
+ handle,
822
+ value: newValue,
823
+ min,
824
+ max
825
+ }) => {
826
+ const {
827
+ value,
828
+ valueUpper
829
+ } = state;
830
+ if (!isValidValue({
831
+ value: newValue,
832
+ min,
833
+ max
834
+ })) {
835
+ return false;
836
+ }
837
+ if (handle === HandlePosition.LOWER) {
838
+ return !valueUpper || newValue <= valueUpper;
839
+ } else if (handle === HandlePosition.UPPER) {
840
+ return !value || newValue >= value;
841
+ }
842
+ return false;
843
+ };
844
+ const isValidValue = ({
845
+ value,
846
+ min,
847
+ max
848
+ }) => {
849
+ return !(value < min || value > max);
850
+ };
851
+ const getAdjustedValueForPosition = ({
852
+ handle,
853
+ value: newValueInput,
854
+ min,
855
+ max
856
+ }) => {
857
+ const {
858
+ value,
859
+ valueUpper
860
+ } = state;
861
+ let newValue = getAdjustedValue({
862
+ value: newValueInput,
863
+ min,
864
+ max
865
+ });
791
866
 
792
- // If value from props does not change, do nothing here.
793
- // Otherwise, do prop -> state sync without "value capping".
794
- if (prevProps.value === this.props.value && prevProps.unstable_valueUpper === this.props.unstable_valueUpper && prevProps.max === this.props.max && prevProps.min === this.props.min) {
795
- return;
867
+ // TODO: Just return the value.
868
+ // Next adjust to the opposite handle.
869
+ if (handle === HandlePosition.LOWER && valueUpper) {
870
+ newValue = newValue > valueUpper ? valueUpper : newValue;
871
+ } else if (handle === HandlePosition.UPPER && value) {
872
+ newValue = newValue < value ? value : newValue;
796
873
  }
797
- this.setState(this.calcValue({
798
- value: this.props.value,
799
- useRawValue: true
800
- }));
801
- if (typeof this.props.unstable_valueUpper !== 'undefined') {
802
- const {
803
- value: valueUpper,
804
- left: leftUpper
805
- } = this.calcValue({
806
- value: this.props.unstable_valueUpper,
807
- useRawValue: true
808
- });
809
- this.setState({
810
- valueUpper,
811
- leftUpper
812
- });
813
- } else {
814
- this.setState({
815
- valueUpper: undefined,
816
- leftUpper: undefined
817
- });
874
+ return newValue;
875
+ };
876
+ const getAdjustedValue = ({
877
+ value,
878
+ min,
879
+ max
880
+ }) => {
881
+ // TODO: Just return the value.
882
+ if (value < min) {
883
+ value = min;
818
884
  }
819
- }
885
+ if (value > max) {
886
+ value = max;
887
+ }
888
+ return value;
889
+ };
820
890
 
821
891
  /**
822
- * Rounds a given value to the nearest step defined by the slider's `step`
823
- * prop.
892
+ * Get the bounding rect for the requested handles' DOM element.
824
893
  *
825
- * @param value - The value to adjust to the nearest step. Defaults to `0`.
826
- * @returns The value rounded to the precision determined by the step.
894
+ * If the bounding rect is not available, a new, empty DOMRect is returned.
827
895
  */
828
- nearestStepValue(value = 0) {
829
- const decimals = (this.props.step?.toString().split('.')[1] || '').length;
830
- return Number(value.toFixed(decimals));
831
- }
832
- getClientXFromEvent(event) {
896
+ const getHandleBoundingRect = handle => {
897
+ let boundingRect;
898
+ if (handle === HandlePosition.LOWER) {
899
+ boundingRect = thumbRef.current?.getBoundingClientRect();
900
+ } else {
901
+ boundingRect = thumbRefUpper.current?.getBoundingClientRect();
902
+ }
903
+ return boundingRect ?? new DOMRect();
904
+ };
905
+ const getClientXFromEvent = event => {
833
906
  let clientX;
834
907
  if ('clientX' in event) {
835
908
  clientX = event.clientX;
@@ -837,17 +910,12 @@ class Slider extends PureComponent {
837
910
  clientX = event.touches[0].clientX;
838
911
  }
839
912
  return clientX;
840
- }
841
- hasTwoHandles() {
842
- return typeof this.state.valueUpper !== 'undefined';
843
- }
844
-
845
- // syncs invalid state and prop
846
- static getDerivedStateFromProps(props, state) {
913
+ };
914
+ useEffect(() => {
847
915
  const {
848
916
  isValid,
849
917
  isValidUpper
850
- } = state;
918
+ } = stateRef.current;
851
919
  const derivedState = {};
852
920
 
853
921
  // Will override state in favor of invalid prop
@@ -858,16 +926,20 @@ class Slider extends PureComponent {
858
926
  if (isValid === false) derivedState.isValid = true;
859
927
  if (isValidUpper === false) derivedState.isValidUpper = true;
860
928
  }
861
- return Object.keys(derivedState).length ? derivedState : null;
862
- }
863
- render() {
864
- var _Fragment, _Fragment2, _Fragment3, _Fragment4;
929
+ if (Object.keys(derivedState).length) {
930
+ setState(derivedState);
931
+ }
932
+ }, [props.invalid]);
933
+
934
+ // TODO: Delete this IIFE. It was added to maintain whitespace and to make it clear
935
+ // what exactly has changed.
936
+ return ((_Fragment, _Fragment2, _Fragment3, _Fragment4) => {
865
937
  const {
866
938
  ariaLabelInput,
867
939
  unstable_ariaLabelInputUpper: ariaLabelInputUpper,
868
940
  className,
869
941
  hideTextInput = false,
870
- id = this.inputId = this.inputId ||
942
+ id = inputIdRef.current = inputIdRef.current ||
871
943
  // TODO:
872
944
  // 1. Why isn't `inputId` just set to this value instead of an empty
873
945
  // string?
@@ -883,6 +955,7 @@ class Slider extends PureComponent {
883
955
  hideLabel,
884
956
  step = 1,
885
957
  // TODO: Other properties are deleted below. Why isn't this one?
958
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/carbon-design-system/carbon/issues/20071
886
959
  stepMultiplier: _stepMultiplier,
887
960
  inputType = 'number',
888
961
  invalidText,
@@ -896,8 +969,7 @@ class Slider extends PureComponent {
896
969
  warnText,
897
970
  translateWithId: t = translateWithId,
898
971
  ...other
899
- } = this.props;
900
- const twoHandles = this.hasTwoHandles();
972
+ } = props;
901
973
  delete other.onRelease;
902
974
  delete other.invalid;
903
975
  delete other.unstable_valueUpper;
@@ -909,12 +981,14 @@ class Slider extends PureComponent {
909
981
  correctedValue,
910
982
  correctedPosition,
911
983
  isRtl
912
- } = this.state;
984
+ } = state;
913
985
  const showWarning = !readOnly && warn ||
914
986
  // TODO: https://github.com/carbon-design-system/carbon/issues/18991#issuecomment-2795709637
987
+ // eslint-disable-next-line valid-typeof , no-constant-binary-expression -- https://github.com/carbon-design-system/carbon/issues/20071
915
988
  typeof correctedValue !== null && correctedPosition === HandlePosition.LOWER && isValid;
916
989
  const showWarningUpper = !readOnly && warn ||
917
990
  // TODO: https://github.com/carbon-design-system/carbon/issues/18991#issuecomment-2795709637
991
+ // eslint-disable-next-line valid-typeof, no-constant-binary-expression -- https://github.com/carbon-design-system/carbon/issues/20071
918
992
  typeof correctedValue !== null && correctedPosition === (twoHandles ? HandlePosition.UPPER : HandlePosition.LOWER) && (twoHandles ? isValidUpper : isValid);
919
993
  return /*#__PURE__*/React.createElement(PrefixContext.Consumer, null, prefix => {
920
994
  const labelId = `${id}-label`;
@@ -966,12 +1040,12 @@ class Slider extends PureComponent {
966
1040
  }]);
967
1041
  const lowerThumbWrapperProps = {
968
1042
  style: {
969
- insetInlineStart: `${this.state.left}%`
1043
+ insetInlineStart: `${state.left}%`
970
1044
  }
971
1045
  };
972
1046
  const upperThumbWrapperProps = {
973
1047
  style: {
974
- insetInlineStart: `${this.state.leftUpper}%`
1048
+ insetInlineStart: `${state.leftUpper}%`
975
1049
  }
976
1050
  };
977
1051
  return /*#__PURE__*/React.createElement("div", {
@@ -997,10 +1071,10 @@ class Slider extends PureComponent {
997
1071
  min: min,
998
1072
  max: max,
999
1073
  step: step,
1000
- onChange: this.onChange,
1001
- onBlur: this.onBlur,
1002
- onKeyUp: this.props.onInputKeyUp,
1003
- onKeyDown: this.onInputKeyDown,
1074
+ onChange: onChangeInput,
1075
+ onBlur: onBlurInput,
1076
+ onKeyUp: props.onInputKeyUp,
1077
+ onKeyDown: onInputKeyDown,
1004
1078
  "data-invalid": !isValid && !readOnly ? true : null,
1005
1079
  "data-handle-position": HandlePosition.LOWER,
1006
1080
  "aria-invalid": !isValid && !readOnly ? true : undefined,
@@ -1014,11 +1088,11 @@ class Slider extends PureComponent {
1014
1088
  }, formatLabel(min, minLabel)), /*#__PURE__*/React.createElement("div", _extends({
1015
1089
  className: sliderClasses,
1016
1090
  ref: node => {
1017
- this.element = node;
1091
+ elementRef.current = node;
1018
1092
  },
1019
- onMouseDown: this.onDragStart,
1020
- onTouchStart: this.onDragStart,
1021
- onKeyDown: this.onKeyDown,
1093
+ onMouseDown: onDragStart,
1094
+ onTouchStart: onDragStart,
1095
+ onKeyDown: onKeyDown,
1022
1096
  role: "presentation",
1023
1097
  tabIndex: -1,
1024
1098
  "data-invalid": (twoHandles ? !isValid || !isValidUpper : !isValid) && !readOnly ? true : null
@@ -1038,8 +1112,8 @@ class Slider extends PureComponent {
1038
1112
  "aria-valuenow": value,
1039
1113
  "aria-labelledby": twoHandles ? undefined : labelId,
1040
1114
  "aria-label": twoHandles ? ariaLabelInput : undefined,
1041
- ref: this.thumbRef,
1042
- onFocus: () => this.setState({
1115
+ ref: thumbRef,
1116
+ onFocus: () => setState({
1043
1117
  activeHandle: HandlePosition.LOWER
1044
1118
  })
1045
1119
  }, twoHandles && !isRtl ? _Fragment || (_Fragment = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LowerHandle, {
@@ -1063,8 +1137,8 @@ class Slider extends PureComponent {
1063
1137
  "aria-valuemin": value,
1064
1138
  "aria-valuenow": valueUpper,
1065
1139
  "aria-label": ariaLabelInputUpper,
1066
- ref: this.thumbRefUpper,
1067
- onFocus: () => this.setState({
1140
+ ref: thumbRefUpper,
1141
+ onFocus: () => setState({
1068
1142
  activeHandle: HandlePosition.UPPER
1069
1143
  })
1070
1144
  }, twoHandles && !isRtl ? _Fragment3 || (_Fragment3 = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(UpperHandle, {
@@ -1078,11 +1152,11 @@ class Slider extends PureComponent {
1078
1152
  }))) : undefined)) : null, /*#__PURE__*/React.createElement("div", {
1079
1153
  className: `${prefix}--slider__track`,
1080
1154
  ref: node => {
1081
- this.track = node;
1155
+ trackRef.current = node;
1082
1156
  }
1083
1157
  }), /*#__PURE__*/React.createElement("div", {
1084
1158
  className: `${prefix}--slider__filled-track`,
1085
- ref: this.filledTrackRef
1159
+ ref: filledTrackRef
1086
1160
  })), /*#__PURE__*/React.createElement(Text, {
1087
1161
  className: `${prefix}--slider__range-label`
1088
1162
  }, formatLabel(max, maxLabel)), /*#__PURE__*/React.createElement("div", {
@@ -1100,10 +1174,10 @@ class Slider extends PureComponent {
1100
1174
  min: min,
1101
1175
  max: max,
1102
1176
  step: step,
1103
- onChange: this.onChange,
1104
- onBlur: this.onBlur,
1105
- onKeyDown: this.onInputKeyDown,
1106
- onKeyUp: this.props.onInputKeyUp,
1177
+ onChange: onChangeInput,
1178
+ onBlur: onBlurInput,
1179
+ onKeyDown: onInputKeyDown,
1180
+ onKeyUp: props.onInputKeyUp,
1107
1181
  "data-invalid": (twoHandles ? !isValidUpper : !isValid) && !readOnly ? true : null,
1108
1182
  "data-handle-position": twoHandles ? HandlePosition.UPPER : null,
1109
1183
  "aria-invalid": (twoHandles ? !isValidUpper : !isValid) && !readOnly ? true : undefined,
@@ -1126,10 +1200,8 @@ class Slider extends PureComponent {
1126
1200
  correctedValue
1127
1201
  })));
1128
1202
  });
1129
- }
1130
- }
1131
- _defineProperty(Slider, "contextType", FeatureFlagContext);
1132
- _defineProperty(Slider, "translationIds", Object.values(translationIds));
1203
+ })();
1204
+ };
1133
1205
  Slider.propTypes = {
1134
1206
  /**
1135
1207
  * The `ariaLabel` for the `<input>`.
@@ -1270,4 +1342,4 @@ Slider.propTypes = {
1270
1342
  warnText: PropTypes.node
1271
1343
  };
1272
1344
 
1273
- export { Slider as default };
1345
+ export { Slider };