Skip to content

Commit

Permalink
fix(silder): fix slider range render style
Browse files Browse the repository at this point in the history
  • Loading branch information
slatejack committed Sep 14, 2024
1 parent 3543930 commit 150ef59
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 66 deletions.
114 changes: 54 additions & 60 deletions src/slider/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { FC, useEffect, useRef, useState } from 'react';
import type { MouseEvent, TouchEvent } from 'react';
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
import cloneDeep from 'lodash/cloneDeep';
import { usePrefixClass } from '../hooks/useClass';
import useDefaultProps from '../hooks/useDefaultProps';
import useDefault from '../_util/useDefault';
Expand All @@ -19,8 +20,6 @@ const Slider: FC<SliderProps> = (props) => {
const [scaleArray, setScaleArray] = useState<any[]>([]);
const [scaleTextArray, setScaleTextArray] = useState<any[]>([]);
const [isScale, setIsScale] = useState<boolean>(false);
const [initialLeft, setInitialLeft] = useState<number>(0);
const [initialRight, setInitialRight] = useState<number>(0);
const [maxRange, setMaxRange] = useState<number>(0);
const [dotTopValue, setDotTopValue] = useState<number[]>([0, 0]);
const [lineLeft, setLineLeft] = useState<number>();
Expand All @@ -30,6 +29,8 @@ const Slider: FC<SliderProps> = (props) => {
const leftDotRef = useRef<HTMLDivElement>(null);
const rightDotRef = useRef<HTMLDivElement>(null);
const sliderLineRef = useRef<HTMLDivElement>(null);
const initialLeft = useRef<number>(0);
const initialRight = useRef<number>(0);
const [innerValue, setInnerValue] = useDefault(value, defaultValue, onChange);
const scope = Number(max) - Number(min);

Expand All @@ -46,7 +47,9 @@ const Slider: FC<SliderProps> = (props) => {
const sliderMaxTextClassName = classNames(`${rootClassName}__value`, `${rootClassName}__value--max`);

useEffect(() => {
getInitialStyle(theme);
if (theme) {
getInitialStyle(theme);
}
}, [theme]);

useEffect(() => {
Expand All @@ -57,11 +60,10 @@ const Slider: FC<SliderProps> = (props) => {
}

function setLineStyle(left: number, right: number) {
const parseNumber = (v: any) => parseInt(v, 10);
const halfBlock = theme === 'capsule' ? BLOCK_SIZE / 2 : 0;
const [a, b] = innerValue as Array<number>;

const parseNumber = (v: any) => parseInt(v, 10);

setDotTopValue([a, b]);

if (left + right <= maxRange) {
Expand All @@ -77,8 +79,8 @@ const Slider: FC<SliderProps> = (props) => {
setSingleBarWidth(innerValue as number);
return;
}
const left = (maxRange * (innerValue[0] ?? 0 - min)) / scope;
const right = maxRange * (max - innerValue[1]);
const left = (maxRange * (innerValue[0] - min)) / scope;
const right = (maxRange * (max - innerValue[1])) / scope;
setLineStyle(left, right);
}, [innerValue, max, maxRange, min, range, scope, theme]);

Expand Down Expand Up @@ -116,22 +118,26 @@ const Slider: FC<SliderProps> = (props) => {
const halfBlock = Number(BLOCK_SIZE) / 2;
const maxRange = line.right - line.left;

setMaxRange(theme === 'capsule' ? maxRange - BLOCK_SIZE - BORDER_WIDTH : maxRange);
initialLeft.current = line.left;
initialRight.current = line.right;
if (theme === 'capsule') {
setMaxRange(maxRange - BLOCK_SIZE - BORDER_WIDTH); // 6 是边框宽度
setInitialLeft((initialLeft) => initialLeft - halfBlock);
setInitialRight((initialRight) => initialRight - halfBlock);
return;
initialLeft.current -= halfBlock;
initialRight.current -= halfBlock;
}
setMaxRange(maxRange);
setInitialLeft(line.left);
setInitialRight(line.right);
}

const getValue = (label: any, value: any) => {
const REGEXP = /[$][{value}]{7}/;
if (isFunction(label)) return label(value);
if (label && label === 'true') return value;
if (REGEXP.test(label)) return label.replace(REGEXP, value);
if (isFunction(label)) {
return label(value);
}
if (label) {
return value;
}
if (REGEXP.test(label)) {
return label.replace(REGEXP, value);
}
};

const convertPosToValue = (posValue: number, isLeft = true) =>
Expand All @@ -156,7 +162,7 @@ const Slider: FC<SliderProps> = (props) => {
};

const changeValue = (value: SliderValue) => {
setInnerValue(trimValue(value, props));
setInnerValue(trimValue(value, { min, max, range }));
};

const handleRangeClick = (e: MouseEvent) => {
Expand All @@ -165,7 +171,7 @@ const Slider: FC<SliderProps> = (props) => {
return;
}
const halfBlock = props.theme === 'capsule' ? Number(BLOCK_SIZE) / 2 : 0;
const currentLeft = e.clientX - initialLeft;
const currentLeft = e.clientX - initialLeft.current;
if (currentLeft < 0 || currentLeft > maxRange + Number(BLOCK_SIZE)) {
return;
}
Expand All @@ -181,11 +187,11 @@ const Slider: FC<SliderProps> = (props) => {

if (isMoveLeft) {
// 当前leftdot中心 + 左侧偏移量 = 目标左侧中心距离
const left = e.clientX - initialLeft;
const left = e.clientX - initialLeft.current;
const leftValue = convertPosToValue(left);
changeValue([calcByStep(leftValue), innerValue?.[1]]);
} else {
const right = -(e.clientX - initialRight);
const right = -(e.clientX - initialRight.current);
const rightValue = convertPosToValue(right, false);
changeValue([innerValue?.[0], calcByStep(rightValue)]);
}
Expand All @@ -199,7 +205,7 @@ const Slider: FC<SliderProps> = (props) => {
if (!sliderLineRef.current) {
return;
}
const currentLeft = e.clientX - initialLeft;
const currentLeft = e.clientX - initialLeft.current;
const value = convertPosToValue(currentLeft);
changeValue(calcByStep(value));
};
Expand All @@ -209,39 +215,36 @@ const Slider: FC<SliderProps> = (props) => {
return;
}
const { pageX } = e?.changedTouches?.[0] || {};
const currentLeft = pageX - initialLeft;
const newData = [...(innerValue as number[])];
const currentLeft = pageX - initialLeft.current;
const newData = cloneDeep(innerValue as number[]);
const leftValue = convertPosToValue(currentLeft);
newData[0] = calcByStep(leftValue);
changeValue(newData);
};

// eslint-disable-next-line @typescript-eslint/no-empty-function
const onTouchEnd = () => {};

const onSingleDotMove = (e: TouchEvent) => {
if (disabled) {
return;
}
const { pageX } = e.changedTouches?.[0] || {};
const value = convertPosToValue(pageX - initialLeft);
changeValue(calcByStep(value));
};

const onTouchMoveRight = (e: TouchEvent) => {
if (disabled) {
return;
}
const { pageX } = e?.changedTouches?.[0] || {};
const currentRight = -(pageX - initialRight);
const newData = [...(innerValue as number[])];
const currentRight = -(pageX - initialRight.current);
const newData = cloneDeep(innerValue as number[]);
const rightValue = convertPosToValue(currentRight, false);
newData[1] = calcByStep(rightValue);
changeValue(newData);
};

const onSingleDotMove = (e: TouchEvent) => {
if (disabled) {
return;
}
const { pageX } = e.changedTouches?.[0] || {};
const value = convertPosToValue(pageX - initialLeft.current);
changeValue(calcByStep(value));
};

const renderMinText = () => {
if (showExtremeValue) {
if (!showExtremeValue) {
return null;
}
const textClassName = classNames({
Expand Down Expand Up @@ -287,16 +290,16 @@ const Slider: FC<SliderProps> = (props) => {
},
)}
>
{scaleTextArray.length && (
<div className={`${rootClassName}__scale-desc, ${rootClassName}__scale-desc--${theme}`}>
{scaleTextArray.length ? (
<div className={`${rootClassName}__scale-desc ${rootClassName}__scale-desc--${theme}`}>
{scaleTextArray[index]}
</div>
)}
) : null}
</div>
));
};

const readerLineRange = () => (
const renderLineRange = () => (
<div
className={classNames(`${rootClassName}__line`, `${rootClassName}__line--${theme}`, {
[`${rootClassName}__line--disabled`]: disabled,
Expand All @@ -307,26 +310,22 @@ const Slider: FC<SliderProps> = (props) => {
ref={leftDotRef}
className={classNames(`${rootClassName}__dot`, `${rootClassName}__dot--left`)}
onTouchMove={onTouchMoveLeft}
onTouchEnd={onTouchEnd}
onTouchCancel={onTouchEnd}
>
{label && (
{label ? (
<div
className={classNames(`${rootClassName}__dot-value`, {
[`${rootClassName}__dot-value--sr-only`]: !props.label,
[`${rootClassName}__dot-value--sr-only`]: !label,
})}
>
{getValue(props.label, dotTopValue[0]) || dotTopValue[0]}
{getValue(label, dotTopValue[0]) || dotTopValue[0]}
</div>
)}
) : null}
<div className={`${rootClassName}__dot-slider`}></div>
</div>
<div
ref={rightDotRef}
className={classNames(`${rootClassName}__dot`, `${rootClassName}__dot--right`)}
onTouchMove={onTouchMoveRight}
onTouchEnd={onTouchEnd}
onTouchCancel={onTouchEnd}
>
{props.label && (
<div
Expand All @@ -342,7 +341,7 @@ const Slider: FC<SliderProps> = (props) => {
</div>
);

const readerLineSingle = () => (
const renderLineSingle = () => (
<div
className={classNames(
`${rootClassName}__line`,
Expand All @@ -354,16 +353,11 @@ const Slider: FC<SliderProps> = (props) => {
)}
style={{ width: `${lineBarWidth}px` }}
>
<div
className={`${rootClassName}__dot`}
onTouchMove={onSingleDotMove}
onTouchEnd={onTouchEnd}
onTouchCancel={onTouchEnd}
>
<div className={`${rootClassName}__dot`} onTouchMove={onSingleDotMove}>
{label ? (
<div
className={classNames(`${rootClassName}__dot-value`, {
[`${rootClassName}__dot-value--sr-only`]: label,
[`${rootClassName}__dot-value--sr-only`]: !label,
})}
>
{getValue(label, value) || innerValue}
Expand All @@ -379,7 +373,7 @@ const Slider: FC<SliderProps> = (props) => {
{renderMinText()}
<div ref={sliderLineRef} className={sliderLineClassName} onClick={range ? handleRangeClick : handleSingleClick}>
{renderScale()}
{range ? readerLineRange() : readerLineSingle()}
{range ? renderLineRange() : renderLineSingle()}
</div>
{renderMaxText()}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/slider/helper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const trimValue = (value: number | number[], props: any): number | number

if (range) {
if (Array.isArray(value)) {
const newValue = value.reduce((prev, cur) => trimSingleValue(cur, min, max));
const newValue = value.map((cur) => trimSingleValue(cur, min, max));
return newValue[0] <= newValue[1] ? value : [value[1], value[0]];
}
return [min, max];
Expand Down
3 changes: 1 addition & 2 deletions src/slider/slider.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

## API


### Slider Props

name | type | default | description | required
-- | -- | -- | -- | --
className | String | - | className of component | N
style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
disabled | Boolean | undefined | \- | N
label | String / Boolean | false | Typescript:`string \| boolean` | N
label | TNode | false | Typescript:`string \| boolean \| TNode<{ value: SliderValue; position?: 'start' \| 'end' }>`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
marks | Object / Array | - | Typescript:`Array<number> \| SliderMarks` `interface SliderMarks { [mark: number]: string \| TNode<{ value: number }> }`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts)[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N
max | Number | 100 | \- | N
min | Number | 0 | \- | N
Expand Down
3 changes: 1 addition & 2 deletions src/slider/slider.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

## API


### Slider Props

名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
disabled | Boolean | undefined | 是否禁用组件 | N
label | String / Boolean | false | 滑块当前值文本。<br />值为 true 显示默认文案;值为 false 不显示滑块当前值文本;<br />值为 `${value}%` 则表示组件会根据占位符渲染文案;<br />值类型为函数时,参数 `value` 标识滑块值,参数 `position=start` 表示范围滑块的起始值,参数 `position=end` 表示范围滑块的终点值。TS 类型:`string \| boolean` | N
label | TNode | false | 滑块当前值文本。<br />值为 true 显示默认文案;值为 false 不显示滑块当前值文本;<br />值为 `${value}%` 则表示组件会根据占位符渲染文案;<br />值类型为函数时,参数 `value` 标识滑块值,参数 `position=start` 表示范围滑块的起始值,参数 `position=end` 表示范围滑块的终点值。TS 类型:`string \| boolean \| TNode<{ value: SliderValue; position?: 'start' \| 'end' }>`[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
marks | Object / Array | - | 刻度标记,示例:[0, 10, 40, 200] 或者 `{ 10: (val) => val + '%', 50: (h) => <button>50</button> }`。TS 类型:`Array<number> \| SliderMarks` `interface SliderMarks { [mark: number]: string \| TNode<{ value: number }> }`[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts)[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N
max | Number | 100 | 滑块范围最大值 | N
min | Number | 0 | 滑块范围最小值 | N
Expand Down
2 changes: 1 addition & 1 deletion src/slider/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface TdSliderProps {
* 滑块当前值文本。<br />值为 true 显示默认文案;值为 false 不显示滑块当前值文本;<br />值为 `${value}%` 则表示组件会根据占位符渲染文案;<br />值类型为函数时,参数 `value` 标识滑块值,参数 `position=start` 表示范围滑块的起始值,参数 `position=end` 表示范围滑块的终点值
* @default false
*/
label?: string | boolean;
label?: string | boolean | TNode<{ value: SliderValue; position?: 'start' | 'end' }>;
/**
* 刻度标记,示例:[0, 10, 40, 200] 或者 `{ 10: (val) => val + '%', 50: (h) => <button>50</button> }`
*/
Expand Down

0 comments on commit 150ef59

Please sign in to comment.