import type { CSSProperties, VNodeTypes, ExtractPropTypes } from 'vue'; import { computed, ref, defineComponent } from 'vue'; import VcSlider from '../vc-slider/src/Slider'; import VcRange from '../vc-slider/src/Range'; import VcHandle from '../vc-slider/src/Handle'; import type { VueNode, CustomSlotsType } from '../_util/type'; import { stringType, booleanType, someType, objectType, withInstall, functionType, } from '../_util/type'; import type { TooltipPlacement } from '../tooltip/Tooltip'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import SliderTooltip from './SliderTooltip'; import classNames from '../_util/classNames'; import { useInjectFormItemContext } from '../form/FormItemContext'; import type { FocusEventHandler } from '../_util/EventInterface'; // CSSINJS import useStyle from './style'; import devWarning from '../vc-util/devWarning'; export type SliderValue = number | [number, number]; export interface SliderMarks { [key: number]: | VueNode | { style: CSSProperties; label: any; }; } interface HandleGeneratorInfo { value?: number; dragging?: boolean; index: number; } export interface SliderRange { draggableTrack?: boolean; } export type HandleGeneratorFn = (config: { tooltipPrefixCls?: string; prefixCls?: string; info: HandleGeneratorInfo; }) => VNodeTypes; type Value = [number, number] | number; const defaultTipFormatter = (value: number) => (typeof value === 'number' ? value.toString() : ''); export const sliderProps = () => ({ id: String, prefixCls: String, tooltipPrefixCls: String, range: someType([Boolean, Object]), reverse: booleanType(), min: Number, max: Number, step: someType([Object, Number]), marks: objectType(), dots: booleanType(), value: someType([Array, Number]), defaultValue: someType([Array, Number]), included: booleanType(), disabled: booleanType(), vertical: booleanType(), tipFormatter: someType<((value?: number) => any) | null>( [Function, Object], () => defaultTipFormatter, ), tooltipOpen: booleanType(), /** @deprecated `tooltipVisible` is deprecated. Please use `tooltipOpen` instead. */ tooltipVisible: booleanType(), tooltipPlacement: stringType(), getTooltipPopupContainer: functionType<(triggerNode: HTMLElement) => HTMLElement>(), autofocus: booleanType(), handleStyle: someType([Array, Object]), trackStyle: someType([Array, Object]), onChange: functionType<(value: Value) => void>(), onAfterChange: functionType<(value: Value) => void>(), onFocus: functionType(), onBlur: functionType(), 'onUpdate:value': functionType<(value: Value) => void>(), }); export type SliderProps = Partial>>; export type Visibles = { [index: number]: boolean }; const Slider = defineComponent({ compatConfig: { MODE: 3 }, name: 'ASlider', inheritAttrs: false, props: sliderProps(), // emits: ['update:value', 'change', 'afterChange', 'blur'], slots: Object as CustomSlotsType<{ mark?: any; default?: any; }>, setup(props, { attrs, slots, emit, expose }) { // Warning for deprecated usage if (process.env.NODE_ENV !== 'production') { [['tooltipVisible', 'tooltipOpen']].forEach(([deprecatedName, newName]) => { devWarning( props.tooltipVisible === undefined, 'Slider', `\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`, ); }); } const { prefixCls, rootPrefixCls, direction, getPopupContainer, configProvider } = useConfigInject('slider', props); // style const [wrapSSR, hashId] = useStyle(prefixCls); const formItemContext = useInjectFormItemContext(); const sliderRef = ref(); const visibles = ref({}); const toggleTooltipOpen = (index: number, visible: boolean) => { visibles.value[index] = visible; }; const tooltipPlacement = computed(() => { if (props.tooltipPlacement) { return props.tooltipPlacement; } if (!props.vertical) { return 'top'; } return direction.value === 'rtl' ? 'left' : 'right'; }); const focus = () => { sliderRef.value?.focus(); }; const blur = () => { sliderRef.value?.blur(); }; const handleChange = (val: SliderValue) => { emit('update:value', val); emit('change', val); formItemContext.onFieldChange(); }; const handleBlur = (e: FocusEvent) => { emit('blur', e); }; expose({ focus, blur, }); const handleWithTooltip: HandleGeneratorFn = ({ tooltipPrefixCls, info: { value, dragging, index, ...restProps }, }) => { const { tipFormatter, tooltipOpen = props.tooltipVisible, getTooltipPopupContainer } = props; const isTipFormatter = tipFormatter ? visibles.value[index] || dragging : false; const open = tooltipOpen || (tooltipOpen === undefined && isTipFormatter); return ( toggleTooltipOpen(index, true)} onMouseleave={() => toggleTooltipOpen(index, false)} /> ); }; return () => { const { tooltipPrefixCls: customizeTooltipPrefixCls, range, id = formItemContext.id.value, ...restProps } = props; const tooltipPrefixCls = configProvider.getPrefixCls('tooltip', customizeTooltipPrefixCls); const cls = classNames( attrs.class, { [`${prefixCls.value}-rtl`]: direction.value === 'rtl', }, hashId.value, ); // make reverse default on rtl direction if (direction.value === 'rtl' && !restProps.vertical) { restProps.reverse = !restProps.reverse; } // extrack draggableTrack from range={{ ... }} let draggableTrack: boolean | undefined; if (typeof range === 'object') { draggableTrack = range.draggableTrack; } if (range) { return wrapSSR( handleWithTooltip({ tooltipPrefixCls, prefixCls: prefixCls.value, info, }) } prefixCls={prefixCls.value} onChange={handleChange} onBlur={handleBlur} v-slots={{ mark: slots.mark }} />, ); } return wrapSSR( handleWithTooltip({ tooltipPrefixCls, prefixCls: prefixCls.value, info, }) } prefixCls={prefixCls.value} onChange={handleChange} onBlur={handleBlur} v-slots={{ mark: slots.mark }} />, ); }; }, }); export default withInstall(Slider);