diff --git a/.github/workflows/deploy-site-preview.yml b/.github/workflows/deploy-site-preview.yml
index bd04b4a790..06dd899740 100644
--- a/.github/workflows/deploy-site-preview.yml
+++ b/.github/workflows/deploy-site-preview.yml
@@ -1,8 +1,7 @@
name: deploy-site-preview
on:
- workflow_run:
- workflows: ['pr_check']
- types: completed
+ pull_request:
+ types: [opened, synchronize, reopened]
push:
tags:
@@ -65,7 +64,7 @@ jobs:
- name: get PreviewID
run: |
- if ${{ github.event_name == 'pull_request' }}; then
+ if ${{ github.event_name == 'push' }}; then
echo "PreviewID=${{ github.event.number }}" >> $GITHUB_ENV
else
echo "PreviewID=main" >> $GITHUB_ENV
diff --git a/components/Slider/__demo__/input.md b/components/Slider/__demo__/input.md
index 2919a701a0..1a2638e755 100644
--- a/components/Slider/__demo__/input.md
+++ b/components/Slider/__demo__/input.md
@@ -7,7 +7,7 @@ title:
## zh-CN
-当 `showInput` 为 true 时,将显示输入框。当设置 `onlyMarkValue` 为 `true` 时,输入框始终不会显示。
+当 `showInput` 为 true 时,将显示输入框。当设置 `onlyMarkValue` 为 `true` 或者范围内多点选择时,输入框始终不会显示。
当 `showInput` 传入 `InputNumberProps` 时,`min`、`max`、`step` 属性会以 `SliderProps` 为先。
@@ -15,7 +15,7 @@ title:
## en-US
-When `showInput` is set to true, the input box will be displayed. But when setting `onlyMarkValue` to `true`, the input box will never be displayed.
+When `showInput` is set to true, the input box will be displayed. But when setting `onlyMarkValue` to `true` or selecting multiple points within the range, the input box will never be displayed.
When `showInput` is passed `InputNumberProps`, the `min`, `max`, `step` properties will be preceded by `SliderProps`.
diff --git a/components/Slider/__demo__/marks.md b/components/Slider/__demo__/marks.md
index c554c68a47..672f761653 100644
--- a/components/Slider/__demo__/marks.md
+++ b/components/Slider/__demo__/marks.md
@@ -1,5 +1,5 @@
---
-order: 4
+order: 5
title:
zh-CN: 添加标签文本
en-US: Marks
diff --git a/components/Slider/__demo__/range-multi.md b/components/Slider/__demo__/range-multi.md
new file mode 100644
index 0000000000..365356df9e
--- /dev/null
+++ b/components/Slider/__demo__/range-multi.md
@@ -0,0 +1,33 @@
+---
+order: 4
+title:
+ zh-CN: 多点选择
+ en-US: Multiple Dots
+---
+
+## zh-CN
+
+范围内多个点选择。(`2.61.0` 支持)
+
+## en-US
+
+Select multiple points within the range. (in `2.61.0`)
+
+
+```js
+import { useState } from 'react';
+import { Slider, Typography } from '@arco-design/web-react';
+
+function App() {
+ const [value, setValue] = useState([0, 20, 50]);
+ return (
+
+
+
+ value: {JSON.stringify(value)}
+
+ );
+}
+
+export default App;
+```
diff --git a/components/Slider/__test__/__snapshots__/demo.test.ts.snap b/components/Slider/__test__/__snapshots__/demo.test.ts.snap
index 20291989ab..b525b31229 100644
--- a/components/Slider/__test__/__snapshots__/demo.test.ts.snap
+++ b/components/Slider/__test__/__snapshots__/demo.test.ts.snap
@@ -1524,6 +1524,70 @@ exports[`renders Slider/demo/range.md correctly 1`] = `
`;
+exports[`renders Slider/demo/range-multi.md correctly 1`] = `
+
+
+
+
+
+ value: [0,20,50]
+
+
+
+`;
+
exports[`renders Slider/demo/reversed.md correctly 1`] = `
Array [
)}
diff --git a/components/Slider/hooks/useLegalValue.ts b/components/Slider/hooks/useLegalValue.ts
index 4b4eea63f0..be4cb20a70 100644
--- a/components/Slider/hooks/useLegalValue.ts
+++ b/components/Slider/hooks/useLegalValue.ts
@@ -78,10 +78,12 @@ export default function useLegalValue(props: {
if (isRange) {
if (isArray(val)) {
beginVal = getLegalValue(val[0]);
- endVal = getLegalValue(val[1]);
- } else {
- console.error('value must be an array when range is true');
+ // endVal = getLegalValue(val[1]);
+ return val.map((v) => {
+ return getLegalValue(v);
+ });
}
+ console.error('value must be an array when range is true');
} else if (isNumber(val)) {
endVal = getLegalValue(val);
} else {
diff --git a/components/Slider/index.tsx b/components/Slider/index.tsx
index 76136cf3ee..47a652d445 100644
--- a/components/Slider/index.tsx
+++ b/components/Slider/index.tsx
@@ -7,7 +7,13 @@ import Dots from './dots';
import Input from './input';
import Ticks from './ticks';
import { isFunction, isObject, isArray } from '../_util/is';
-import { formatPercent, getIntervalOffset } from './utils';
+import {
+ findNearestIndex,
+ formatPercent,
+ getIntervalOffset,
+ needSort,
+ sortNumberArray,
+} from './utils';
import cs from '../_util/classNames';
import { ConfigContext } from '../ConfigProvider';
import { TooltipPosition, SliderProps } from './interface';
@@ -79,10 +85,10 @@ function Slider(baseProps: SliderProps, ref) {
});
// 计算合法值
- const curVal = getLegalRangeValue(value);
+ let curVal = getLegalRangeValue(value);
const lastVal = useRef(curVal);
- let [beginVal, endVal] = curVal;
- const reverseOrder = useRef(beginVal > endVal);
+ // let [beginVal, endVal] = curVal;
+ const reverseOrder = useRef(needSort(curVal));
// value变化后 更新lastVal
useUpdate(() => {
@@ -90,14 +96,14 @@ function Slider(baseProps: SliderProps, ref) {
}, [value, getLegalRangeValue]);
if (reverseOrder.current) {
- [beginVal, endVal] = [endVal, beginVal];
+ curVal = sortNumberArray(curVal);
}
- // 偏移比例
- const beginOffset = getIntervalOffset(beginVal, intervalConfigs);
- const endOffset = getIntervalOffset(endVal, intervalConfigs);
- // 是否显示输入框
- const isShowInput = showInput && !onlyMarkValue;
+ const maxVal = curVal[curVal.length - 1];
+ const minVal = curVal[0];
+
+ // 是否显示输入框。多点选择不显示 input
+ const isShowInput = showInput && !onlyMarkValue && (!range || curVal.length < 3);
const extraInputProps = useMemo(() => {
if (isShowInput && (isArray(showInput) || isObject(showInput))) {
return isArray(showInput) ? [...showInput] : [{ ...showInput }, { ...showInput }];
@@ -118,29 +124,26 @@ function Slider(baseProps: SliderProps, ref) {
const isDragging = useRef(false);
const barStartDragVal = useRef(0);
- function getEmitParams([beginVal, endVal]: number[]): number | number[] {
- if (beginVal > endVal) {
- [beginVal, endVal] = [endVal, beginVal];
- }
- return range ? [beginVal, endVal] : endVal;
+ function getEmitParams(value: number[]): number | number[] {
+ const sortedValue = sortNumberArray(value);
+ return range ? sortedValue : sortedValue[sortedValue.length - 1];
}
function updateValue(val) {
- let [newBeginVal, newEndVal] = val;
- newBeginVal = getLegalValue(newBeginVal);
- newEndVal = getLegalValue(newEndVal);
- lastVal.current = [newBeginVal, newEndVal];
- return [newBeginVal, newEndVal];
+ const copyVal = val.map((x) => getLegalValue(x));
+ lastVal.current = copyVal;
+ return copyVal;
}
function onChange(val, reason?: 'mousemove' | 'jumpToClick' | 'inputValueChange') {
- const [newBeginVal, newEndVal] = updateValue(val);
- const emitParams = getEmitParams([newBeginVal, newEndVal]);
+ const newValue = updateValue(val);
+ const emitParams = getEmitParams(newValue);
+
setValue(emitParams);
// 在手动修改的情况下才可能出现反序问题。
if (reason === 'inputValueChange') {
- reverseOrder.current = newBeginVal > newEndVal;
+ reverseOrder.current = newValue.some((x, i) => x > newValue[i]);
} else {
// 在mousemove 跟 jumpToClick 顺序会保持 [begin,end]
reverseOrder.current = false;
@@ -158,7 +161,7 @@ function Slider(baseProps: SliderProps, ref) {
}
function inRange(val: number) {
- let [range1, range2] = [beginVal, endVal];
+ let [range1, range2] = [curVal[0], curVal[curVal.length - 1]];
if (range1 > range2) {
[range1, range2] = [range2, range1];
}
@@ -261,10 +264,18 @@ function Slider(baseProps: SliderProps, ref) {
if (disabled) return;
const value = getLegalValue(val);
- if (range && endVal - value > value - beginVal) {
- onChange([value, endVal], 'jumpToClick');
+ // 找到 value 临近的两个值。
+ const [beforeIndex, nextIndex] = findNearestIndex(value, curVal);
+ const nearBeginVal = curVal[beforeIndex];
+ const nearEndValue = curVal[nextIndex];
+ const copyVal = curVal.slice(0);
+
+ if (range && nearEndValue - value > value - nearBeginVal) {
+ copyVal[beforeIndex] = value;
+ onChange(copyVal, 'jumpToClick');
} else {
- onChange([beginVal, value], 'jumpToClick');
+ copyVal[nextIndex] = value;
+ onChange(copyVal, 'jumpToClick');
}
onMouseUp();
}
@@ -275,15 +286,13 @@ function Slider(baseProps: SliderProps, ref) {
}
// 拖动开始节点
- function handleBeginMove(x: number, y: number) {
+ function handleMove(x: number, y: number, index) {
isDragging.current = true;
- onChange([getValueByCoords(x, y), endVal], 'mousemove');
- }
+ const copyVal = curVal.slice(0);
- // 拖动结束节点
- function handleEndMove(x: number, y: number) {
- isDragging.current = true;
- onChange([beginVal, getValueByCoords(x, y)], 'mousemove');
+ copyVal[index] = getValueByCoords(x, y);
+
+ onChange(copyVal, 'mousemove');
}
function handleMoveEnd() {
@@ -292,27 +301,23 @@ function Slider(baseProps: SliderProps, ref) {
}
// 结束节点的 arrow event
- function handleEndArrowEvent(type: 'addition' | 'subtraction') {
+ function handleArrowEvent(type: 'addition' | 'subtraction', index: number) {
if (disabled) return;
+ const copyVal = curVal.slice(0);
- onChange([beginVal, getNextMarkValue(endVal, type)]);
- }
+ copyVal[index] = getNextMarkValue(curVal[index], type);
- // 起始节点的 arrow event
- function handleBeginArrowEvent(type: 'addition' | 'subtraction') {
- if (disabled) return;
- onChange([getNextMarkValue(beginVal, type), endVal]);
+ onChange(copyVal);
}
// bar 移动中
function onBarMouseMove(e) {
const newVal = getLegalValue(getValueByCoords(e.clientX, e.clientY));
const offsetVal = newVal - barStartDragVal.current;
- const newBeginVal = beginVal + offsetVal;
- const newEndVal = endVal + offsetVal;
+ const copyVal = curVal.map((x) => x + offsetVal);
- if (isLegalValue(newBeginVal) && isLegalValue(newEndVal)) {
- onChange([newBeginVal, newEndVal], 'mousemove');
+ if (copyVal.every((v) => isLegalValue(v))) {
+ onChange(copyVal, 'mousemove');
}
}
@@ -355,13 +360,19 @@ function Slider(baseProps: SliderProps, ref) {
})}
onMouseDown={onRoadMouseDown}
>
-
+
{showTicks && (
- {range && (
-
- )}
-
+ {curVal.map((val, index) => {
+ if (!range && index !== curVal.length - 1) {
+ return null;
+ }
+ return (
+ handleMove(x, y, index)}
+ onMoveEnd={handleMoveEnd}
+ onArrowEvent={(type) => handleArrowEvent(type, index)}
+ />
+ );
+ })}
{isShowInput && (
= new Map();
@@ -26,7 +26,7 @@ export default memo(function Ticks(props: TicksProps) {
const stepVal = plus(i * step, begin);
if (stepVal <= min || stepVal >= max) continue;
const offset = formatPercent(getIntervalOffset(stepVal, intervalConfigs));
- stepsMap.set(offset, { offset, isActive: valueInRange(stepVal, value) });
+ stepsMap.set(offset, { offset, isActive: valueInRange(stepVal, valueRange) });
}
};
diff --git a/components/Slider/utils.ts b/components/Slider/utils.ts
index 87ae278ea4..e12665ca76 100644
--- a/components/Slider/utils.ts
+++ b/components/Slider/utils.ts
@@ -60,3 +60,28 @@ export function getIntervalOffset(val, intervalConfig: IntervalConfig[]) {
return plus(beginOffset, offset);
}
}
+
+// 从小到大排序
+export function sortNumberArray(arr: number[]) {
+ const copyArr = arr.slice(0);
+ copyArr.sort((a, b) => a - b);
+ return copyArr;
+}
+
+// 是否需要排序
+export function needSort(arr: number[]) {
+ return arr.some((x, i) => x > arr[i + 1]);
+}
+
+// 找到 value 在 array 中的索引。value: 5 ,arrry: [1, 3, 8], return: 2.
+export function findNearestIndex(value: number, array: number[]): [number, number] {
+ let valueIndex = array.indexOf(value);
+ if (valueIndex === -1) {
+ const arr = sortNumberArray(array.concat(value));
+
+ valueIndex = arr.indexOf(value);
+
+ return [Math.max(valueIndex - 1, 0), Math.min(valueIndex, array.length - 1)];
+ }
+ return [valueIndex, valueIndex + 1];
+}