@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
@@ -1,14 +1,29 @@
1
- import React from 'react'
1
+ import React, { useState } from 'react'
2
2
  import { Container, Slider, Typography } from '@toptal/picasso'
3
3
  import { SPACING_4, SPACING_8, SPACING_10 } from '@toptal/picasso-utils'
4
4
 
5
- const formatLabel = (val: any) => {
6
- const formattedVal = val.length === 2 ? val : '0' + val
5
+ type Value = number
6
+
7
+ const formatLabel = (value: Value) => {
8
+ const formattedVal = String(value).padStart(2, '0')
7
9
 
8
10
  return <Typography color='inherit'>GMT+{formattedVal}:00</Typography>
9
11
  }
10
12
 
11
13
  const Example = () => {
14
+ const [value1, setValue1] = useState(0)
15
+ const [value2, setValue2] = useState(0)
16
+ const [value3, setValue3] = useState(0)
17
+ const handleChange1 = (_: React.ChangeEvent<{}>, newValue: Value) => {
18
+ setValue1(newValue)
19
+ }
20
+ const handleChange2 = (_: React.ChangeEvent<{}>, newValue: Value) => {
21
+ setValue2(newValue)
22
+ }
23
+ const handleChange3 = (_: React.ChangeEvent<{}>, newValue: Value) => {
24
+ setValue3(newValue)
25
+ }
26
+
12
27
  return (
13
28
  <Container padded={SPACING_4}>
14
29
  <Container>
@@ -16,7 +31,7 @@ const Example = () => {
16
31
  Display persistently
17
32
  </Typography>
18
33
  <Container top={SPACING_8}>
19
- <Slider tooltip='on' compact />
34
+ <Slider value={value1} onChange={handleChange1} tooltip='on' />
20
35
  </Container>
21
36
  </Container>
22
37
  <Container top={SPACING_8}>
@@ -24,7 +39,7 @@ const Example = () => {
24
39
  Display when the thumb is hovered or focused
25
40
  </Typography>
26
41
  <Container top={SPACING_8}>
27
- <Slider tooltip='auto' compact />
42
+ <Slider value={value2} onChange={handleChange2} tooltip='auto' />
28
43
  </Container>
29
44
  </Container>
30
45
  <Container top={SPACING_8}>
@@ -36,8 +51,9 @@ const Example = () => {
36
51
  min={0}
37
52
  max={23}
38
53
  tooltip='on'
54
+ value={value3}
55
+ onChange={handleChange3}
39
56
  tooltipFormat={formatLabel}
40
- compact
41
57
  />
42
58
  </Container>
43
59
  </Container>
@@ -53,14 +53,6 @@ page
53
53
  'base/Slider'
54
54
  )
55
55
  .addExample('Slider/story/Marks.example.tsx', 'Marks', 'base/Slider')
56
- .addExample(
57
- 'Slider/story/CustomTooltip.example.tsx',
58
- {
59
- title: 'Custom Tooltip',
60
- takeScreenshot: false,
61
- },
62
- 'base/Slider'
63
- )
64
56
  .addExample(
65
57
  'Slider/story/HideThumb.example.tsx',
66
58
  'Hide thumb when value is null or undefined',
@@ -1,9 +1,15 @@
1
1
  import type { ReactNode } from 'react'
2
+ import { describe, expect, it } from '@jest/globals'
2
3
  import React from 'react'
3
4
  import type { RenderResult } from '@testing-library/react'
4
5
  import { render } from '@testing-library/react'
5
6
  import type { OmitInternalProps } from '@toptal/picasso-shared'
6
7
 
8
+ jest.mock('@toptal/picasso-utils', () => ({
9
+ ...jest.requireActual('@toptal/picasso-utils'),
10
+ useOnScreen: jest.fn().mockRejectedValueOnce(true),
11
+ }))
12
+
7
13
  import type { Props } from './Slider'
8
14
  import { Slider } from './Slider'
9
15
 
@@ -0,0 +1,35 @@
1
+ import React from 'react'
2
+ import { twJoin } from 'tailwind-merge'
3
+
4
+ import { getBgColor } from '../utils'
5
+
6
+ export type SliderMarkProps = {
7
+ markActive: boolean
8
+ ownerState: { value: number }
9
+ style: React.CSSProperties
10
+ 'data-index': number
11
+ forceInactive: boolean
12
+ }
13
+
14
+ // We need custom Mark component because we have
15
+ // different bg color based on value of the Slider
16
+ const SliderMark = ({
17
+ markActive,
18
+ ownerState,
19
+ 'data-index': dataIndex,
20
+ style,
21
+ forceInactive,
22
+ }: SliderMarkProps) => {
23
+ return (
24
+ <span
25
+ data-index={dataIndex}
26
+ style={style}
27
+ className={twJoin(
28
+ 'absolute w-[6px] h-[6px] rounded-[50%] border-[2px] top-[1.5px] border-solid border-white opacity-100 -translate-x-2/4 box-content',
29
+ getBgColor({ markActive, forceInactive, value: ownerState.value })
30
+ )}
31
+ />
32
+ )
33
+ }
34
+
35
+ export default SliderMark
@@ -0,0 +1 @@
1
+ export { default } from './SliderMark'
@@ -1,73 +1,93 @@
1
- import React, { useRef } from 'react'
2
- import type { ValueLabelProps as MUIValueLabelProps } from '@material-ui/core/Slider'
3
- import { Tooltip } from '@toptal/picasso-tooltip'
1
+ import type { SliderValueLabelSlotProps } from '@mui/base/Slider'
2
+ import type { RefObject } from 'react'
3
+ import React, { useEffect, useRef, useState } from 'react'
4
+ import { twJoin } from 'tailwind-merge'
4
5
 
5
- import { useSliderContext } from '../Slider'
6
+ import { getXPlacement } from '../utils'
6
7
 
7
8
  type ValueLabelDisplay = 'on' | 'auto' | 'off'
8
9
 
9
- // This type is needed because ValueLabelProps does not describe all exposed props
10
- export type ValueLabelProps = MUIValueLabelProps & {
11
- valueLabelDisplay: ValueLabelDisplay
12
- index: number
10
+ const classesByTooltip: Record<ValueLabelDisplay, string> = {
11
+ off: 'hidden',
12
+ // We need to use visibility: hidden instead of display: none to keep the
13
+ // label visible for javascript calculations.
14
+ auto: 'invisible group-hover/thumb:visible flex justify-center items-center',
15
+ on: 'flex justify-center items-center',
13
16
  }
14
17
 
15
- export interface Props extends ValueLabelProps {
16
- tooltip?: ValueLabelDisplay
17
- disablePortal?: boolean
18
- compact?: boolean
19
- valueLabelDisplay: ValueLabelDisplay
20
- index: number
21
- }
18
+ const xPlacementClasses = {
19
+ left: 'right-[calc(100%-13px)]',
20
+ right: 'left-[calc(100%-13px)]',
21
+ center: '',
22
+ } as const
23
+
24
+ const yPlacementClasses = {
25
+ bottom: 'top-[calc(100%+2px)]',
26
+ top: 'bottom-[calc(100%+2px)]',
27
+ } as const
22
28
 
23
29
  const SliderValueLabel = ({
24
- tooltip,
25
- disablePortal,
26
- compact,
27
30
  children,
28
- open,
29
- value,
30
- valueLabelDisplay,
31
- index,
32
- }: Props) => {
33
- const thumbRef = useRef<HTMLDivElement>(null)
34
- const { registerValueLabel, hasTooltipOverlow } = useSliderContext()
35
- const isTooltipAlwaysVisible = tooltip === 'on'
31
+ index = -1,
32
+ tooltip = 'off',
33
+ onRender,
34
+ yPlacement,
35
+ isOverlaped,
36
+ ownerState: { value },
37
+ }: SliderValueLabelSlotProps & {
38
+ tooltip: ValueLabelDisplay
39
+ yPlacement: 'top' | 'bottom'
40
+ /** indicates if there are two SliderValueLabels that overlap each other */
41
+ isOverlaped: boolean
42
+ onRender: (index: number, ref: RefObject<HTMLSpanElement>) => void
43
+ }) => {
44
+ const ref = useRef<HTMLSpanElement>(null)
36
45
 
37
- if (valueLabelDisplay === 'off') {
38
- return children
39
- }
40
-
41
- const getPlacement = () => {
42
- if (hasTooltipOverlow) {
43
- return index === 0 ? 'top-end' : 'top-start'
44
- }
45
-
46
- return 'top'
47
- }
46
+ // we need to change the placement of the label if it is overlaped
47
+ // or if it is out of the viewport
48
+ const [xPlacement, setXPlacement] = useState<'left' | 'right' | 'center'>(
49
+ 'center'
50
+ )
48
51
 
49
- const handleTooltipRef = (tooltipElement: HTMLDivElement) => {
50
- // At this moment, both thumb and tooltip refs are set so we can register them in the context
51
- const thumbElement = thumbRef.current
52
+ useEffect(() => {
53
+ onRender(index, ref)
54
+ }, [index, onRender])
52
55
 
53
- if (tooltipElement && thumbElement) {
54
- registerValueLabel(index, tooltipElement, thumbElement)
56
+ useEffect(() => {
57
+ if (!ref.current) {
58
+ return
55
59
  }
56
- }
60
+
61
+ setXPlacement(
62
+ getXPlacement({
63
+ rect: ref.current.getBoundingClientRect(),
64
+ isOverlaped: isOverlaped,
65
+ isFirstLabel: index === 0,
66
+ currentPlacement: xPlacement,
67
+ })
68
+ )
69
+ // we need to recalculate on value change to get new rect
70
+ }, [isOverlaped, index, xPlacement, value])
57
71
 
58
72
  return (
59
- <Tooltip
60
- ref={thumbRef}
61
- tooltipRef={handleTooltipRef}
62
- content={value}
63
- open={open || valueLabelDisplay === 'on'}
64
- placement={getPlacement()}
65
- preventOverflow={isTooltipAlwaysVisible}
66
- disablePortal={disablePortal}
67
- compact={compact}
73
+ <span
74
+ ref={ref}
75
+ className={twJoin(
76
+ 'absolute will-change-transform transition-transform',
77
+ classesByTooltip[tooltip],
78
+ yPlacementClasses[yPlacement],
79
+ xPlacementClasses[xPlacement]
80
+ )}
68
81
  >
69
- {children}
70
- </Tooltip>
82
+ <span
83
+ className={twJoin(
84
+ 'shadow-4 text-sm text-white bg-graphite-800',
85
+ 'm-1 rounded-sm py-[2px] px-2 max-w-[300px] break-words'
86
+ )}
87
+ >
88
+ {children}
89
+ </span>
90
+ </span>
71
91
  )
72
92
  }
73
93
 
@@ -1,5 +1 @@
1
- export { default as SliderValueLabel } from './SliderValueLabel'
2
- export type {
3
- Props as SliderValueLabelProps,
4
- ValueLabelProps,
5
- } from './SliderValueLabel'
1
+ export { default } from './SliderValueLabel'
package/src/index.ts CHANGED
@@ -1,2 +1 @@
1
1
  export * from './Slider'
2
- export * from './SliderValueLabel'
@@ -0,0 +1,92 @@
1
+ import { describe, expect, it } from '@jest/globals'
2
+
3
+ import { getBgColor } from './index'
4
+ import type { GetBgColorProps } from './index'
5
+
6
+ describe('getBgColor function', () => {
7
+ describe('when markActive is true, forceInactive is false, and value is not undefined', () => {
8
+ it('returns "bg-blue-500"', () => {
9
+ const options: GetBgColorProps = {
10
+ markActive: true,
11
+ forceInactive: false,
12
+ value: 0,
13
+ }
14
+ const bgColor = getBgColor(options)
15
+
16
+ expect(bgColor).toBe('bg-blue-500')
17
+ })
18
+ })
19
+
20
+ describe('when markActive is true, forceInactive is false, and value is undefined', () => {
21
+ it('returns "bg-gray-500"', () => {
22
+ const options: GetBgColorProps = {
23
+ markActive: true,
24
+ forceInactive: false,
25
+ value: undefined,
26
+ }
27
+ const bgColor = getBgColor(options)
28
+
29
+ expect(bgColor).toBe('bg-gray-500')
30
+ })
31
+ })
32
+
33
+ describe('when markActive is true, forceInactive is true', () => {
34
+ const options: GetBgColorProps = { markActive: true, forceInactive: true }
35
+
36
+ describe('and value is not undefined', () => {
37
+ it('returns "bg-gray-500"', () => {
38
+ const bgColor = getBgColor({ ...options, value: 10 })
39
+
40
+ expect(bgColor).toBe('bg-gray-500')
41
+ })
42
+ })
43
+
44
+ describe('and value is undefined', () => {
45
+ it('returns "bg-gray-500"', () => {
46
+ const bgColor = getBgColor({ ...options, value: undefined })
47
+
48
+ expect(bgColor).toBe('bg-gray-500')
49
+ })
50
+ })
51
+ })
52
+
53
+ describe('when markActive is false', () => {
54
+ const options: GetBgColorProps = { markActive: false }
55
+
56
+ describe('and forceInactive is false', () => {
57
+ it('returns "bg-gray-500" regardless of value', () => {
58
+ let bgColor = getBgColor({
59
+ ...options,
60
+ forceInactive: false,
61
+ value: 10,
62
+ })
63
+
64
+ expect(bgColor).toBe('bg-gray-500')
65
+
66
+ bgColor = getBgColor({
67
+ ...options,
68
+ forceInactive: false,
69
+ value: undefined,
70
+ })
71
+
72
+ expect(bgColor).toBe('bg-gray-500')
73
+ })
74
+ })
75
+
76
+ describe('and forceInactive is true', () => {
77
+ it('returns "bg-gray-500" regardless of value', () => {
78
+ let bgColor = getBgColor({ ...options, forceInactive: true, value: 10 })
79
+
80
+ expect(bgColor).toBe('bg-gray-500')
81
+
82
+ bgColor = getBgColor({
83
+ ...options,
84
+ forceInactive: true,
85
+ value: undefined,
86
+ })
87
+
88
+ expect(bgColor).toBe('bg-gray-500')
89
+ })
90
+ })
91
+ })
92
+ })
@@ -0,0 +1,28 @@
1
+ export type GetBgColorProps = {
2
+ markActive?: boolean
3
+ forceInactive?: boolean
4
+ value?: number | readonly number[]
5
+ }
6
+
7
+ export const getBgColor = ({
8
+ markActive,
9
+ forceInactive,
10
+ value,
11
+ }: GetBgColorProps) => {
12
+ const inactive = 'bg-gray-500'
13
+ const active = 'bg-blue-500'
14
+
15
+ if (forceInactive) {
16
+ return inactive
17
+ }
18
+
19
+ if (markActive) {
20
+ if (value === undefined) {
21
+ return inactive
22
+ }
23
+
24
+ return active
25
+ }
26
+
27
+ return inactive
28
+ }
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it } from '@jest/globals'
2
+
3
+ import { checkOverlap } from './check-overlap' // Update your checkOverlap import
4
+
5
+ describe('checkOverlap', () => {
6
+ describe('when labels do not overlap', () => {
7
+ it('returns false', () => {
8
+ const firstLabelRect = { right: 0, width: 20 } as DOMRect
9
+ const secondLabelRect = { left: 30, width: 20 } as DOMRect
10
+ const previousResult = false
11
+
12
+ expect(
13
+ checkOverlap({ firstLabelRect, secondLabelRect, previousResult })
14
+ ).toBe(false)
15
+ })
16
+ })
17
+
18
+ describe('when labels do overlap', () => {
19
+ it('returns true', () => {
20
+ const firstLabelRect = { right: 30, width: 20 } as DOMRect
21
+ const secondLabelRect = { left: 40, width: 20 } as DOMRect
22
+ const previousResult = false
23
+
24
+ expect(
25
+ checkOverlap({ firstLabelRect, secondLabelRect, previousResult })
26
+ ).toBe(true)
27
+ })
28
+ })
29
+
30
+ describe('when labels partially overlaped in previous render', () => {
31
+ describe('when the previousResult is true', () => {
32
+ it('they would still overlap in a non-overlapped state', () => {
33
+ const firstLabelRect = { right: 10, width: 20 } as DOMRect
34
+ const secondLabelRect = { left: 25, width: 20 } as DOMRect
35
+ const previousResult = true
36
+
37
+ expect(
38
+ checkOverlap({ firstLabelRect, secondLabelRect, previousResult })
39
+ ).toBe(true)
40
+ })
41
+ })
42
+
43
+ describe('when previousResult is false', () => {
44
+ it('would not overlap in a non-overlapped state', () => {
45
+ const firstLabelRect = { right: 10, width: 20 } as DOMRect
46
+ const secondLabelRect = { left: 30, width: 20 } as DOMRect
47
+ const previousResult = false
48
+
49
+ expect(
50
+ checkOverlap({ firstLabelRect, secondLabelRect, previousResult })
51
+ ).toBe(false)
52
+ })
53
+ })
54
+ })
55
+ })
@@ -0,0 +1,43 @@
1
+ export type CheckOverlapProps = {
2
+ firstLabelRect: DOMRect
3
+ secondLabelRect: DOMRect
4
+ previousResult: boolean
5
+ }
6
+
7
+ /**
8
+ * if we have range slider, there is a chance
9
+ * when we select values next to each other
10
+ * that the lables might overlap each other. Like this example:
11
+ * [ [ ] ]
12
+ * A B
13
+ *
14
+ * In that case we need to reposition the labels to the edges of the thumb.
15
+ * [ ] [ ]
16
+ * A B
17
+ *
18
+ * In next rerender when we recalculate the position of the labels the
19
+ * above example would end with result as not overlapping as the position was already changed.
20
+ * So we need to work with the previous result of this function `previousResult`. When
21
+ * true we need to add half of the width of the label to simulate the center position.
22
+ *
23
+ * This function checks and returns if the labels overlap.
24
+ * It is called on each render of the component and everytime the Slider changes value.
25
+ **/
26
+ export const checkOverlap = ({
27
+ firstLabelRect,
28
+ secondLabelRect,
29
+ previousResult,
30
+ }: CheckOverlapProps): boolean => {
31
+ const gap = 16
32
+ const halfWidth1 = firstLabelRect.width / 2
33
+ const halfWidth2 = secondLabelRect.width / 2
34
+
35
+ const rightBoundaryOfFirstLabel = previousResult
36
+ ? firstLabelRect.right + halfWidth1
37
+ : firstLabelRect.right
38
+ const leftBoundaryOfSecondLabel = previousResult
39
+ ? secondLabelRect.left - halfWidth2
40
+ : secondLabelRect.left
41
+
42
+ return rightBoundaryOfFirstLabel + gap > leftBoundaryOfSecondLabel
43
+ }
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from '@jest/globals'
2
+
3
+ import { getXPlacement } from './get-x-placement' // Update your getXPlacement import
4
+
5
+ describe('getXPlacement', () => {
6
+ describe('when leftBoundary < gap', () => {
7
+ it('returns right', () => {
8
+ const rect = { width: 20, left: -5, right: 0 } as DOMRect
9
+ const isOverlaped = false
10
+ const isFirstLabel = true
11
+ const currentPlacement = 'center'
12
+
13
+ expect(
14
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
15
+ ).toBe('right')
16
+ })
17
+ })
18
+
19
+ describe('when rightBoundary > window.innerWidth - gap', () => {
20
+ it('returns left', () => {
21
+ const rect = { width: 100, left: 920, right: 1020 } as DOMRect
22
+ const isOverlaped = false
23
+ const isFirstLabel = true
24
+ const currentPlacement = 'center'
25
+
26
+ expect(
27
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
28
+ ).toBe('left')
29
+ })
30
+ })
31
+
32
+ describe('when rightBoundary < window.innerWidth - gap', () => {
33
+ it('returns left', () => {
34
+ const rect = { width: 100, left: 870, right: 970 } as DOMRect
35
+ const isOverlaped = false
36
+ const isFirstLabel = true
37
+ const currentPlacement = 'center'
38
+
39
+ expect(
40
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
41
+ ).toBe('center')
42
+ })
43
+ })
44
+
45
+ describe('when currentPlacement is left and rightBoundary > window.innerWidth - gap', () => {
46
+ it('returns left', () => {
47
+ const rect = { width: 100, left: 870, right: 970 } as DOMRect
48
+ const isOverlaped = true
49
+ const isFirstLabel = false
50
+ const currentPlacement = 'left'
51
+
52
+ expect(
53
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
54
+ ).toBe('left')
55
+ })
56
+ })
57
+
58
+ describe('when labels are overlapped', () => {
59
+ const rect = { width: 20, left: 50, right: 70 } as DOMRect
60
+ const isOverlaped = true
61
+
62
+ it('returns left for the first label', () => {
63
+ const isFirstLabel = true
64
+ const currentPlacement = 'center'
65
+
66
+ expect(
67
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
68
+ ).toBe('left')
69
+ })
70
+
71
+ it('returns right for the not first label', () => {
72
+ const isFirstLabel = false
73
+ const currentPlacement = 'center'
74
+
75
+ expect(
76
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
77
+ ).toBe('right')
78
+ })
79
+ })
80
+
81
+ describe('when none of conditions are met', () => {
82
+ it('returns center', () => {
83
+ const rect = { width: 20, left: 100, right: 120 } as DOMRect
84
+ const isOverlaped = false
85
+ const isFirstLabel = false
86
+ const currentPlacement = 'center'
87
+
88
+ expect(
89
+ getXPlacement({ rect, isOverlaped, isFirstLabel, currentPlacement })
90
+ ).toBe('center')
91
+ })
92
+ })
93
+ })
@@ -0,0 +1,28 @@
1
+ export type GetPositionProps = {
2
+ rect: DOMRect
3
+ isOverlaped: boolean
4
+ isFirstLabel: boolean
5
+ currentPlacement: 'left' | 'right' | 'center'
6
+ }
7
+
8
+ export const getXPlacement = ({
9
+ rect: { width, left, right },
10
+ isOverlaped,
11
+ isFirstLabel,
12
+ currentPlacement,
13
+ }: GetPositionProps): 'left' | 'right' | 'center' => {
14
+ const gap = 16
15
+ const halfWidth = width / 2
16
+ const leftBoundary = currentPlacement === 'right' ? left - halfWidth : left
17
+ const rightBoundary = currentPlacement === 'left' ? right + halfWidth : right
18
+
19
+ if (leftBoundary < gap) {
20
+ return 'right'
21
+ } else if (rightBoundary > window.innerWidth - gap) {
22
+ return 'left'
23
+ } else if (isOverlaped) {
24
+ return isFirstLabel ? 'left' : 'right'
25
+ }
26
+
27
+ return 'center'
28
+ }
@@ -0,0 +1,3 @@
1
+ export * from './check-bg-color'
2
+ export * from './check-overlap'
3
+ export * from './get-x-placement'
@@ -1,11 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- interface ProviderProps {
3
- children: ReactNode;
4
- }
5
- declare const SliderContextProvider: ({ children }: ProviderProps) => JSX.Element;
6
- declare const useSliderContext: () => {
7
- registerValueLabel: (index: number, tooltip: HTMLDivElement, thumb: HTMLElement) => void;
8
- hasTooltipOverlow: boolean;
9
- };
10
- export { SliderContextProvider, useSliderContext };
11
- //# sourceMappingURL=SliderContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SliderContext.d.ts","sourceRoot":"","sources":["../../../src/Slider/SliderContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAsBtC,UAAU,aAAa;IACrB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAID,QAAA,MAAM,qBAAqB,iBAAkB,aAAa,gBA+CzD,CAAA;AAED,QAAA,MAAM,gBAAgB;gCAjEX,MAAM,WACJ,cAAc,SAChB,WAAW,KACf,IAAI;uBACU,OAAO;CA6D4B,CAAA;AAExD,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAA"}