/* * Copyright OpenSearch Contributors * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ import { get, set, curry, PropertyName } from 'lodash'; import { Dispatch, SetStateAction } from 'react'; function resolveValue(value: T | (() => T)) { return typeof value === 'function' ? (value as Function)() : value; } /** * Update an element in an sub array * @param setStateCallback setState function * @param path lodash path, e.g. [0, indexPattern] or "[0].indexPattern" * @param newValue value to be updated * * e.g. * currentState = [{a: 1}, {a: 2}] * path = [0, 'a'] * newValue = 10 * The newState will be [{a: 10}, {a: 2}] */ export function updateElementInArray( setStateCallback: Dispatch>, path: PropertyName | PropertyName[], newValue: T | (() => T) ) { setStateCallback((prevState) => { const newState = [...prevState]; set(newState, path, resolveValue(newValue)); return newState; }); } export const updateElementInArrayHandler = curry(updateElementInArray); /** * Append an element to an array (or sub array indicating by path) * @param setStateCallback setState function * @param path lodash path, e.g. [0, indexPattern] or "[0].indexPattern", use [] to indicate root level * @param newValue value to be updated * * e.g. * Scenario 1, path = [] to append to root level array * currentState = [1, 2] * path = [] * newValue = 3 * The newState will be [1, 2, 3] * * Scenario 2, path != [] to append to sub array * currentState = [{a: [1, 2]}, {a: [3, 4]}] * path = [0, 'a'] * newValue = 5 * The newState will be [{a: [1, 2, 5]}, {a: [3, 4]}] */ export function appendElementToArray( setStateCallback: Dispatch>, path: PropertyName | PropertyName[], newValue: T | (() => T) ) { const resolvedNewValue = resolveValue(newValue); setStateCallback((prevState) => { if ((path as PropertyName[]).length === 0) { return [...prevState, resolvedNewValue]; } else { const newArray = [...(get(prevState, path) as T[]), resolvedNewValue]; const newState = [...prevState]; set(newState, path, newArray); return newState; } }); } /** * Remove an element from an array (or sub array indicating by path) * @param setStateCallback setState function * @param path lodash path, e.g. [0, indexPattern] or "[0].indexPattern", use [] to indicate root level * @param index index of element to be removed * * e.g. * Scenario 1, path = [] to append to root level array * currentState = [1, 2] * path = [] * index = 0 * The newState will be [2] * * Scenario 2, path != [] to append to sub array * currentState = [{a: [1, 2]}, {a: [3, 4]}] * path = [0, 'a'] * index = 1 * The newState will be [{a: [1]}, {a: [3, 4]}] */ export function removeElementFromArray( setStateCallback: Dispatch>, path: PropertyName | PropertyName[], index: number ) { setStateCallback((prevState) => { if ((path as PropertyName[]).length === 0) { const newState = [...prevState]; newState.splice(index, 1); return newState; } else { const newArray = [...(get(prevState, path) as T[])]; newArray.splice(index, 1); const newState = [...prevState]; set(newState, path, newArray); return newState; } }); }