D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
share
/
grafana
/
public
/
app
/
core
/
components
/
OptionsUI
/
Filename :
slider.tsx
back
Copy
import { css, cx } from '@emotion/css'; import { Global } from '@emotion/react'; import Slider from 'rc-slider'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { FieldConfigEditorProps, GrafanaTheme2, SliderFieldConfigSettings } from '@grafana/data'; import { useTheme2 } from '@grafana/ui'; import { getStyles } from '@grafana/ui/src/components/Slider/styles'; import { NumberInput } from './NumberInput'; type Props = FieldConfigEditorProps<number, SliderFieldConfigSettings>; export const SliderValueEditor = ({ value, onChange, item }: Props) => { // Input reference const inputRef = useRef<HTMLSpanElement>(null); // Settings const { settings } = item; const min = settings?.min || 0; const max = settings?.max || 100; const step = settings?.step; const marks = settings?.marks || { [min]: min, [max]: max }; const included = settings?.included; const ariaLabelForHandle = settings?.ariaLabelForHandle; // Core slider specific parameters and state const inputWidthDefault = 75; const isHorizontal = true; const theme = useTheme2(); const [sliderValue, setSliderValue] = useState<number>(value ?? min); const [inputWidth, setInputWidth] = useState<number>(inputWidthDefault); // Check for a difference between prop value and internal state useEffect(() => { if (value != null && value !== sliderValue) { setSliderValue(value); } }, [value, sliderValue]); // Using input font and expected maximum number of digits, set input width useEffect(() => { const inputElement = getComputedStyle(inputRef.current!); const fontWeight = inputElement.getPropertyValue('font-weight') || 'normal'; const fontSize = inputElement.getPropertyValue('font-size') || '16px'; const fontFamily = inputElement.getPropertyValue('font-family') || 'Arial'; const wideNumericalCharacter = '0'; const marginDigits = 4; // extra digits to account for things like negative, exponential, and controls const inputPadding = 8; // TODO: base this on input styling const maxDigits = Math.max((max + (step || 0)).toString().length, (max - (step || 0)).toString().length) + marginDigits; const refString = wideNumericalCharacter.repeat(maxDigits); const calculatedTextWidth = getTextWidth(refString, `${fontWeight} ${fontSize} ${fontFamily}`); if (calculatedTextWidth) { setInputWidth(calculatedTextWidth + inputPadding * 2); } }, [max, step]); const onSliderChange = useCallback( (v: number | number[]) => { const value = typeof v === 'number' ? v : v[0]; setSliderValue(value); if (onChange) { onChange(value); } }, [setSliderValue, onChange] ); const onSliderInputChange = useCallback( (value?: number) => { let v = value; if (Number.isNaN(v) || !v) { v = 0; } setSliderValue(v); if (onChange) { onChange(v); } }, [onChange] ); // Styles const styles = getStyles(theme, isHorizontal, Boolean(marks)); const stylesSlider = getStylesSlider(theme, inputWidth); const sliderInputClassNames = !isHorizontal ? [styles.sliderInputVertical] : []; return ( <div className={cx(styles.container, styles.slider)}> {/** Slider tooltip's parent component is body and therefore we need Global component to do css overrides for it. */} <Global styles={styles.slider} /> <label className={cx(styles.sliderInput, ...sliderInputClassNames)}> <Slider min={min} max={max} step={step} defaultValue={value} value={sliderValue} onChange={onSliderChange} vertical={!isHorizontal} reverse={false} ariaLabelForHandle={ariaLabelForHandle} marks={marks} included={included} /> <span className={stylesSlider.numberInputWrapper} ref={inputRef}> <NumberInput value={sliderValue} onChange={onSliderInputChange} max={max} min={min} step={step} /> </span> </label> </div> ); }; // Calculate width of string with given font function getTextWidth(text: string, font: string): number | null { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); if (context) { context.font = font; const metrics = context.measureText(text); return metrics.width; } return null; } const getStylesSlider = (theme: GrafanaTheme2, width: number) => { return { numberInputWrapper: css` margin-left: ${theme.spacing(3)}; max-height: 32px; max-width: ${width}px; min-width: ${width}px; overflow: visible; width: 100%; `, }; };