function MetricsAxisOptions()

in src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx [67:339]


function MetricsAxisOptions(props: ValidationVisOptionsProps<BasicVislibParams>) {
  const { stateParams, setValue, aggs, vis, isTabSelected } = props;

  const [isCategoryAxisHorizontal, setIsCategoryAxisHorizontal] = useState(true);

  const setParamByIndex: SetParamByIndex = useCallback(
    (axesName, index, paramName, value) => {
      const items = stateParams[axesName];
      const array = [...items] as typeof items;

      array[index] = {
        ...array[index],
        [paramName]: value,
      };

      setValue(axesName, array);
    },
    [stateParams, setValue]
  );

  const setCategoryAxis = useCallback(
    (value: Axis) => {
      const categoryAxes = [...stateParams.categoryAxes];
      categoryAxes[0] = value;
      setValue('categoryAxes', categoryAxes);
    },
    [setValue, stateParams.categoryAxes]
  );

  // stores previous aggs' custom labels
  const [lastCustomLabels, setLastCustomLabels] = useState({} as { [key: string]: string });
  // stores previous aggs' field and type
  const [lastSeriesAgg, setLastSeriesAgg] = useState(
    {} as {
      [key: string]: { type: string; field: string };
    }
  );

  const updateAxisTitle = useCallback(
    (seriesParams?: SeriesParam[]) => {
      const series = seriesParams || stateParams.seriesParams;
      let isAxesChanged = false;
      let lastValuesChanged = false;
      const lastLabels = { ...lastCustomLabels };
      const lastMatchingSeriesAgg = { ...lastSeriesAgg };

      const axes = stateParams.valueAxes.map((axis, axisNumber) => {
        let newCustomLabel = '';
        let updatedAxis;
        const matchingSeries: IAggConfig[] = [];

        series.forEach((serie, seriesIndex) => {
          if ((axisNumber === 0 && !serie.valueAxis) || serie.valueAxis === axis.id) {
            const aggByIndex = aggs.bySchemaName('metric')[seriesIndex];
            matchingSeries.push(aggByIndex);
          }
        });

        if (matchingSeries.length === 1) {
          // if several series matches to the axis, axis title is set according to the first serie.
          newCustomLabel = matchingSeries[0].makeLabel();
        }

        if (lastCustomLabels[axis.id] !== newCustomLabel && newCustomLabel !== '') {
          const lastSeriesAggType = get(lastSeriesAgg, `${matchingSeries[0].id}.type`);
          const lastSeriesAggField = get(lastSeriesAgg, `${matchingSeries[0].id}.field`);
          const matchingSeriesAggType = get(matchingSeries, '[0]type.name', '');
          const matchingSeriesAggField = get(matchingSeries, '[0]params.field.name', '');

          const aggTypeIsChanged = lastSeriesAggType !== matchingSeriesAggType;
          const aggFieldIsChanged = lastSeriesAggField !== matchingSeriesAggField;

          lastMatchingSeriesAgg[matchingSeries[0].id] = {
            type: matchingSeriesAggType,
            field: matchingSeriesAggField,
          };
          lastLabels[axis.id] = newCustomLabel;
          lastValuesChanged = true;

          if (
            Object.keys(lastCustomLabels).length !== 0 &&
            (aggTypeIsChanged ||
              aggFieldIsChanged ||
              axis.title.text === '' ||
              lastCustomLabels[axis.id] === axis.title.text) &&
            newCustomLabel !== axis.title.text
          ) {
            // Override axis title with new custom label
            updatedAxis = {
              ...axis,
              title: { ...axis.title, text: newCustomLabel },
            };
            isAxesChanged = true;
          }
        }

        return updatedAxis || axis;
      });

      if (isAxesChanged) {
        setValue('valueAxes', axes);
      }

      if (lastValuesChanged) {
        setLastSeriesAgg(lastMatchingSeriesAgg);
        setLastCustomLabels(lastLabels);
      }
    },
    [
      aggs,
      lastCustomLabels,
      lastSeriesAgg,
      setValue,
      stateParams.seriesParams,
      stateParams.valueAxes,
    ]
  );

  const onValueAxisPositionChanged = useCallback(
    (index: number, value: ValueAxis['position']) => {
      const valueAxes = [...stateParams.valueAxes];
      const name = getUpdatedAxisName(value, valueAxes);

      valueAxes[index] = {
        ...valueAxes[index],
        name,
        position: value,
      };
      setValue('valueAxes', valueAxes);
    },
    [stateParams.valueAxes, setValue]
  );

  const onCategoryAxisPositionChanged = useCallback(
    (chartPosition: Axis['position']) => {
      const isChartHorizontal = isAxisHorizontal(chartPosition);
      setIsCategoryAxisHorizontal(isAxisHorizontal(chartPosition));

      stateParams.valueAxes.forEach((axis, index) => {
        if (isAxisHorizontal(axis.position) === isChartHorizontal) {
          const position = mapPosition(axis.position);
          onValueAxisPositionChanged(index, position);
        }
      });
    },
    [stateParams.valueAxes, onValueAxisPositionChanged]
  );

  const addValueAxis = useCallback(() => {
    const nextAxisIdNumber = stateParams.valueAxes.reduce(
      countNextAxisNumber(VALUE_AXIS_PREFIX),
      1
    );

    const newAxis = cloneDeep(stateParams.valueAxes[0]);
    newAxis.id = VALUE_AXIS_PREFIX + nextAxisIdNumber;
    newAxis.position = mapPositionOpposite(newAxis.position);
    newAxis.name = getUpdatedAxisName(newAxis.position, stateParams.valueAxes);

    setValue('valueAxes', [...stateParams.valueAxes, newAxis]);
    return newAxis;
  }, [stateParams.valueAxes, setValue]);

  const removeValueAxis = useCallback(
    (axis: ValueAxis) => {
      const newValueAxes = stateParams.valueAxes.filter((valAxis) => valAxis.id !== axis.id);

      setValue('valueAxes', newValueAxes);

      let isSeriesUpdated = false;
      const series = stateParams.seriesParams.map((ser) => {
        if (axis.id === ser.valueAxis) {
          isSeriesUpdated = true;
          return { ...ser, valueAxis: newValueAxes[0].id };
        }
        return ser;
      });

      if (isSeriesUpdated) {
        // if seriesParams have valueAxis equals to removed one, then we reset it to the first valueAxis
        setValue('seriesParams', series);
      }

      if (stateParams.grid.valueAxis === axis.id) {
        // reset Y-axis grid lines setting
        setValue('grid', { ...stateParams.grid, valueAxis: undefined });
      }
    },
    [stateParams.seriesParams, stateParams.valueAxes, setValue, stateParams.grid]
  );

  const changeValueAxis: ChangeValueAxis = useCallback(
    (index, paramName, selectedValueAxis) => {
      let newValueAxis = selectedValueAxis;
      if (selectedValueAxis === 'new') {
        const axis = addValueAxis();
        newValueAxis = axis.id;
      }

      setParamByIndex('seriesParams', index, paramName, newValueAxis);

      updateAxisTitle();
    },
    [addValueAxis, setParamByIndex, updateAxisTitle]
  );

  const schemaName = vis.type.schemas.metrics[0].name;
  const metrics = useMemo(() => {
    return aggs.bySchemaName(schemaName);
  }, [schemaName, aggs]);

  const firstValueAxesId = stateParams.valueAxes[0].id;

  useEffect(() => {
    const updatedSeries = metrics.map((agg) => {
      const params = stateParams.seriesParams.find((param) => param.data.id === agg.id);
      const label = agg.makeLabel();

      // update labels for existing params or create new one
      if (params) {
        return {
          ...params,
          data: {
            ...params.data,
            label,
          },
        };
      } else {
        const series = makeSerie(
          agg.id,
          label,
          firstValueAxesId,
          stateParams.seriesParams[stateParams.seriesParams.length - 1]
        );
        return series;
      }
    });

    setValue('seriesParams', updatedSeries);
    updateAxisTitle(updatedSeries);
  }, [metrics, firstValueAxesId, setValue, stateParams.seriesParams, updateAxisTitle]);

  return isTabSelected ? (
    <>
      <SeriesPanel
        changeValueAxis={changeValueAxis}
        setParamByIndex={setParamByIndex}
        seriesParams={stateParams.seriesParams}
        valueAxes={stateParams.valueAxes}
        vis={vis}
      />
      <EuiSpacer size="s" />
      <ValueAxesPanel
        addValueAxis={addValueAxis}
        isCategoryAxisHorizontal={isCategoryAxisHorizontal}
        removeValueAxis={removeValueAxis}
        onValueAxisPositionChanged={onValueAxisPositionChanged}
        setParamByIndex={setParamByIndex}
        setMultipleValidity={props.setMultipleValidity}
        seriesParams={stateParams.seriesParams}
        valueAxes={stateParams.valueAxes}
        vis={vis}
      />
      <EuiSpacer size="s" />
      <CategoryAxisPanel
        axis={stateParams.categoryAxes[0]}
        onPositionChanged={onCategoryAxisPositionChanged}
        setCategoryAxis={setCategoryAxis}
        vis={vis}
      />
    </>
  ) : null;
}