feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
.input-slider {
display: flex;
align-items: flex-start;
:global {
.semi-slider {
padding: 0;
}
}
.slider {
width: 174px;
height: 52px;
:global {
.semi-slider-marks {
top: 32px;
font-size: 12px;
color: var(--light-usage-text-color-text-2, rgba(28, 31, 35, 0.6));
}
.semi-slider-mark {
transform: unset;
}
.semi-slider-mark:last-child {
left: unset;
right: 0;
transform: translateX(-100%);
width: fit-content;
white-space: nowrap;
}
.semi-slider-dot.semi-slider-dot-active {
background-color: transparent;
}
}
}
.input-number {
flex: 1;
:global {
.semi-input-wrapper {
border: 1px solid
var(--light-usage-border-color-border, rgba(28, 31, 35, 0.08));
background-color: #fff;
&:focus-within {
border-color: var(--semi-color-focus-border);
}
}
input {
text-align: center;
}
}
}
.input-btn {
position: absolute;
padding: 10px 8px;
font-size: 12px;
cursor: pointer;
color: rgba(28, 29, 35, 0.8);
top: 0;
z-index: 1;
&:first-child {
left: 0;
}
&:last-child {
right: 0;
}
&-disabled {
cursor: not-allowed;
}
}
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { InputSlider } from './input-slider';

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { type FC } from 'react';
import { isInteger, isNumber, isUndefined } from 'lodash-es';
import classNames from 'classnames';
import { type SliderProps } from '@coze-arch/bot-semi/Slider';
import { type CommonFieldProps } from '@coze-arch/bot-semi/Form';
import { withField, InputNumber, Slider } from '@coze-arch/bot-semi';
import { IconMinus, IconPlus } from '@douyinfe/semi-icons';
import { RCSliderWrapper, type RCSliderProps } from '../rc-slider-wrapper';
import s from './index.module.less';
interface InputSliderProps {
value?: number;
onChange?: (v: number) => void;
max?: number;
min?: number;
step?: number;
disabled?: boolean;
decimalPlaces?: number;
marks?: SliderProps['marks'];
className?: string;
/** 是否使用 rc-slider 替换 semi-slider目前 semi-slider 存在一个比较明显的 bug在缩放场景下拖拽定位存在问题已经反馈等待修复 */
useRcSlider?: boolean;
}
const POWVAL = 10;
const formateDecimalPlacesString = (
value: string | number,
prevValue?: number,
decimalPlaces?: number,
) => {
if (isUndefined(decimalPlaces)) {
return value.toString();
}
const numberValue = Number(value);
const stringValue = value.toString();
if (Number.isNaN(numberValue)) {
return `${value}`;
}
if (decimalPlaces === 0 && !isInteger(Number(value)) && prevValue) {
return `${prevValue}`;
}
const decimalPointIndex = stringValue.indexOf('.');
if (decimalPointIndex < 0) {
return stringValue;
}
const formattedValue = stringValue.substring(
0,
decimalPointIndex + 1 + decimalPlaces,
);
if (formattedValue.endsWith('.') && decimalPlaces === 0) {
return formattedValue.substring(0, formattedValue.length - 1);
}
return formattedValue;
};
const formateDecimalPlacesNumber = (
value: number,
prevValue?: number,
decimalPlaces?: number,
) => {
if (isUndefined(decimalPlaces)) {
return value;
}
if (decimalPlaces === 0 && !isInteger(value) && prevValue) {
return prevValue;
}
const pow = Math.pow(POWVAL, decimalPlaces);
return Math.round(value * pow) / pow;
};
const BaseInputSlider: React.FC<InputSliderProps> = ({
value,
onChange,
max = 1,
min = 0,
step = 1,
disabled,
decimalPlaces,
marks,
className,
useRcSlider = false,
}) => {
const onNumberChange = (numberValue: number) => {
const formattedValue = formateDecimalPlacesNumber(
numberValue,
value,
decimalPlaces,
);
onChange?.(formattedValue);
};
return (
<div className={classNames(s['input-slider'], className)}>
{useRcSlider ? (
<RCSliderWrapper
disabled={disabled}
value={value}
max={max}
min={min}
step={step}
marks={marks as RCSliderProps['marks']}
onChange={v => {
if (typeof v === 'number') {
onChange?.(v);
}
}}
/>
) : (
<Slider
className={s.slider}
disabled={disabled}
value={value}
max={max}
min={min}
step={step}
marks={marks}
onChange={v => {
if (typeof v === 'number') {
onChange?.(v);
}
}}
/>
)}
<div style={{ position: 'relative', marginLeft: 24 }}>
<IconMinus
className={classNames(
s['input-btn'],
disabled && s['input-btn-disabled'],
)}
onClick={e => {
e.stopPropagation();
if (isNumber(value) && value <= min) {
return;
}
if (!disabled && value !== undefined) {
onNumberChange(value - step);
}
}}
/>
<InputNumber
className={s['input-number']}
value={value}
disabled={disabled}
formatter={inputValue =>
formateDecimalPlacesString(inputValue, value)
}
hideButtons
onNumberChange={onNumberChange}
max={max}
min={min}
/>
<IconPlus
className={classNames(
s['input-btn'],
disabled && s['input-btn-disabled'],
)}
onClick={e => {
if (isNumber(value) && value >= max) {
return;
}
e.stopPropagation();
if (!disabled && value !== undefined) {
onNumberChange(value + step);
}
}}
/>
</div>
</div>
);
};
export const InputSlider: FC<CommonFieldProps & InputSliderProps> =
withField(BaseInputSlider);