diff --git a/src/slider/Handle.tsx b/src/slider/Handle.tsx deleted file mode 100644 index 70bd0c94..00000000 --- a/src/slider/Handle.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useDrag } from "@use-gesture/react"; -import React, { FC, RefObject, useRef } from "react"; -import useConfig from "../_util/useConfig"; - -interface HandleProps { - value: number; - min: number; - max: number; - disabled: boolean; - onDrag: (value: number, first: boolean, last: boolean) => void; - barRef: RefObject; -} - -const Handle: FC = (props) => { - const { value, min, max, disabled, onDrag, barRef } = props; - - const { classPrefix } = useConfig(); - const name = `${classPrefix}-slider`; - - const preValue = useRef(0); - - const bind = useDrag( - ({ first, last, xy, initial }) => { - - if (disabled) return; - if (first) { - preValue.current = value; - } - const x = xy[0] - initial[0]; - const sliderOffsetWidth = barRef.current?.offsetWidth; - if (!sliderOffsetWidth) return; - const diff = (x / Math.ceil(sliderOffsetWidth)) * (max - min); - onDrag(preValue.current + diff, first, last); - }, - { - axis: "x", - pointer: { touch: true }, - } - ); - - return ( -
- ); -}; - -Handle.displayName = 'Handle' - -export default Handle; diff --git a/src/slider/Marks.tsx b/src/slider/Marks.tsx deleted file mode 100644 index 905c0128..00000000 --- a/src/slider/Marks.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import isArray from 'lodash/isArray'; -import React, { FC } from 'react'; -import cls from 'classnames'; -import useConfig from '../_util/useConfig'; -import { SliderMarks } from './type'; - -interface MarkProps { - range: boolean; - value?: [number, number]; - marks?: Array | SliderMarks; -} - -const defaultProps = { - range: false, -}; - -const Marks: FC = (props) => { - const { range, value, marks } = props; - - const { classPrefix } = useConfig(); - const name = `${classPrefix}-slider`; - - // 渲染单个刻度 - const renderMark = (item, index, renderIndex = false) => { - const textClass = cls({ - [`${name}__mark-text`]: true, - [`${classPrefix}-is-active`]: value[1] > index, - }); - - return ( -
- {item} -
- ); - }; - - if (range || (marks && isArray(marks))) { - return
{value.map((item, index) => renderMark(item, index))}
; - } - if (marks && !isArray(marks)) { - return
{Object.keys(marks).map((key) => renderMark(marks[key], key, true))}
; - } - - return null; -}; - -Marks.defaultProps = defaultProps; -Marks.displayName = 'Marks'; - -export default Marks; diff --git a/src/slider/Slider.tsx b/src/slider/Slider.tsx index 5bfdd698..418ace9f 100644 --- a/src/slider/Slider.tsx +++ b/src/slider/Slider.tsx @@ -1,223 +1,389 @@ -import React, { FC, useCallback, useMemo, useRef } from 'react'; -import classnames from 'classnames'; -import identity from 'lodash/identity'; -import isArray from 'lodash/isArray'; -import useConfig from '../_util/useConfig'; -import nearest from '../_util/nearest'; +import React, { FC, useEffect, useRef, useState } from 'react'; +import type { MouseEvent, TouchEvent } from 'react'; +import classNames from 'classnames'; +import isFunction from 'lodash/isFunction'; +import { usePrefixClass } from '../hooks/useClass'; +import useDefaultProps from '../hooks/useDefaultProps'; import useDefault from '../_util/useDefault'; -import withNativeProps, { NativeProps } from '../_util/withNativeProps'; -import Handle from './Handle'; -import Marks from './Marks'; +import { NativeProps } from '../_util/withNativeProps'; import { SliderValue, TdSliderProps } from './type'; - -const defaultProps = { - disabled: false, - max: 100, - min: 0, - range: false, - step: 1, - label: true, - showExtremeValue: false, - onChange: identity, - onDragstart: identity, - onDragend: identity, -}; +import { sliderDefaultProps } from './defaultProps'; +import { trimSingleValue, trimValue } from './helper'; +import { BLOCK_SIZE, BORDER_WIDTH } from './constants'; export interface SliderProps extends TdSliderProps, NativeProps {} const Slider: FC = (props) => { - const { classPrefix } = useConfig(); - const name = `${classPrefix}-slider`; - - const { - disabled, - max, - min, - range, - step, - value, - defaultValue, - marks, - showExtremeValue, - label, - onChange, - onDragstart, - onDragend, - } = props; - - const barRef = useRef(null); - const handleRef = useRef(null); - const firstValueRef = useRef<[number, number]>(); - const dragLockRef = useRef(false); - - const [rawValue, setRawValue] = useDefault( - value, - defaultValue || (range ? [min, min] : min), - onChange, - ); + const { disabled, max, min, range, step, theme, value, defaultValue, marks, showExtremeValue, label, onChange } = + useDefaultProps(props, sliderDefaultProps); + const [scaleArray, setScaleArray] = useState([]); + const [scaleTextArray, setScaleTextArray] = useState([]); + const [isScale, setIsScale] = useState(false); + const [initialLeft, setInitialLeft] = useState(0); + const [initialRight, setInitialRight] = useState(0); + const [maxRange, setMaxRange] = useState(0); + const [dotTopValue, setDotTopValue] = useState([0, 0]); + const [lineLeft, setLineLeft] = useState(); + const [lineRight, setLineRight] = useState(0); + const [lineBarWidth, setLineBarWidth] = useState(0); + const rootRef = useRef(null); + const leftDotRef = useRef(null); + const rightDotRef = useRef(null); + const sliderLineRef = useRef(null); + const [innerValue, setInnerValue] = useDefault(value, defaultValue, onChange); + const scope = Number(max) - Number(min); - // 排序 - const sortValue = useCallback((val: [number, number]): [number, number] => val.sort((a, b) => a - b), []); + const rootClassName = usePrefixClass('slider'); + const containerClassName = classNames(rootClassName, { + [`${rootClassName}--top`]: label || scaleTextArray.length, + [`${rootClassName}--disabled`]: disabled, + [`${rootClassName}--range`]: range, + }); + const sliderLineClassName = classNames(`${rootClassName}__bar`, `${rootClassName}__bar--${theme}`, { + [`${rootClassName}__bar--disabled`]: disabled, + [`${rootClassName}__bar--marks`]: isScale && theme === 'capsule', + }); + const sliderMaxTextClassName = classNames(`${rootClassName}__value`, `${rootClassName}__value--max`); - // 统一单/双游标滑块value结构 - const convertValue = useCallback( - (value: SliderValue): [number, number] => (range ? value : [min, value]) as [number, number], - [min, range], - ); + useEffect(() => { + getInitialStyle(theme); + }, [theme]); + + useEffect(() => { + function setSingleBarWidth(value: number) { + const halfBlock = theme === 'capsule' ? BLOCK_SIZE / 2 : 0; + const percentage = (Number(value) - min) / scope; + setLineBarWidth(percentage * maxRange + halfBlock); + } + + function setLineStyle(left: number, right: number) { + const halfBlock = theme === 'capsule' ? BLOCK_SIZE / 2 : 0; + const [a, b] = innerValue as Array; + + const parseNumber = (v: any) => parseInt(v, 10); + + setDotTopValue([a, b]); - const reverseValue = useCallback((value: [number, number]): SliderValue => (range ? value : value[1]), [range]); + if (left + right <= maxRange) { + setLineLeft(parseNumber(left + halfBlock)); + setLineRight(parseNumber(right + halfBlock)); + } else { + setLineLeft(parseNumber(maxRange + halfBlock - right)); + setLineRight(parseNumber(maxRange - left + halfBlock * 1.5)); + } + } + + if (!range) { + setSingleBarWidth(innerValue as number); + return; + } + const left = (maxRange * (innerValue[0] ?? 0 - min)) / scope; + const right = maxRange * (max - innerValue[1]); + setLineStyle(left, right); + }, [innerValue, max, maxRange, min, range, scope, theme]); + + useEffect(() => { + function handleMask(marks: any) { + const calcPos = (arr: number[]) => { + const margin = theme === 'capsule' ? BLOCK_SIZE / 2 : 0; + return arr.map((item) => ({ + val: item, + left: Math.round(((item - min) / scope) * maxRange) + margin, + })); + }; + if (marks?.length && Array.isArray(marks)) { + setIsScale(true); + setScaleArray(calcPos(marks)); + setScaleTextArray([]); + } + + if (Object.prototype.toString.call(marks) === '[object Object]') { + const scaleArray = Object.keys(marks).map((item) => Number(item)); + const scaleTextArray = scaleArray.map((item) => marks[item]); + setIsScale(scaleArray.length > 0); + setScaleArray(calcPos(scaleArray)); + setScaleTextArray(scaleTextArray); + } + } - // 计算要显示的点 - const pointList = useMemo(() => { if (marks) { - return isArray(marks) - ? marks.sort((a, b) => a - b) - : Object.keys(marks) - .map(parseFloat) - .sort((a, b) => a - b); + handleMask(marks); } - const points: number[] = []; - for (let i = min; i <= max; i += step) { - points.push(i); + }, [marks, maxRange, min, scope, theme]); + + function getInitialStyle(theme: 'default' | 'capsule') { + const line = sliderLineRef.current?.getBoundingClientRect() as DOMRect; + const halfBlock = Number(BLOCK_SIZE) / 2; + const maxRange = line.right - line.left; + + if (theme === 'capsule') { + setMaxRange(maxRange - BLOCK_SIZE - BORDER_WIDTH); // 6 是边框宽度 + setInitialLeft((initialLeft) => initialLeft - halfBlock); + setInitialRight((initialRight) => initialRight - halfBlock); + return; } - return points; - }, [marks, max, min, step]); + setMaxRange(maxRange); + setInitialLeft(line.left); + setInitialRight(line.right); + } - const sliderValue = useMemo(() => sortValue(convertValue(rawValue)), [rawValue, convertValue, sortValue]); + 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); + }; - const trackSize = `${(100 * (sliderValue[1] - sliderValue[0])) / (max - min)}%`; + const convertPosToValue = (posValue: number, isLeft = true) => + isLeft ? (posValue / maxRange) * scope + min : max - (posValue / maxRange) * scope; - const trackStart = `${(100 * (sliderValue[0] - min)) / (max - min)}%`; + const getPrecision = () => { + const precisions = [min, max, step].map((item) => { + const decimalArr = `${item}`.split('.'); + return decimalArr[1] ? decimalArr[1].length : 0; + }); + return Math.max.apply(null, precisions); + }; - const getValueByPosition = (position: number) => { - let newPosition = position; - if (position < min) { - newPosition = min; - } else if (position > max) { - newPosition = max; + const calcByStep = (value: number): number => { + const precision = getPrecision(); + if (step < 0 || step > scope) { + return Number(parseFloat(`${value}`).toFixed(precision)); } + const closestStep = trimSingleValue(Math.round(value / step) * step, min, max); - let value = min; + return Number(parseFloat(`${closestStep}`).toFixed(precision)); + }; + + const changeValue = (value: SliderValue) => { + setInnerValue(trimValue(value, props)); + }; - // 如果有显示刻度点,就移动到刻度点上 - if (pointList?.length) { - value = nearest({ - items: pointList, - target: newPosition, - }); + const handleRangeClick = (e: MouseEvent) => { + e.stopPropagation(); + if (disabled) { + return; + } + const halfBlock = props.theme === 'capsule' ? Number(BLOCK_SIZE) / 2 : 0; + const currentLeft = e.clientX - initialLeft; + if (currentLeft < 0 || currentLeft > maxRange + Number(BLOCK_SIZE)) { + return; + } + + const leftDotValue = leftDotRef.current?.getBoundingClientRect() as DOMRect; + const rightDotValue = rightDotRef.current?.getBoundingClientRect() as DOMRect; + // 点击处-halfblock 与 leftDot左侧的距离(绝对值) + const distanceLeft = Math.abs(e.clientX - leftDotValue.left - halfBlock); + // 点击处-halfblock 与 rightDot左侧的距离(绝对值) + const distanceRight = Math.abs(rightDotValue.left - e.clientX + halfBlock); + // 哪个绝对值小就移动哪个Dot + const isMoveLeft = distanceLeft < distanceRight; + + if (isMoveLeft) { + // 当前leftdot中心 + 左侧偏移量 = 目标左侧中心距离 + const left = e.clientX - initialLeft; + const leftValue = convertPosToValue(left); + changeValue([calcByStep(leftValue), innerValue?.[1]]); } else { - const lengthPerStep = 100 / ((max - min) / step); - const steps = Math.round(newPosition / lengthPerStep); - value = lengthPerStep * steps * (max - min) * 0.01 + min; + const right = -(e.clientX - initialRight); + const rightValue = convertPosToValue(right, false); + changeValue([innerValue?.[0], calcByStep(rightValue)]); } - return value; }; - // 更新滑块value - const updateSliderValue = (value: [number, number]) => { - const next = sortValue(value); - const current = sliderValue; - if (next[0] === current[0] && next[1] === current[1]) return; - setRawValue(reverseValue(next)); + const handleSingleClick = (e: MouseEvent) => { + e.stopPropagation(); + if (disabled) { + return; + } + if (!sliderLineRef.current) { + return; + } + const currentLeft = e.clientX - initialLeft; + const value = convertPosToValue(currentLeft); + changeValue(calcByStep(value)); }; - const handleClick = (e) => { - if (!dragLockRef.current) return; - if (disabled) return; + const onTouchMoveLeft = (e: TouchEvent) => { + if (disabled) { + return; + } + const { pageX } = e?.changedTouches?.[0] || {}; + const currentLeft = pageX - initialLeft; + const newData = [...(innerValue as number[])]; + const leftValue = convertPosToValue(currentLeft); + newData[0] = calcByStep(leftValue); + changeValue(newData); + }; - e.stopPropagation(); + // eslint-disable-next-line @typescript-eslint/no-empty-function + const onTouchEnd = () => {}; - const bar = barRef.current; - if (!bar) return; + const onSingleDotMove = (e: TouchEvent) => { + if (disabled) { + return; + } + const { pageX } = e.changedTouches?.[0] || {}; + const value = convertPosToValue(pageX - initialLeft); + changeValue(calcByStep(value)); + }; - const sliderOffsetLeft = bar.getBoundingClientRect().left; - const position = ((e.clientX - sliderOffsetLeft) / Math.ceil(bar.offsetWidth)) * (max - min) + min; + const onTouchMoveRight = (e: TouchEvent) => { + if (disabled) { + return; + } + const { pageX } = e?.changedTouches?.[0] || {}; + const currentRight = -(pageX - initialRight); + const newData = [...(innerValue as number[])]; + const rightValue = convertPosToValue(currentRight, false); + newData[1] = calcByStep(rightValue); + changeValue(newData); + }; - const targetValue = getValueByPosition(position); - let next: [number, number]; + const renderMinText = () => { + if (showExtremeValue) { + return null; + } + const textClassName = classNames({ + [`${rootClassName}__value`]: !range, + [`${rootClassName}__value--min`]: !range, + [`${rootClassName}__range-extreme`]: range, + [`${rootClassName}__range-extreme--min`]: range, + }); if (range) { - if (Math.abs(targetValue - sliderValue[0]) > Math.abs(targetValue - sliderValue[1])) { - next = [sliderValue[0], targetValue]; - } else { - next = [targetValue, sliderValue[1]]; - } - } else { - next = [min, targetValue]; + return {min}; } + return {label ? getValue(label, min) : min}; + }; - updateSliderValue(next); + const renderMaxText = () => { + if (!showExtremeValue) { + return null; + } + if (range) { + return {max}; + } + return {label ? getValue(label, max) : max}; }; - // 游标滑块 - const renderHandle = (index: number) => ( - { - if (first) { - dragLockRef.current = true; - firstValueRef.current = sliderValue; - onDragstart(); - } - const val = getValueByPosition(position); - const firstValue = firstValueRef.current; - if (!firstValue) return; - const next = [...firstValue] as [number, number]; - next[index] = val; - updateSliderValue(next); - if (last) { - onDragend(); - window.setTimeout(() => { - dragLockRef.current = false; - }, 100); - } - }} - /> - ); + const renderScale = () => { + if (!isScale) { + return null; + } + return scaleArray.map((item, index) => ( +
= item.val, + [`${rootClassName}__scale-item--active`]: range && dotTopValue[1] >= item.val && item.val >= dotTopValue[0], + [`${rootClassName}__scale-item--disabled`]: disabled, + [`${rootClassName}__scale-item--hidden`]: + ((index === 0 || index === scaleArray.length - 1) && theme === 'capsule') || innerValue === item.val, + }, + )} + > + {scaleTextArray.length && ( +
+ {scaleTextArray[index]} +
+ )} +
+ )); + }; - return withNativeProps( - props, + const readerLineRange = () => (
- {showExtremeValue &&
{min}
}
- {/* 总长度 */} -
- {/* 滑块长度 */} -
- {/* 双游标滑块操作 */} - {range && renderHandle(0)} - {/* 单游标滑块操作 */} - {renderHandle(1)} - {/* 刻度内容 */} - + {label && ( +
+ {getValue(props.label, dotTopValue[0]) || dotTopValue[0]} +
+ )} +
- {!range && label &&
{sliderValue[1]}
} - {showExtremeValue &&
{max}
} -
, +
+ {props.label && ( +
+ {getValue(props.label, dotTopValue[1]) || dotTopValue[1]} +
+ )} +
+
+
); -}; -Slider.defaultProps = defaultProps; -Slider.displayName = 'Slider'; + const readerLineSingle = () => ( +
+
+ {label ? ( +
+ {getValue(label, value) || innerValue} +
+ ) : null} +
+
+
+ ); + + return ( +
+ {renderMinText()} +
+ {renderScale()} + {range ? readerLineRange() : readerLineSingle()} +
+ {renderMaxText()} +
+ ); +}; export default Slider; diff --git a/src/slider/constants.ts b/src/slider/constants.ts new file mode 100644 index 00000000..6aeeea5c --- /dev/null +++ b/src/slider/constants.ts @@ -0,0 +1,3 @@ +export const BLOCK_SIZE = 20; + +export const BORDER_WIDTH = 6; diff --git a/src/slider/defaultProps.ts b/src/slider/defaultProps.ts new file mode 100644 index 00000000..b2eb076f --- /dev/null +++ b/src/slider/defaultProps.ts @@ -0,0 +1,17 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdSliderProps } from './type'; + +export const sliderDefaultProps: TdSliderProps = { + disabled: undefined, + label: false, + max: 100, + min: 0, + range: false, + showExtremeValue: false, + step: 1, + theme: 'default', + defaultValue: 0, +}; diff --git a/src/slider/helper/index.ts b/src/slider/helper/index.ts new file mode 100644 index 00000000..7451ecc7 --- /dev/null +++ b/src/slider/helper/index.ts @@ -0,0 +1,38 @@ +/** + * 处理单个number的超限和异常 + * @param {any} value + * @param {number} min + * @param {number} max + * @return {number} + */ +export const trimSingleValue = (value: any, min: number, max: number): number => { + if (value < min) { + return min; + } + + if (value > max) { + return max; + } + + return value; +}; + +/** + * 处理超限和异常value + * @param value + * @param props + * @returns + */ +export const trimValue = (value: number | number[], props: any): number | number[] => { + const { min, max, range } = props; + + if (range) { + if (Array.isArray(value)) { + const newValue = value.reduce((prev, cur) => trimSingleValue(cur, min, max)); + return newValue[0] <= newValue[1] ? value : [value[1], value[0]]; + } + return [min, max]; + } + + return trimSingleValue(value, min, max); +}; diff --git a/src/slider/index.tsx b/src/slider/index.tsx index 3e3c8ea2..2ef5b476 100644 --- a/src/slider/index.tsx +++ b/src/slider/index.tsx @@ -1,8 +1,9 @@ import _Slider from './Slider'; +import './style/index.js'; -import './style'; +export type { SliderProps } from './Slider'; +export * from './type'; export const Slider = _Slider; export default Slider; - diff --git a/src/slider/slider.en-US.md b/src/slider/slider.en-US.md new file mode 100644 index 00000000..807d0bf5 --- /dev/null +++ b/src/slider/slider.en-US.md @@ -0,0 +1,25 @@ +:: BASE_DOC :: + +## 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 +marks | Object / Array | - | Typescript:`Array \| 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 +range | Boolean | false | \- | N +showExtremeValue | Boolean | false | \- | N +step | Number | 1 | \- | N +theme | String | default | options: default/capsule | N +value | Number / Array | 0 | Typescript:`SliderValue` `type SliderValue = number \| Array`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N +defaultValue | Number / Array | 0 | uncontrolled property。Typescript:`SliderValue` `type SliderValue = number \| Array`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N +onChange | Function | | Typescript:`(value: SliderValue) => void`
| N +onDragend | Function | | Typescript:`(value: SliderValue, e: TouchEvent) => void`
| N +onDragstart | Function | | Typescript:`(e: TouchEvent) => void`
| N diff --git a/src/slider/slider.md b/src/slider/slider.md index 7ea4b0cf..99e695bb 100644 --- a/src/slider/slider.md +++ b/src/slider/slider.md @@ -1,22 +1,25 @@ :: BASE_DOC :: ## API + + ### Slider Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N -disabled | Boolean | false | 是否禁用组件 | N -label | TNode | true | 滑块当前值文本。
值为 true 显示默认文案;值为 false 不显示滑块当前值文本;
值为 `value` 则表示组件会根据占位符渲染文案;
值类型为函数时,参数 `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) => }`。TS 类型:`Array | 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 +disabled | Boolean | undefined | 是否禁用组件 | N +label | String / Boolean | false | 滑块当前值文本。
值为 true 显示默认文案;值为 false 不显示滑块当前值文本;
值为 `${value}%` 则表示组件会根据占位符渲染文案;
值类型为函数时,参数 `value` 标识滑块值,参数 `position=start` 表示范围滑块的起始值,参数 `position=end` 表示范围滑块的终点值。TS 类型:`string \| boolean` | N +marks | Object / Array | - | 刻度标记,示例:[0, 10, 40, 200] 或者 `{ 10: (val) => val + '%', 50: (h) => }`。TS 类型:`Array \| 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 range | Boolean | false | 双游标滑块 | N showExtremeValue | Boolean | false | 是否边界值 | N step | Number | 1 | 步长 | N -value | Number / Array | - | 滑块值。TS 类型:`SliderValue` `type SliderValue = number | Array`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N -defaultValue | Number / Array | - | 滑块值。非受控属性。TS 类型:`SliderValue` `type SliderValue = number | Array`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N +theme | String | default | 滑块风格。可选项:default/capsule | N +value | Number / Array | 0 | 滑块值。TS 类型:`SliderValue` `type SliderValue = number \| Array`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N +defaultValue | Number / Array | 0 | 滑块值。非受控属性。TS 类型:`SliderValue` `type SliderValue = number \| Array`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/slider/type.ts) | N onChange | Function | | TS 类型:`(value: SliderValue) => void`
滑块值变化时触发 | N -onDragend | Function | | TS 类型:`() => void`
结束拖动时触发 | N -onDragstart | Function | | TS 类型:`() => void`
开始拖动时触发 | N +onDragend | Function | | TS 类型:`(value: SliderValue, e: TouchEvent) => void`
结束拖动时触发 | N +onDragstart | Function | | TS 类型:`(e: TouchEvent) => void`
开始拖动时触发 | N diff --git a/src/slider/style/index.js b/src/slider/style/index.js index ec130ce3..586efd57 100644 --- a/src/slider/style/index.js +++ b/src/slider/style/index.js @@ -1 +1 @@ -import '../../_common/style/mobile/components/slider/_index.less'; +import '../../_common/style/mobile/components/slider/v2/_index.less'; diff --git a/src/slider/type.ts b/src/slider/type.ts index 3d8ef4d6..3d2a2abc 100644 --- a/src/slider/type.ts +++ b/src/slider/type.ts @@ -5,18 +5,18 @@ * */ import { TNode } from '../common'; +import { TouchEvent } from 'react'; export interface TdSliderProps { /** * 是否禁用组件 - * @default false */ disabled?: boolean; /** * 滑块当前值文本。
值为 true 显示默认文案;值为 false 不显示滑块当前值文本;
值为 `${value}%` 则表示组件会根据占位符渲染文案;
值类型为函数时,参数 `value` 标识滑块值,参数 `position=start` 表示范围滑块的起始值,参数 `position=end` 表示范围滑块的终点值 - * @default true + * @default false */ - label?: string | boolean | TNode<{ value: SliderValue; position?: 'start' | 'end' }>; + label?: string | boolean; /** * 刻度标记,示例:[0, 10, 40, 200] 或者 `{ 10: (val) => val + '%', 50: (h) => }` */ @@ -46,12 +46,19 @@ export interface TdSliderProps { * @default 1 */ step?: number; + /** + * 滑块风格 + * @default default + */ + theme?: 'default' | 'capsule'; /** * 滑块值 + * @default 0 */ value?: SliderValue; /** * 滑块值,非受控属性 + * @default 0 */ defaultValue?: SliderValue; /** @@ -61,13 +68,15 @@ export interface TdSliderProps { /** * 结束拖动时触发 */ - onDragend?: () => void; + onDragend?: (value: SliderValue, e: TouchEvent) => void; /** * 开始拖动时触发 */ - onDragstart?: () => void; + onDragstart?: (e: TouchEvent) => void; } -export interface SliderMarks { [mark: number]: string | TNode<{ value: number }> }; +export interface SliderMarks { + [mark: number]: string | TNode<{ value: number }>; +} export type SliderValue = number | Array;