All files / src/components/RangeDateSelection RangeDateSelection.tsx

48% Statements 12/25
50% Branches 4/8
16.66% Functions 1/6
48% Lines 12/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122    1x                                     1x                                           3x   3x   3x       3x         3x         3x 3x   3x   3x                                           1x                                                                  
'use client';
 
import React from 'react';
 
import type {DateTime} from '@gravity-ui/date-utils';
import {Minus, Plus} from '@gravity-ui/icons';
import {Button, Icon} from '@gravity-ui/uikit';
 
import {block} from '../../utils/cn';
import type {AccessibilityProps, DomProps, StyleProps} from '../types';
import {filterDOMProps} from '../utils/filterDOMProps';
 
import {DateTimeRuler} from './components/Ruler/Ruler';
import type {ViewportDimensions, ViewportInterval} from './components/Ruler/Ruler';
import {SelectionControl} from './components/SelectionControl/SelectionControl';
import {useRangeDateSelectionState} from './hooks/useRangeDateSelectionState';
import type {RangeDateSelectionOptions} from './hooks/useRangeDateSelectionState';
import {i18n} from './i18n';
 
import './RangeDateSelection.scss';
 
const b = block('range-date-selection');
 
export interface RangeDateSelectionProps
    extends RangeDateSelectionOptions, DomProps, StyleProps, AccessibilityProps {
    /** Formats time ticks */
    formatTime?: (time: DateTime) => string;
    /** Displays now line */
    displayNow?: boolean;
    /** Enables dragging ruler */
    draggableRuler?: boolean;
    /** Displays buttons to scale selection */
    hasScaleButtons?: boolean;
    /** Position of scale buttons */
    scaleButtonsPosition?: 'start' | 'end';
    /** Renders additional svg content in the ruler */
    renderAdditionalRulerContent?: (props: {
        interval: ViewportInterval;
        dimensions: ViewportDimensions;
    }) => React.ReactNode;
}
 
export function RangeDateSelection(props: RangeDateSelectionProps) {
    const state = useRangeDateSelectionState(props);
 
    const [isDraggingRuler, setDraggingRuler] = React.useState(false);
 
    const handleRulerMoveStart = () => {
        state.setDraggingValue(state.value);
        setDraggingRuler(true);
    };
    const handleRulerMove = (d: number) => {
        const intervalWidth = state.viewportInterval.end.diff(state.viewportInterval.start);
        const delta = -Math.floor((d * intervalWidth) / 100);
        state.move(delta);
    };
    const handleRulerMoveEnd = () => {
        setDraggingRuler(false);
        state.endDragging();
    };
 
    let id = React.useId();
    id = props.id ?? id;
 
    const {t} = i18n.useTranslation();
 
    return (
        <div
            {...filterDOMProps(props, {labelable: true})}
            id={id}
            className={b(null, props.className)}
            style={props.style}
            dir="ltr" // TODO: RTL support
        >
            <DateTimeRuler
                className={b('ruler', {dragging: isDraggingRuler})}
                {...state.viewportInterval}
                onMoveStart={handleRulerMoveStart}
                onMove={props.draggableRuler ? handleRulerMove : undefined}
                onMoveEnd={handleRulerMoveEnd}
                dragDisabled={state.isDragging}
                displayNow={props.displayNow}
                minValue={props.minValue}
                maxValue={props.maxValue}
                formatTime={props.formatTime}
                timeZone={state.timeZone}
                renderAdditionalRulerContent={props.renderAdditionalRulerContent}
            >
                <SelectionControl className={b('selection')} state={state} aria-labelledby={id} />
            </DateTimeRuler>
            {props.hasScaleButtons ? (
                <div className={b('buttons', {position: props.scaleButtonsPosition ?? 'start'})}>
                    <Button
                        view="flat-secondary"
                        size="xs"
                        onClick={() => {
                            state.startDragging();
                            state.scale(0.5);
                            state.endDragging();
                        }}
                        aria-label={t('Decrease range')}
                    >
                        <Icon data={Minus} />
                    </Button>
                    <Button
                        view="flat-secondary"
                        size="xs"
                        onClick={() => {
                            state.startDragging();
                            state.scale(1.5);
                            state.endDragging();
                        }}
                        aria-label={t('Increase range')}
                    >
                        <Icon data={Plus} />
                    </Button>
                </div>
            ) : null}
        </div>
    );
}