{"version":3,"file":"useFormTable.hook.mjs","sources":["../../src/lib/hooks/useFormTable.hook.js"],"sourcesContent":["/*\nCopyright 2019 Iguazio Systems Ltd.\n\nLicensed under the Apache License, Version 2.0 (the \"License\") with\nan addition restriction as set forth herein. You may not use this\nfile except in compliance with the License. You may obtain a copy of\nthe License at http://www.apache.org/licenses/LICENSE-2.0.\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\nimplied. See the License for the specific language governing\npermissions and limitations under the License.\n\nIn addition, you may not use the software for any purposes that are\nillegal under applicable law, and the grant of the foregoing license\nunder the Apache 2.0 license is conditioned upon your compliance with\nsuch restriction.\n*/\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { get, omit } from 'lodash'\nimport { ARRAY_ERROR } from 'final-form'\n\nexport const useFormTable = (formState, exitEditModeTriggerItem, onExitEditModeCallback) => {\n  // `editingItem` should contain the `data` object with all fields that are used in the `formState`.\n  // Properties that aren't used in the `formState` should be placed directly in the `editingItem` object\n  // `editingItem` also has an `ui` property which is used internally in this hook\n  //\n  // e.g.\n  // editingItem = {\n  //   data: {\n  //     <fieldName>: <fieldValue>,\n  //     <fieldName2>: <fieldValue2>\n  //   },\n  //   <anotherProperty>: <anotherPropertyValue>\n  //   ui: {\n  //     isNew: <true|false>, // `true` if we are creating a new row, if we are editing it's `false`\n  //     fieldsPath: <\"the.path\">, // the path where table data is placed in the `formState`\n  //     index: <0|1|...> // index of the editing row\n  //   }\n  // }\n  const [editingItem, setEditingItem] = useState(null)\n  const editingItemRef = useRef(null)\n  const editingItemErrorsRef = useRef(null)\n  const formStateRef = useRef(null)\n  const bottomScrollRef = useRef(null)\n  const onExitEditModeCallbackRef = useRef(onExitEditModeCallback)\n\n  useLayoutEffect(() => {\n    const tableErrors = get(formState?.errors, editingItem?.ui.fieldsPath, [])\n    editingItemErrorsRef.current = get(tableErrors, editingItem?.ui.index, null)\n  }, [editingItem?.ui.fieldsPath, editingItem?.ui.index, formState?.errors])\n\n  useLayoutEffect(() => {\n    formStateRef.current = formState\n  }, [formState])\n\n  useLayoutEffect(() => {\n    onExitEditModeCallbackRef.current = onExitEditModeCallback\n  }, [onExitEditModeCallback])\n\n  const exitEditMode = () => {\n    if (editingItemRef.current?.data) {\n      Object.entries(editingItemRef.current?.data).forEach(([fieldName]) => {\n        formStateRef.current?.form.mutators.setFieldState(\n          `${editingItemRef.current?.ui.fieldsPath}[${editingItemRef.current?.ui.index}].data.${fieldName}`,\n          {\n            modified: false\n          }\n        )\n      })\n    }\n\n    editingItemRef.current = null\n    setEditingItem(null)\n    onExitEditModeCallbackRef?.current && onExitEditModeCallbackRef.current()\n  }\n\n  useEffect(() => {\n    const applyOrDiscardOrDeleteInEffect = () => {\n      if (editingItemRef?.current) {\n        if (!editingItemErrorsRef.current) {\n          exitEditMode()\n        } else {\n          if (editingItemRef.current?.ui?.isNew) {\n            const values = get(formStateRef.current.values, editingItemRef.current?.ui.fieldsPath)\n\n            if (values?.length > 1) {\n              formStateRef.current.form.mutators.remove(\n                editingItemRef.current?.ui.fieldsPath,\n                editingItemRef.current?.ui.index\n              )\n            } else {\n              formStateRef.current.form.change(editingItemRef.current?.ui.fieldsPath, [])\n            }\n          } else {\n            formStateRef.current.form.mutators.update(\n              editingItemRef.current?.ui.fieldsPath,\n              editingItemRef.current?.ui.index,\n              omit(editingItemRef.current, ['ui'])\n            )\n          }\n\n          exitEditMode()\n        }\n      }\n    }\n\n    return () => {\n      applyOrDiscardOrDeleteInEffect()\n    }\n  }, [exitEditModeTriggerItem])\n\n  const addNewRow = (event, fields, fieldsPath, newItem) => {\n    applyOrDiscardOrDelete(event)\n    formStateRef.current.form.mutators.push(fieldsPath, newItem)\n    setEditingItem(() => {\n      const newEditingItem = {\n        ...newItem,\n        ui: {\n          isNew: true,\n          fieldsPath,\n          index: fields.value?.length || 0\n        }\n      }\n      editingItemRef.current = newEditingItem\n\n      return newEditingItem\n    })\n\n    scrollIntoView()\n  }\n\n  const applyChanges = (event, index) => {\n    if (editingItemRef.current) {\n      if (!editingItemErrorsRef.current) {\n        if (editingItemRef.current?.ui.isNew) {\n          scrollIntoView()\n        }\n\n        exitEditMode()\n      } else {\n        // Mark all empty fields as `modified` in order to highlight the error if the field is invalid\n        Object.entries(editingItemErrorsRef.current?.data).forEach(([fieldName]) => {\n          formStateRef.current?.form.mutators.setFieldState(\n            `${editingItemRef.current?.ui.fieldsPath}[${index}].data.${fieldName}`,\n            {\n              modified: true\n            }\n          )\n        })\n      }\n    }\n  }\n\n  const deleteRow = (event, fieldsPath, index) => {\n    if (editingItemRef.current && index !== editingItemRef.current.ui.index) {\n      applyOrDiscardOrDelete(event)\n    }\n\n    const values = get(formStateRef.current.values, fieldsPath)\n\n    if (values?.length > 1) {\n      formStateRef.current.form.mutators.remove(fieldsPath, index)\n    } else {\n      formStateRef.current.form.change(fieldsPath, [])\n    }\n\n    exitEditMode()\n    event && event.stopPropagation()\n  }\n\n  const discardChanges = (event, fieldsPath, index) => {\n    formStateRef.current.form.mutators.update(\n      fieldsPath,\n      index,\n      omit(editingItemRef.current, ['ui'])\n    )\n    exitEditMode()\n    event && event.stopPropagation()\n  }\n\n  const discardOrDelete = (event, fieldsPath, index) => {\n    if (!editingItemRef.current || editingItemRef.current?.ui?.isNew) {\n      deleteRow(event, fieldsPath, index)\n    } else {\n      discardChanges(event, fieldsPath, index)\n    }\n  }\n\n  const applyOrDiscardOrDelete = (event = null) => {\n    if (editingItemRef?.current) {\n      if (!editingItemErrorsRef.current) {\n        applyChanges(event, editingItemRef.current?.ui.index)\n      } else {\n        discardOrDelete(\n          event,\n          editingItemRef.current?.ui.fieldsPath,\n          editingItemRef.current?.ui.index\n        )\n      }\n    }\n  }\n\n  const enterEditMode = (event, fields, fieldsPath, index) => {\n    applyOrDiscardOrDelete(event)\n\n    setTimeout(() => {\n      const editItem = fields.value[index]\n\n      setEditingItem(() => {\n        const newEditingItem = { ...editItem, ui: { fieldsPath, index } }\n        editingItemRef.current = newEditingItem\n\n        return newEditingItem\n      })\n    })\n  }\n\n  const scrollIntoView = () => {\n    if (bottomScrollRef.current) {\n      setTimeout(() => {\n        bottomScrollRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' })\n      })\n    }\n  }\n\n  const isCurrentRowEditing = rowPath => {\n    return (\n      editingItemRef?.current &&\n      `${editingItemRef.current.ui.fieldsPath}[${editingItemRef.current.ui.index}]` === rowPath\n    )\n  }\n\n  const getTableArrayErrors = fieldsPath => {\n    if (formState.submitFailed && formState.invalid) {\n      return get(formState, `errors.${fieldsPath}.${ARRAY_ERROR}`, [])\n    } else {\n      return []\n    }\n  }\n\n  return {\n    addNewRow,\n    applyChanges,\n    applyOrDiscardOrDelete,\n    bottomScrollRef,\n    deleteRow,\n    discardChanges,\n    discardOrDelete,\n    editingItem,\n    editingItemRef,\n    enterEditMode,\n    exitEditMode,\n    getTableArrayErrors,\n    isCurrentRowEditing\n  }\n}\n"],"names":["useFormTable","formState","exitEditModeTriggerItem","onExitEditModeCallback","editingItem","setEditingItem","useState","editingItemRef","useRef","editingItemErrorsRef","formStateRef","bottomScrollRef","onExitEditModeCallbackRef","useLayoutEffect","tableErrors","get","exitEditMode","_a","_b","fieldName","_c","useEffect","applyOrDiscardOrDeleteInEffect","values","_d","_e","_f","_g","_h","omit","addNewRow","event","fields","fieldsPath","newItem","applyOrDiscardOrDelete","newEditingItem","scrollIntoView","applyChanges","index","deleteRow","discardChanges","discardOrDelete","enterEditMode","editItem","ARRAY_ERROR","rowPath"],"mappings":"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"}