|
| 1 | +import React from 'react'; |
| 2 | + |
| 3 | +import {Text} from '@gravity-ui/uikit'; |
| 4 | + |
| 5 | +import isString from 'lodash/isString'; |
| 6 | +import set from 'lodash/set'; |
| 7 | + |
| 8 | +import { |
| 9 | + FieldValue, |
| 10 | + ObjectIndependentInput, |
| 11 | + StringSpec, |
| 12 | + ValidateError, |
| 13 | + isStringSpec, |
| 14 | +} from '../../../../core'; |
| 15 | +import {END_TIME, START_TIME} from '../../../constants/common'; |
| 16 | + |
| 17 | +import {TimeRangeSelect} from './components'; |
| 18 | +import {filterTimeArray, validateArray} from './utils'; |
| 19 | + |
| 20 | +export const TimeRangeSelector: ObjectIndependentInput = (props) => { |
| 21 | + const {spec, input, name, Layout} = props; |
| 22 | + |
| 23 | + const [startTimeSpec, endTimeSpec] = React.useMemo( |
| 24 | + () => |
| 25 | + [START_TIME, END_TIME].map((key) => |
| 26 | + isStringSpec(spec.properties?.[key]) |
| 27 | + ? (spec.properties?.[key] as StringSpec<any, undefined, undefined>) |
| 28 | + : undefined, |
| 29 | + ), |
| 30 | + [spec.properties], |
| 31 | + ); |
| 32 | + |
| 33 | + const {initialStartTimeOptions, initialEndTimeOptions, canBeFiltered} = React.useMemo(() => { |
| 34 | + const [initialStartTimeOptions, initialEndTimeOptions] = [startTimeSpec, endTimeSpec].map( |
| 35 | + (spec) => { |
| 36 | + if (spec && spec.enum) { |
| 37 | + return spec.enum.map((id) => ({ |
| 38 | + id, |
| 39 | + value: id, |
| 40 | + content: spec.viewSpec.selectParams?.meta?.[id] ? ( |
| 41 | + <div key={id}> |
| 42 | + <Text>{spec.description?.[id] || id}</Text> |
| 43 | + <Text color="secondary" as="div"> |
| 44 | + {spec.viewSpec.selectParams.meta[id]} |
| 45 | + </Text> |
| 46 | + </div> |
| 47 | + ) : ( |
| 48 | + spec.description?.[id] || id |
| 49 | + ), |
| 50 | + key: id, |
| 51 | + })); |
| 52 | + } |
| 53 | + |
| 54 | + return undefined; |
| 55 | + }, |
| 56 | + ); |
| 57 | + |
| 58 | + const canBeFiltered = |
| 59 | + initialStartTimeOptions && |
| 60 | + initialEndTimeOptions && |
| 61 | + validateArray(initialStartTimeOptions) && |
| 62 | + validateArray(initialEndTimeOptions); |
| 63 | + |
| 64 | + return { |
| 65 | + initialStartTimeOptions, |
| 66 | + initialEndTimeOptions, |
| 67 | + canBeFiltered, |
| 68 | + }; |
| 69 | + }, [endTimeSpec, startTimeSpec]); |
| 70 | + |
| 71 | + const {startTimeOptions, endTimeOptions} = React.useMemo(() => { |
| 72 | + let startTimeOptions = initialStartTimeOptions; |
| 73 | + let endTimeOptions = initialEndTimeOptions; |
| 74 | + |
| 75 | + [START_TIME, END_TIME].forEach((key) => { |
| 76 | + const time = input.value?.[key]; |
| 77 | + |
| 78 | + if ( |
| 79 | + isString(time) && |
| 80 | + canBeFiltered && |
| 81 | + initialEndTimeOptions && |
| 82 | + initialStartTimeOptions |
| 83 | + ) { |
| 84 | + if (START_TIME === key) { |
| 85 | + endTimeOptions = filterTimeArray(initialEndTimeOptions, time, 'greater'); |
| 86 | + } else { |
| 87 | + startTimeOptions = filterTimeArray(initialStartTimeOptions, time, 'less'); |
| 88 | + } |
| 89 | + } |
| 90 | + }); |
| 91 | + |
| 92 | + return {startTimeOptions, endTimeOptions}; |
| 93 | + }, [canBeFiltered, initialEndTimeOptions, initialStartTimeOptions, input.value]); |
| 94 | + |
| 95 | + const parentOnChange = React.useCallback( |
| 96 | + (childName: string, childValue: FieldValue, childErrors?: Record<string, ValidateError>) => |
| 97 | + input.onChange( |
| 98 | + (currentValue) => |
| 99 | + set({...currentValue}, childName.split(`${name}.`).join(''), childValue), |
| 100 | + childErrors, |
| 101 | + ), |
| 102 | + [input, name], |
| 103 | + ); |
| 104 | + |
| 105 | + if (!startTimeSpec || !endTimeSpec || !startTimeOptions || !endTimeOptions) { |
| 106 | + return null; |
| 107 | + } |
| 108 | + |
| 109 | + const content = ( |
| 110 | + <React.Fragment> |
| 111 | + <TimeRangeSelect |
| 112 | + spec={startTimeSpec} |
| 113 | + name={`${name}.${START_TIME}`} |
| 114 | + options={startTimeOptions} |
| 115 | + value={input.value?.[START_TIME]} |
| 116 | + handleChange={(value) => parentOnChange(START_TIME, value[0])} |
| 117 | + props={props} |
| 118 | + /> |
| 119 | + <TimeRangeSelect |
| 120 | + spec={endTimeSpec} |
| 121 | + name={`${name}.${END_TIME}`} |
| 122 | + options={endTimeOptions} |
| 123 | + value={input.value?.[END_TIME]} |
| 124 | + handleChange={(value) => parentOnChange(END_TIME, value[0])} |
| 125 | + props={props} |
| 126 | + /> |
| 127 | + </React.Fragment> |
| 128 | + ); |
| 129 | + |
| 130 | + if (Layout) { |
| 131 | + return <Layout {...props}>{content}</Layout>; |
| 132 | + } |
| 133 | + |
| 134 | + return <React.Fragment>{content}</React.Fragment>; |
| 135 | +}; |
0 commit comments