Skip to content

Commit 26c13b8

Browse files
added dragging
1 parent 73fa483 commit 26c13b8

1 file changed

Lines changed: 100 additions & 11 deletions

File tree

src/index.tsx

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { CSSProperties, useState } from 'react';
2+
import interpolate from "color-interpolate";
23

34
interface ToggleSliderHandleProps extends CSSProperties {
45
size: number | string,
@@ -45,6 +46,8 @@ interface ToggleSliderProps {
4546
barRendererActive?: React.ReactNode,
4647
flip?: boolean,
4748
active?: boolean,
49+
draggable?: boolean,
50+
barTransitionType?: "fade" | "slide",
4851
}
4952

5053
function ToggleSlider({
@@ -72,13 +75,29 @@ function ToggleSlider({
7275
handleStyles,
7376
handleStylesActive,
7477
active: initialActive = false,
78+
draggable = true,
79+
barTransitionType = "fade",
7580
}: ToggleSliderProps) {
7681

7782
const [active, setActive] = useState(initialActive);
83+
const [progress, setProgressState] = useState((active && !flip || !active && flip) ? 1 : 0);
84+
const [currentBarColor, setCurrentBarColor] = useState(active ? barBackgroundColorActive : barBackgroundColor);
85+
const [dragging, setDragging] = useState(false);
86+
87+
function interpolateColors(start: string, end: string, pos: number) {
88+
const colorMap = interpolate([start, end]);
89+
return colorMap(pos);
90+
}
91+
92+
function setProgress(p: number) {
93+
const paddingLeft = Math.max(padding, Math.min(barWidth - padding - handleSize, p * barWidth - handleSize / 2));
94+
const paddingProgress = (paddingLeft - padding) / (barWidth - padding * 2 - handleSize);
95+
setProgressState(p);
96+
97+
if (barTransitionType === "fade") {
98+
setCurrentBarColor(interpolateColors(barBackgroundColor, barBackgroundColorActive, paddingProgress));
99+
}
78100

79-
function onClick() {
80-
onToggle(!active);
81-
setActive(!active);
82101
}
83102

84103
const overlayStyles: CSSProperties = {
@@ -90,33 +109,103 @@ function ToggleSlider({
90109
barStyles = active ? { ...barStyles, ...barStylesActive } : barStyles;
91110
handleStyles = active ? { ...handleStyles, ...handleStylesActive } : handleStyles;
92111

93-
94112
const bar = barRenderer ?? <ToggleSliderBar width={barWidth} height={barHeight}
95113
borderRadius={barBorderRadius}
96-
backgroundColor={active ? barBackgroundColorActive : barBackgroundColor}
97-
transition={`all ${barTransitionDuration ?? transitionDuration}`}
114+
background={currentBarColor}
115+
transition={`all ${dragging ? "0s" : (barTransitionDuration ?? transitionDuration)}`}
98116
{...barStyles}/>;
99117
const handle = handleRenderer ?? <ToggleSliderHandle size={handleSize}
100118
borderRadius={handleBorderRadius}
101119
backgroundColor={active ?
102120
(handleBackgroundColorActive ?? handleBackgroundColor) :
103121
handleBackgroundColor}
104-
transition={`all ${handleTransitionDuration ?? transitionDuration}`}
122+
transition={`all ${dragging ? "0s" : (handleTransitionDuration ?? transitionDuration)}`}
105123
{...handleStyles}/>;
106124
const barActive = barRendererActive ?? bar;
107125
const handleActive = handleRendererActive ?? handle;
108126

127+
const [down, setDown] = useState(false);
128+
129+
function onPointerDown(e: React.PointerEvent) {
130+
setDown(true);
131+
}
132+
133+
function onPointerMove(e: React.PointerEvent) {
134+
135+
if (!down || !draggable) {
136+
return;
137+
}
138+
139+
let localDragging = dragging;
140+
if (!dragging) {
141+
setDragging(true);
142+
localDragging = true;
143+
}
144+
145+
if (localDragging) {
146+
const bounds = e.currentTarget.getBoundingClientRect();
147+
const position = (e.clientX - bounds.left) / barWidth;
148+
setProgress(position);
149+
}
150+
151+
}
152+
153+
function onPointerUp() {
154+
155+
if (!down) {
156+
return;
157+
}
158+
159+
if (!dragging) {
160+
onToggle(!active);
161+
setProgress(active ? 0 : 1);
162+
setActive(!active);
163+
} else {
164+
const newActive = progress > 0.5;
165+
onToggle(newActive);
166+
setProgress(newActive ? 1 : 0);
167+
setActive(newActive);
168+
}
169+
setDown(false);
170+
setDragging(false)
171+
}
172+
109173
return (
110-
<div style={{ display: "flex", flexFlow: "row nowrap", cursor: "pointer", width: barWidth }} onClick={onClick}>
111-
<div style={{ paddingTop: Math.max(handleSize - barHeight, 0) / 2 ,transition: `all ${transitionDuration}`, ...overlayStyles }}>
174+
<div style={{ display: "flex", flexFlow: "row nowrap", cursor: "pointer", width: barWidth, userSelect: "none" }}
175+
onPointerUp={onPointerUp}
176+
onPointerDown={onPointerDown}
177+
onPointerMove={onPointerMove}
178+
onPointerLeave={onPointerUp}>
179+
<div style={{
180+
paddingTop: Math.max(handleSize - barHeight, 0) / 2, ...overlayStyles,
181+
}}>
112182
{active ? barActive : bar}
113183
</div>
184+
{barTransitionType === "slide" ? (
185+
<div style={{ marginLeft: "-100%", ...overlayStyles }}>
186+
<div style={{
187+
borderRadius: barBorderRadius,
188+
width: barWidth,
189+
height: barHeight,
190+
marginTop: Math.max(handleSize - barHeight, 0) / 2,
191+
// boxSizing: "border-box",
192+
overflow: "hidden",
193+
}}>
194+
<div style={{
195+
transition: `all ${dragging ? "0s" : transitionDuration}`,
196+
backgroundColor: barBackgroundColorActive,
197+
width: progress * barWidth,
198+
height: barHeight,
199+
}}/>
200+
</div>
201+
</div>
202+
) : undefined}
114203
<div style={{ marginLeft: "-100%", ...overlayStyles }}>
115204
<div
116205
style={{
117-
transition: `all ${transitionDuration}`,
206+
transition: `all ${dragging ? "0s" : transitionDuration}`,
118207
paddingTop: Math.max(barHeight - handleSize, 0) / 2,
119-
paddingLeft: ((active && !flip || !active && flip) ? barWidth - padding - handleSize : padding),
208+
paddingLeft: Math.max(padding, Math.min(barWidth - padding - handleSize, progress * barWidth - handleSize / 2)),
120209
}}>
121210
{active ? handleActive : handle}
122211
</div>

0 commit comments

Comments
 (0)