All Downloads are FREE. Search and download functionalities are using the official Maven repository.

components.form.utils.task-utils.tsx Maven / Gradle / Ivy

import { Crumb } from 'components/breadcrumbs/Crumb';
import {
    basedOnEntries, getProcessName, getTaskSimpleName
} from 'components/form/utils/form-utils';
import { FormInfo } from 'contexts/FormInfoContext';
import { T } from 'helpers/translator';
import { Field } from 'types/graphql';
import Types from 'types/types';

import { useTheme } from '@mui/material/styles';

export function setStepIconTabIndices(delay: number = 0) {
  const updateIndices = () => {
    //selector for checkboxes: 'input:not([type="hidden"]):not([type="checkbox"][name="multiple"]),button[type="submit"]'
    document 
      .querySelectorAll('.step button.MuiButtonBase-root.MuiIconButton-root,.ps__thumb-x')
      .forEach( (item, index) => {
        item.setAttribute("tabindex", "-1")
      });
  }
  setTimeout(updateIndices, delay)
}


export function getTaskCrumbs(translator: T, formInfo: FormInfo): Crumb[] {

  return [
    {
      key: "default:start",
      label: translator.toMenuLabel('start')!, 
      link: "/gears/processes/start",
    },
    {
      key: formInfo.processDefinition.key,
      label: getProcessName(translator, formInfo)!,
    },
    { 
      key: formInfo.taskId || (formInfo.processDefinition.key + "-start"),
      label: getTaskSimpleName(translator, formInfo)! 
    }
  ]
}

export type BreakPointType = "free" | "normal"

function determineBreakpointType(width: number): BreakPointType {
  const theme  = useTheme()
  const points = theme.breakpoints.values
  switch (true) {
    case points.xs > width: return "normal"
    case points.sm > width: return "normal"
    case points.md > width + 200: return "free"
    case points.lg > width + 200: return "free"
    case points.xl > width + 200: return "free"
    default: return "free"
  }
}

function determineFieldBreakpoint(formInfo: FormInfo, field: Field): BreakPointType {
  const type = field.type
  if (type == "MULTIPLE")
    return "free"
  else 
    return "normal"
}

type StepPlanField = {
  field: Field
  type: BreakPointType
}

type StepPlanItemGroups = StepPlanField[][]

type StepPlanItem = {
  basedOn: boolean
  fields: Field[]
  header: boolean
  submit: boolean
  type: BreakPointType
}

export type StepPlan = StepPlanItem[]

/* this function returns a partitioning of fields, identified by a partition type.
   The goal is te separate a multiple input from others types of inputs.
   [field1, field2, ...] => [{type: ..., [field1, field2]}, {type: ..., [fieldn]}, ...]
*/
export const createStepCardPlan = (formInfo: FormInfo) => {
  const combine = (previousField: StepPlanField, currentField: StepPlanField) => previousField.type === currentField.type

  const createPlan = (fields: Field[]): StepPlanItemGroups =>  fields
    // identify the type of each field
    .map((field: Field): StepPlanField => ({ type: determineFieldBreakpoint(formInfo, field), field }))
    // combine adjacent fields of the same type
    .reduce(
      function(prev: StepPlanItemGroups, curr: StepPlanField) {
        if (prev.length && combine(prev[prev.length - 1][0], curr)) {
            prev[prev.length - 1].push(curr);
        } else {
            prev.push([curr]);
        }
        return prev;
      }, []
    );

  const normalize = (result: StepPlanItemGroups): StepPlan => result
    .map(group => { return {
      type  : group[0].type,
      fields: group.map(item => item.field),
      header : false,
      submit : false,
      basedOn : false,
    } } )

  const addHeader = (result: StepPlan): StepPlan => {
    // add header to a partition
    if (result.length > 0 && result[0].type === "normal") {
      // inject header into first partition if it is small
      result[0].header = true
      return result
    } else {
      // add seperate small partition to contain only the header
      return [{ type: "normal", fields: [], header: true, basedOn: false, submit: false }, ...result ]
    }
  }


  const addBasedOn = (result: StepPlan): StepPlan => {
    // add header to a partition
    const basedOn = formInfo.form.values.__basedOn

    if (!basedOn)
      return result

    const basedOnWidth = determineBasedOnWidth(basedOn)
    const basedOnBreakpoint = determineBreakpointType(basedOnWidth)

    if (result.length && result[0].type === basedOnBreakpoint) {
      // inject header into first partition if it is small
      result[0].basedOn = true
      return result
    } else {
      // add seperate small partition to contain only the header
      return [{ type: basedOnBreakpoint, fields: [], header: false, basedOn: true, submit: false }, ...result ]
    }
  }

  const addSubmit = (result: StepPlan): StepPlan => {
    // add header to a partition
    if (result.length && result[result.length -1].type === "normal") {
      result[result.length -1].submit = true
      return result
    } else {
      return [...result, { type: "normal", fields: [], header: false, basedOn: false, submit: true } ]
    }
  }

  const fields: Field[] = Array.isArray(formInfo?.form?.fields) ? formInfo.form.fields : []
  const plan = addSubmit(addHeader(addBasedOn(normalize(createPlan(fields)))))

  // verify plan
  if (plan.length) {
    if (!plan[0].header)
      console.error("A step plan does not include the header in the first partition: %o", plan)
    if (fields.length && !plan[plan.length -1].submit)
      console.error("A step plan does not a submit in the last partition: %o", plan)
  }

  return plan
}

export const determineBasedOnWidth = (basedOn: any, asRow: boolean = false): number => {
  const basedOnColumns = determineBasedOnColumns(basedOn, asRow)
  const width = 150
  return width * basedOnColumns
}

export const determineBasedOnColumns = (basedOn: any, asRow?: boolean) => {
  function computeColumns(basedOn: any, asRow?: boolean): number {
    if (basedOn?.__kind === 'entity')
      return 1
    else if (basedOn?.__kind === 'tuple' || (Types.isObject(basedOn) && basedOnEntries(basedOn).length != 0)) {
      const entries = basedOnEntries(basedOn)
      if (asRow) {
        const entrySizes = entries.map ( ([key, value]) => computeColumns(value))
        return entrySizes.reduce((partialSum, a) => partialSum + a, 0)
      } else {
        const entrySizes = entries.map ( ([key, value]) => 1 + computeColumns(value))
        return Math.max(...[...entrySizes,2])
      }
    } else if (Array.isArray(basedOn)) {
      if (Array.isArray(basedOn[0])) {
        // list : max the rows, sum the columns
        const rowWidths =  basedOn
          .map(row => row
            .map(computeColumns)
            .reduce((partialSum: number, a: number): number => partialSum + a, 0)
          )

        return Math.max(...rowWidths)
      } else if (Types.isObject(basedOn[0])) {
        return Math.max(...(basedOn.map(row => computeColumns(row, true))))
      }
      else
        return basedOn.map(row => computeColumns(row)).reduce((partialSum, a) => partialSum + a, 0)
    }
    else if (basedOn?.__kind === 'entity')
      return 1
    else
      return 1
  }

  return basedOn == null || basedOn == undefined ? 0 : computeColumns(basedOn, asRow)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy