@toptal/picasso-slider 1.0.6 → 2.0.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 (88) hide show
  1. package/dist-package/src/Slider/Slider.d.ts +20 -20
  2. package/dist-package/src/Slider/Slider.d.ts.map +1 -1
  3. package/dist-package/src/Slider/Slider.js +49 -41
  4. package/dist-package/src/Slider/Slider.js.map +1 -1
  5. package/dist-package/src/Slider/hooks/index.d.ts +2 -0
  6. package/dist-package/src/Slider/hooks/index.d.ts.map +1 -0
  7. package/dist-package/src/Slider/hooks/index.js +2 -0
  8. package/dist-package/src/Slider/hooks/index.js.map +1 -0
  9. package/dist-package/src/Slider/hooks/use-label-overlap.d.ts +8 -0
  10. package/dist-package/src/Slider/hooks/use-label-overlap.d.ts.map +1 -0
  11. package/dist-package/src/Slider/hooks/use-label-overlap.js +35 -0
  12. package/dist-package/src/Slider/hooks/use-label-overlap.js.map +1 -0
  13. package/dist-package/src/Slider/index.d.ts +0 -1
  14. package/dist-package/src/Slider/index.d.ts.map +1 -1
  15. package/dist-package/src/Slider/index.js +0 -1
  16. package/dist-package/src/Slider/index.js.map +1 -1
  17. package/dist-package/src/SliderMark/SliderMark.d.ts +13 -0
  18. package/dist-package/src/SliderMark/SliderMark.d.ts.map +1 -0
  19. package/dist-package/src/SliderMark/SliderMark.js +10 -0
  20. package/dist-package/src/SliderMark/SliderMark.js.map +1 -0
  21. package/dist-package/src/SliderMark/index.d.ts +2 -0
  22. package/dist-package/src/SliderMark/index.d.ts.map +1 -0
  23. package/dist-package/src/SliderMark/index.js +2 -0
  24. package/dist-package/src/SliderMark/index.js.map +1 -0
  25. package/dist-package/src/SliderValueLabel/SliderValueLabel.d.ts +9 -14
  26. package/dist-package/src/SliderValueLabel/SliderValueLabel.d.ts.map +1 -1
  27. package/dist-package/src/SliderValueLabel/SliderValueLabel.js +40 -23
  28. package/dist-package/src/SliderValueLabel/SliderValueLabel.js.map +1 -1
  29. package/dist-package/src/SliderValueLabel/index.d.ts +1 -2
  30. package/dist-package/src/SliderValueLabel/index.d.ts.map +1 -1
  31. package/dist-package/src/SliderValueLabel/index.js +1 -1
  32. package/dist-package/src/SliderValueLabel/index.js.map +1 -1
  33. package/dist-package/src/index.d.ts +0 -1
  34. package/dist-package/src/index.d.ts.map +1 -1
  35. package/dist-package/src/index.js +0 -1
  36. package/dist-package/src/index.js.map +1 -1
  37. package/dist-package/src/utils/check-bg-color.d.ts +7 -0
  38. package/dist-package/src/utils/check-bg-color.d.ts.map +1 -0
  39. package/dist-package/src/utils/check-bg-color.js +15 -0
  40. package/dist-package/src/utils/check-bg-color.js.map +1 -0
  41. package/dist-package/src/utils/check-overlap.d.ts +26 -0
  42. package/dist-package/src/utils/check-overlap.d.ts.map +1 -0
  43. package/dist-package/src/utils/check-overlap.js +32 -0
  44. package/dist-package/src/utils/check-overlap.js.map +1 -0
  45. package/dist-package/src/utils/get-x-placement.d.ts +8 -0
  46. package/dist-package/src/utils/get-x-placement.d.ts.map +1 -0
  47. package/dist-package/src/utils/get-x-placement.js +17 -0
  48. package/dist-package/src/utils/get-x-placement.js.map +1 -0
  49. package/dist-package/src/utils/index.d.ts +4 -0
  50. package/dist-package/src/utils/index.d.ts.map +1 -0
  51. package/dist-package/src/utils/index.js +4 -0
  52. package/dist-package/src/utils/index.js.map +1 -0
  53. package/package.json +5 -7
  54. package/src/Slider/Slider.tsx +114 -101
  55. package/src/Slider/__snapshots__/test.tsx.snap +58 -30
  56. package/src/Slider/hooks/index.ts +1 -0
  57. package/src/Slider/hooks/use-label-overlap.ts +48 -0
  58. package/src/Slider/index.ts +0 -1
  59. package/src/Slider/story/ControlledWithLabel.example.tsx +1 -1
  60. package/src/Slider/story/Marks.example.tsx +13 -4
  61. package/src/Slider/story/Range.example.tsx +0 -1
  62. package/src/Slider/story/RangeWithValueLabel.example.tsx +0 -1
  63. package/src/Slider/story/Tooltip.example.tsx +22 -6
  64. package/src/Slider/story/index.jsx +0 -8
  65. package/src/Slider/test.tsx +6 -0
  66. package/src/SliderMark/SliderMark.tsx +35 -0
  67. package/src/SliderMark/index.ts +1 -0
  68. package/src/SliderValueLabel/SliderValueLabel.tsx +74 -54
  69. package/src/SliderValueLabel/index.ts +1 -5
  70. package/src/index.ts +0 -1
  71. package/src/utils/check-bg-color.test.ts +92 -0
  72. package/src/utils/check-bg-color.ts +28 -0
  73. package/src/utils/check-overlap.test.ts +55 -0
  74. package/src/utils/check-overlap.ts +43 -0
  75. package/src/utils/get-x-placement.test.ts +93 -0
  76. package/src/utils/get-x-placement.ts +28 -0
  77. package/src/utils/index.ts +3 -0
  78. package/dist-package/src/Slider/SliderContext.d.ts +0 -11
  79. package/dist-package/src/Slider/SliderContext.d.ts.map +0 -1
  80. package/dist-package/src/Slider/SliderContext.js +0 -38
  81. package/dist-package/src/Slider/SliderContext.js.map +0 -1
  82. package/dist-package/src/Slider/styles.d.ts +0 -4
  83. package/dist-package/src/Slider/styles.d.ts.map +0 -1
  84. package/dist-package/src/Slider/styles.js +0 -68
  85. package/dist-package/src/Slider/styles.js.map +0 -1
  86. package/src/Slider/SliderContext.tsx +0 -80
  87. package/src/Slider/story/CustomTooltip.example.tsx +0 -36
  88. package/src/Slider/styles.ts +0 -71
@@ -0,0 +1,32 @@
1
+ /**
2
+ * if we have range slider, there is a chance
3
+ * when we select values next to each other
4
+ * that the lables might overlap each other. Like this example:
5
+ * [ [ ] ]
6
+ * A B
7
+ *
8
+ * In that case we need to reposition the labels to the edges of the thumb.
9
+ * [ ] [ ]
10
+ * A B
11
+ *
12
+ * In next rerender when we recalculate the position of the labels the
13
+ * above example would end with result as not overlapping as the position was already changed.
14
+ * So we need to work with the previous result of this function `previousResult`. When
15
+ * true we need to add half of the width of the label to simulate the center position.
16
+ *
17
+ * This function checks and returns if the labels overlap.
18
+ * It is called on each render of the component and everytime the Slider changes value.
19
+ **/
20
+ export const checkOverlap = ({ firstLabelRect, secondLabelRect, previousResult, }) => {
21
+ const gap = 16;
22
+ const halfWidth1 = firstLabelRect.width / 2;
23
+ const halfWidth2 = secondLabelRect.width / 2;
24
+ const rightBoundaryOfFirstLabel = previousResult
25
+ ? firstLabelRect.right + halfWidth1
26
+ : firstLabelRect.right;
27
+ const leftBoundaryOfSecondLabel = previousResult
28
+ ? secondLabelRect.left - halfWidth2
29
+ : secondLabelRect.left;
30
+ return rightBoundaryOfFirstLabel + gap > leftBoundaryOfSecondLabel;
31
+ };
32
+ //# sourceMappingURL=check-overlap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-overlap.js","sourceRoot":"","sources":["../../../src/utils/check-overlap.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;IAkBI;AACJ,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAC3B,cAAc,EACd,eAAe,EACf,cAAc,GACI,EAAW,EAAE;IAC/B,MAAM,GAAG,GAAG,EAAE,CAAA;IACd,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,GAAG,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,GAAG,CAAC,CAAA;IAE5C,MAAM,yBAAyB,GAAG,cAAc;QAC9C,CAAC,CAAC,cAAc,CAAC,KAAK,GAAG,UAAU;QACnC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAA;IACxB,MAAM,yBAAyB,GAAG,cAAc;QAC9C,CAAC,CAAC,eAAe,CAAC,IAAI,GAAG,UAAU;QACnC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAA;IAExB,OAAO,yBAAyB,GAAG,GAAG,GAAG,yBAAyB,CAAA;AACpE,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ export declare type GetPositionProps = {
2
+ rect: DOMRect;
3
+ isOverlaped: boolean;
4
+ isFirstLabel: boolean;
5
+ currentPlacement: 'left' | 'right' | 'center';
6
+ };
7
+ export declare const getXPlacement: ({ rect: { width, left, right }, isOverlaped, isFirstLabel, currentPlacement, }: GetPositionProps) => 'left' | 'right' | 'center';
8
+ //# sourceMappingURL=get-x-placement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-x-placement.d.ts","sourceRoot":"","sources":["../../../src/utils/get-x-placement.ts"],"names":[],"mappings":"AAAA,oBAAY,gBAAgB,GAAG;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;IACrB,gBAAgB,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;CAC9C,CAAA;AAED,eAAO,MAAM,aAAa,mFAKvB,gBAAgB,KAAG,MAAM,GAAG,OAAO,GAAG,QAexC,CAAA"}
@@ -0,0 +1,17 @@
1
+ export const getXPlacement = ({ rect: { width, left, right }, isOverlaped, isFirstLabel, currentPlacement, }) => {
2
+ const gap = 16;
3
+ const halfWidth = width / 2;
4
+ const leftBoundary = currentPlacement === 'right' ? left - halfWidth : left;
5
+ const rightBoundary = currentPlacement === 'left' ? right + halfWidth : right;
6
+ if (leftBoundary < gap) {
7
+ return 'right';
8
+ }
9
+ else if (rightBoundary > window.innerWidth - gap) {
10
+ return 'left';
11
+ }
12
+ else if (isOverlaped) {
13
+ return isFirstLabel ? 'left' : 'right';
14
+ }
15
+ return 'center';
16
+ };
17
+ //# sourceMappingURL=get-x-placement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-x-placement.js","sourceRoot":"","sources":["../../../src/utils/get-x-placement.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAC5B,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAC5B,WAAW,EACX,YAAY,EACZ,gBAAgB,GACC,EAA+B,EAAE;IAClD,MAAM,GAAG,GAAG,EAAE,CAAA;IACd,MAAM,SAAS,GAAG,KAAK,GAAG,CAAC,CAAA;IAC3B,MAAM,YAAY,GAAG,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3E,MAAM,aAAa,GAAG,gBAAgB,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAA;IAE7E,IAAI,YAAY,GAAG,GAAG,EAAE;QACtB,OAAO,OAAO,CAAA;KACf;SAAM,IAAI,aAAa,GAAG,MAAM,CAAC,UAAU,GAAG,GAAG,EAAE;QAClD,OAAO,MAAM,CAAA;KACd;SAAM,IAAI,WAAW,EAAE;QACtB,OAAO,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;KACvC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './check-bg-color';
2
+ export * from './check-overlap';
3
+ export * from './get-x-placement';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './check-bg-color';
2
+ export * from './check-overlap';
3
+ export * from './get-x-placement';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/picasso-slider",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "Toptal UI components library - Slider",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,18 +22,16 @@
22
22
  },
23
23
  "homepage": "https://github.com/toptal/picasso/tree/master/packages/picasso#readme",
24
24
  "dependencies": {
25
+ "@mui/base": "5.0.0-beta.40",
25
26
  "@toptal/picasso-shared": "15.0.0",
26
27
  "@toptal/picasso-tooltip": "1.1.3",
27
28
  "@toptal/picasso-utils": "1.0.3",
28
- "classnames": "^2.5.1"
29
+ "tailwind-merge": "^2.2.2"
29
30
  },
30
- "sideEffects": [
31
- "**/styles.ts",
32
- "**/styles.js"
33
- ],
31
+ "sideEffects": false,
34
32
  "peerDependencies": {
35
- "@material-ui/core": "4.12.4",
36
33
  "@toptal/picasso-provider": "*",
34
+ "@toptal/picasso-tailwind": ">2.1.0",
37
35
  "react": ">=16.12.0 < 19.0.0"
38
36
  },
39
37
  "exports": {
@@ -1,75 +1,59 @@
1
- import type { ChangeEvent, ComponentProps } from 'react'
2
- import React, { forwardRef, useRef, useMemo } from 'react'
3
- import type { Theme } from '@material-ui/core/styles'
4
- import { makeStyles } from '@material-ui/core/styles'
5
- import type { ValueLabelProps as MUIValueLabelProps } from '@material-ui/core'
6
- import { Slider as MUISlider } from '@material-ui/core'
7
- import cx from 'classnames'
8
- import { useCombinedRefs } from '@toptal/picasso-utils'
1
+ // import type { ComponentProps } from 'react'
2
+ import React, { forwardRef, useRef } from 'react'
3
+ import { Slider as MUIBaseSlider } from '@mui/base/Slider'
4
+ import { useCombinedRefs, useOnScreen } from '@toptal/picasso-utils'
5
+ import { twJoin, twMerge } from 'tailwind-merge'
6
+ import type { BaseProps } from '@toptal/picasso-shared'
9
7
 
10
- import type { ValueLabelProps } from '../SliderValueLabel'
11
- import { SliderValueLabel } from '../SliderValueLabel'
12
- import { SliderContextProvider } from './SliderContext'
13
- import styles from './styles'
8
+ import SliderMark from '../SliderMark'
9
+ import SliderValueLabel from '../SliderValueLabel'
10
+ import { useLabelOverlap } from './hooks'
14
11
 
15
- const useStyles = makeStyles<Theme>(styles)
16
-
17
- type Value = number | number[]
18
- type ValueLabelDisplay = 'on' | 'auto' | 'off'
19
-
20
- export interface Props extends ComponentProps<typeof MUISlider> {
12
+ export interface Props extends BaseProps {
21
13
  /** Minimum slider value */
22
14
  min?: number
23
15
  /** Maximum slider value */
24
16
  max?: number
25
17
  /** Controlled value of the component */
26
- value?: Value
27
- /** The default value. Use when the component is not controlled. */
28
- defaultValue?: Value
18
+ value?: number | number[]
19
+ /** The default value. Use when the component is not controlled */
20
+ defaultValue?: number | number[]
29
21
  /** Step for the thumb movement */
30
22
  step?: number
31
23
  /** Whether marks are shown or not */
32
24
  marks?: boolean
33
25
  /** Whether component is disabled or not */
34
26
  disabled?: boolean
35
- // Workaround for https://github.com/mui-org/material-ui/issues/21889
36
- /** The tooltip component. */
37
- TooltipComponent?: React.ElementType<ValueLabelProps & { index: number }>
38
27
  /** Controls when tooltip is displayed:
39
28
  - **auto** the value tooltip will display when the thumb is hovered or focused.
40
29
  - **on** will display persistently.
41
30
  - **off** will never display
42
31
  */
43
- tooltip?: ValueLabelDisplay
32
+ tooltip?: 'on' | 'auto' | 'off'
44
33
  /** The format function the value tooltip's value. */
45
34
  tooltipFormat?: string | ((value: number, index: number) => React.ReactNode)
46
- /** Show a compact tooltip */
47
- compact?: boolean
48
- /** Disable the portal behavior of the tooltip. The children stay within it's parent */
49
- disablePortal?: boolean
50
35
  /** Callback invoked when slider changes its state. */
51
- onChange?: (event: ChangeEvent<{}>, value: Value) => void
36
+ onChange?: (
37
+ event: Event,
38
+ value: number | number[],
39
+ activeThumb: number
40
+ ) => void
41
+ /** Callback invoked on focus */
42
+ onFocus?: (event: React.FocusEvent<HTMLElement>) => void
43
+ /** Callback invoked on blur */
44
+ onBlur?: (event: React.FocusEvent<HTMLElement>) => void
52
45
  /** Hide thumb when value is undefined or null. Works only when the component is controlled. */
53
46
  hideThumbOnEmpty?: boolean
54
47
  /** Disable track highlight. */
55
48
  disableTrackHighlight?: boolean
56
- }
57
-
58
- const createDefaultValueLabelComponent = (
59
- tooltip?: ValueLabelDisplay,
60
- disablePortal?: boolean,
61
- compact?: boolean
62
- ) => {
63
- const ValueLableComponent = (props: ValueLabelProps) => (
64
- <SliderValueLabel
65
- {...props}
66
- tooltip={tooltip}
67
- disablePortal={disablePortal}
68
- compact={compact}
69
- />
70
- )
71
-
72
- return ValueLableComponent
49
+ /**
50
+ * Name attribute of the `input` element.
51
+ */
52
+ name?: string
53
+ /**
54
+ * Id attribute of the `input` element.
55
+ */
56
+ id?: string
73
57
  }
74
58
 
75
59
  export const Slider = forwardRef<HTMLElement, Props>(function Slider(
@@ -82,73 +66,103 @@ export const Slider = forwardRef<HTMLElement, Props>(function Slider(
82
66
  marks,
83
67
  value,
84
68
  defaultValue = 0,
85
- tooltip,
69
+ tooltip = 'off',
86
70
  tooltipFormat,
87
- compact,
88
- TooltipComponent: UserDefinedTooltip,
89
71
  step,
90
72
  disabled,
91
- disablePortal,
92
73
  onChange,
74
+ onBlur,
75
+ onFocus,
93
76
  hideThumbOnEmpty,
94
77
  disableTrackHighlight,
95
- ...rest
78
+ className,
79
+ style,
80
+ name,
81
+ id,
82
+ 'data-private': dataPrivate,
83
+ 'data-testid': dataTestid,
96
84
  } = props
97
- const {
98
- wrapper,
99
- markTrack,
100
- hideThumb,
101
- markInactive,
102
- unmarkTrack,
103
- ...classes
104
- } = useStyles()
85
+ const containerRef = useRef<HTMLDivElement>(null)
105
86
  const sliderRef = useCombinedRefs<HTMLElement>(ref, useRef<HTMLElement>(null))
87
+ const { isPartiallyOverlapped, handleValueLabelOnRender } = useLabelOverlap({
88
+ value,
89
+ })
106
90
 
107
91
  const isThumbHidden =
108
92
  hideThumbOnEmpty && (typeof value === 'undefined' || value === null)
109
93
 
110
- const DefaultValueLabelComponent = useMemo(
111
- () => createDefaultValueLabelComponent(tooltip, disablePortal, compact),
112
- [tooltip, disablePortal, compact]
113
- )
114
-
115
- // From Workaround for https://github.com/mui-org/material-ui/issues/21889
116
- const ValueLabelComponent = (UserDefinedTooltip ||
117
- DefaultValueLabelComponent) as unknown as React.ElementType<MUIValueLabelProps>
94
+ // The rootMargin is not working correctly in the storybooks iframe
95
+ // To test properly we can open the iframe in new window
96
+ const isContainerOnScreen = useOnScreen({
97
+ ref: containerRef,
98
+ rootMargin: '-24px 0px 0px 0px',
99
+ threshold: 1,
100
+ })
118
101
 
119
102
  return (
120
- <SliderContextProvider>
121
- <div className={wrapper}>
122
- <MUISlider
123
- {...rest}
124
- ref={sliderRef}
125
- defaultValue={defaultValue}
126
- value={value}
127
- min={min}
128
- max={max}
129
- step={step}
130
- marks={marks}
131
- disabled={disabled}
132
- classes={{
133
- ...classes,
134
- track: cx(classes.track, {
135
- [markTrack]: marks,
136
- [unmarkTrack]: disableTrackHighlight,
137
- }),
138
- thumb: cx(classes.thumb, {
139
- [hideThumb]: isThumbHidden,
140
- }),
141
- markActive: cx(classes.markActive, {
142
- [markInactive]: isThumbHidden || disableTrackHighlight,
143
- }),
144
- }}
145
- ValueLabelComponent={ValueLabelComponent}
146
- valueLabelFormat={tooltipFormat}
147
- valueLabelDisplay={tooltip}
148
- onChange={onChange}
149
- />
150
- </div>
151
- </SliderContextProvider>
103
+ <div
104
+ ref={containerRef}
105
+ className={twMerge('my-[6px] mx-0', className)}
106
+ style={style}
107
+ >
108
+ <MUIBaseSlider
109
+ ref={sliderRef}
110
+ defaultValue={defaultValue}
111
+ value={value}
112
+ min={min}
113
+ max={max}
114
+ step={step}
115
+ marks={marks}
116
+ disabled={disabled}
117
+ data-testid={dataTestid}
118
+ data-private={dataPrivate}
119
+ onFocus={onFocus}
120
+ onBlur={onBlur}
121
+ name={name}
122
+ id={id}
123
+ slots={{
124
+ mark: SliderMark,
125
+ valueLabel: SliderValueLabel,
126
+ }}
127
+ slotProps={{
128
+ mark: {
129
+ // @ts-expect-error we have custom Mark component, where we extend props and MUI does not understand it
130
+ forceInactive: disableTrackHighlight,
131
+ },
132
+ root: {
133
+ className:
134
+ 'block cursor-pointer width-full relative py-[6px] -my-[6px]',
135
+ },
136
+ rail: {
137
+ className:
138
+ 'block absolute w-full h-[1px] opacity-[0.24] rounded-none bg-gray-500',
139
+ },
140
+ thumb: {
141
+ className: twJoin(
142
+ 'group/thumb flex justify-center items-center w-[15px] h-[15px]',
143
+ 'rounded-[50%] bg-blue-500 border-[2px] border-solid border-white',
144
+ '-mt-[7px] -ml-[6px] outline-0 absolute transition-shadow cursor-pointer',
145
+ isThumbHidden && 'hidden'
146
+ ),
147
+ role: 'slider',
148
+ },
149
+ track: {
150
+ className: twJoin(
151
+ 'block absolute h-[1px]',
152
+ disableTrackHighlight ? 'bg-gray-200' : 'bg-blue-500'
153
+ ),
154
+ },
155
+ valueLabel: {
156
+ tooltip,
157
+ onRender: handleValueLabelOnRender,
158
+ yPlacement: isContainerOnScreen ? 'top' : 'bottom',
159
+ isOverlaped: isPartiallyOverlapped,
160
+ },
161
+ }}
162
+ valueLabelFormat={tooltipFormat}
163
+ onChange={onChange}
164
+ />
165
+ </div>
152
166
  )
153
167
  })
154
168
 
@@ -159,7 +173,6 @@ Slider.defaultProps = {
159
173
  min: 0,
160
174
  max: 100,
161
175
  tooltip: 'off',
162
- disablePortal: false,
163
176
  }
164
177
 
165
178
  export default Slider
@@ -3,33 +3,47 @@
3
3
  exports[`Slider renders 1`] = `
4
4
  <div>
5
5
  <div
6
- class="makeStyles-wrapper"
6
+ class="my-[6px] mx-0"
7
7
  >
8
8
  <span
9
- class="MuiSlider-root makeStyles-root MuiSlider-colorPrimary"
9
+ class="base-Slider block cursor-pointer width-full relative py-[6px] -my"
10
10
  >
11
11
  <span
12
- class="MuiSlider-rail makeStyles-rail"
12
+ class="base-Slider block absolute w-full h-[1px] opacity-[0.24] rounded-none bg-gray"
13
13
  />
14
14
  <span
15
- class="MuiSlider-track makeStyles-track"
15
+ class="base-Slider block absolute h-[1px] bg-blue"
16
16
  style="left: 0%; width: 0%;"
17
17
  />
18
- <input
19
- type="hidden"
20
- value="0"
21
- />
22
18
  <span
23
- aria-orientation="horizontal"
24
- aria-valuemax="100"
25
- aria-valuemin="0"
26
- aria-valuenow="0"
27
- class="MuiSlider-thumb makeStyles-thumb MuiSlider-thumbColorPrimary"
19
+ class="base-Slider group/thumb flex justify-center items-center w-[15px] h-[15px] rounded-[50%] bg-blue border-[2px] border-solid border-white -mt -ml outline-0 absolute transition-shadow cursor-pointer"
28
20
  data-index="0"
29
21
  role="slider"
30
22
  style="left: 0%;"
31
- tabindex="0"
32
- />
23
+ >
24
+ <input
25
+ aria-orientation="horizontal"
26
+ aria-valuemax="100"
27
+ aria-valuemin="0"
28
+ aria-valuenow="0"
29
+ data-index="0"
30
+ max="100"
31
+ min="0"
32
+ step="1"
33
+ style="border: 0px; height: 100%; margin: -1px; overflow: hidden; padding: 0px; position: absolute; white-space: nowrap; width: 100%; direction: ltr;"
34
+ type="range"
35
+ value="0"
36
+ />
37
+ <span
38
+ class="absolute will-change transition-transform hidden top-[calc(100%+2px)] left-[calc(100%-13px)]"
39
+ >
40
+ <span
41
+ class="shadow-4 text-sm text-white bg-graphite m-1 rounded-sm py-[2px] px-2 max-w break-words"
42
+ >
43
+ 0
44
+ </span>
45
+ </span>
46
+ </span>
33
47
  </span>
34
48
  </div>
35
49
  </div>
@@ -38,33 +52,47 @@ exports[`Slider renders 1`] = `
38
52
  exports[`Slider with initial value 1`] = `
39
53
  <div>
40
54
  <div
41
- class="makeStyles-wrapper"
55
+ class="my-[6px] mx-0"
42
56
  >
43
57
  <span
44
- class="MuiSlider-root makeStyles-root MuiSlider-colorPrimary"
58
+ class="base-Slider block cursor-pointer width-full relative py-[6px] -my"
45
59
  >
46
60
  <span
47
- class="MuiSlider-rail makeStyles-rail"
61
+ class="base-Slider block absolute w-full h-[1px] opacity-[0.24] rounded-none bg-gray"
48
62
  />
49
63
  <span
50
- class="MuiSlider-track makeStyles-track"
64
+ class="base-Slider block absolute h-[1px] bg-blue"
51
65
  style="left: 0%; width: 4%;"
52
66
  />
53
- <input
54
- type="hidden"
55
- value="4"
56
- />
57
67
  <span
58
- aria-orientation="horizontal"
59
- aria-valuemax="100"
60
- aria-valuemin="0"
61
- aria-valuenow="4"
62
- class="MuiSlider-thumb makeStyles-thumb MuiSlider-thumbColorPrimary"
68
+ class="base-Slider group/thumb flex justify-center items-center w-[15px] h-[15px] rounded-[50%] bg-blue border-[2px] border-solid border-white -mt -ml outline-0 absolute transition-shadow cursor-pointer"
63
69
  data-index="0"
64
70
  role="slider"
65
71
  style="left: 4%;"
66
- tabindex="0"
67
- />
72
+ >
73
+ <input
74
+ aria-orientation="horizontal"
75
+ aria-valuemax="100"
76
+ aria-valuemin="0"
77
+ aria-valuenow="4"
78
+ data-index="0"
79
+ max="100"
80
+ min="0"
81
+ step="1"
82
+ style="border: 0px; height: 100%; margin: -1px; overflow: hidden; padding: 0px; position: absolute; white-space: nowrap; width: 100%; direction: ltr;"
83
+ type="range"
84
+ value="4"
85
+ />
86
+ <span
87
+ class="absolute will-change transition-transform hidden top-[calc(100%+2px)] left-[calc(100%-13px)]"
88
+ >
89
+ <span
90
+ class="shadow-4 text-sm text-white bg-graphite m-1 rounded-sm py-[2px] px-2 max-w break-words"
91
+ >
92
+ 4
93
+ </span>
94
+ </span>
95
+ </span>
68
96
  </span>
69
97
  </div>
70
98
  </div>
@@ -0,0 +1 @@
1
+ export * from './use-label-overlap'
@@ -0,0 +1,48 @@
1
+ import type { RefObject } from 'react'
2
+ import { useCallback, useEffect, useState } from 'react'
3
+
4
+ import { checkOverlap } from '../../utils'
5
+
6
+ export const useLabelOverlap = ({ value }: { value?: number | number[] }) => {
7
+ const [isPartiallyOverlapped, setIsPartiallyOverlapped] = useState(false)
8
+ const [valueLabels, setValueLabels] = useState<RefObject<HTMLSpanElement>[]>(
9
+ []
10
+ )
11
+ const isRangeSlider = Array.isArray(value)
12
+
13
+ useEffect(() => {
14
+ if (!isRangeSlider) {
15
+ return
16
+ }
17
+ const isFullyOverlaped = value[0] === value[1]
18
+
19
+ if (isFullyOverlaped) {
20
+ setIsPartiallyOverlapped(false)
21
+ } else {
22
+ if (!(valueLabels[0]?.current && valueLabels[1]?.current)) {
23
+ return
24
+ }
25
+
26
+ setIsPartiallyOverlapped(
27
+ checkOverlap({
28
+ firstLabelRect: valueLabels[0].current.getBoundingClientRect(),
29
+ secondLabelRect: valueLabels[1].current.getBoundingClientRect(),
30
+ previousResult: isPartiallyOverlapped,
31
+ })
32
+ )
33
+ }
34
+ }, [value, isRangeSlider, isPartiallyOverlapped, valueLabels])
35
+
36
+ const handleValueLabelOnRender = useCallback(
37
+ (index: number, labelRef: RefObject<HTMLSpanElement>) => {
38
+ setValueLabels(valLabels => {
39
+ valLabels[index] = labelRef
40
+
41
+ return valLabels
42
+ })
43
+ },
44
+ [setValueLabels]
45
+ )
46
+
47
+ return { isPartiallyOverlapped, handleValueLabelOnRender }
48
+ }
@@ -3,5 +3,4 @@ import type { OmitInternalProps } from '@toptal/picasso-shared'
3
3
  import type { Props } from './Slider'
4
4
 
5
5
  export { default as Slider } from './Slider'
6
- export { useSliderContext } from './SliderContext'
7
6
  export type SliderProps = OmitInternalProps<Props>
@@ -14,7 +14,7 @@ const Example = () => {
14
14
  <Grid.Item sm={6}>
15
15
  <Grid alignItems='center'>
16
16
  <Grid.Item sm>
17
- <Slider value={value} onChange={handleChange} max={100} compact />
17
+ <Slider value={value} onChange={handleChange} max={100} />
18
18
  </Grid.Item>
19
19
  <Grid.Item>
20
20
  <Typography size='medium'>{value}</Typography>
@@ -1,16 +1,25 @@
1
- import React from 'react'
1
+ import React, { useState } from 'react'
2
2
  import { Container, Slider } from '@toptal/picasso'
3
3
 
4
4
  type Value = number | number[]
5
5
 
6
6
  const Example = () => {
7
- const handleChange = (_: React.ChangeEvent<{}>, value: Value) => {
8
- window.console.log('onChange: ', value)
7
+ const [value, setValue] = useState<Value>(10)
8
+ const handleChange = (_: React.ChangeEvent<{}>, val: Value) => {
9
+ window.console.log('onChange: ', val)
10
+ setValue(val)
9
11
  }
10
12
 
11
13
  return (
12
14
  <Container>
13
- <Slider step={10} marks min={10} max={110} onChange={handleChange} />
15
+ <Slider
16
+ value={value}
17
+ step={10}
18
+ marks
19
+ min={10}
20
+ max={110}
21
+ onChange={handleChange}
22
+ />
14
23
  </Container>
15
24
  )
16
25
  }
@@ -32,7 +32,6 @@ const Example = () => {
32
32
  onChange={handleChange}
33
33
  tooltip='on'
34
34
  tooltipFormat={renderLabel}
35
- compact
36
35
  />
37
36
  </Container>
38
37
  </Container>
@@ -36,7 +36,6 @@ const Example = () => {
36
36
  onChange={handleChange}
37
37
  tooltip='on'
38
38
  tooltipFormat={renderLabel}
39
- compact
40
39
  />
41
40
  </Grid.Item>
42
41
  <Grid.Item sm={1}>