import React from 'react' import { type RowStylingProps } from './StandardTable.models' export type DataItemExtended = DataItem & { name?: string } & Partial export const getTotalOptionsDefaultValue = ( value?: React.ReactNode, totalCheckboxOptions?: number | null, ): number | undefined => { if (totalCheckboxOptions) { return totalCheckboxOptions } let headerValue: number | undefined if (typeof value === 'number') { headerValue = value } else if (typeof value === 'string') { headerValue = Number(value) } else if (React.isValidElement(value)) { const element = value as React.ReactElement<{ children?: React.ReactNode value?: React.ReactNode }> headerValue = Number(element.props.children ?? element.props.value) } return headerValue } export const compareCheckedData = ( baseData: DataItem, checkedData: DataItem, dataKey: keyof DataItem, ): boolean => dataKey ? baseData?.[dataKey] === checkedData?.[dataKey] : (baseData as { name?: string }).name === (checkedData as { name?: string }).name /** * Synthetic field added to nested child rows stored in checkedBoxes so they can be * scoped to their parent group. Prevents cross-group false-positive matches when two * child rows from different parents share the same dataKey value. * * this will remove after toggle- 'standardtable_new_nested_rows_update' clean up — * keep this constant and compareNestedCheckedData (drop the toggle guards that gate them). */ export const NESTED_PARENT_KEY = '_nestedParentKey' as const /** * Parent-aware variant of compareCheckedData for nested child rows. * When the checkedBox carries a _nestedParentKey, also verifies it matches parentKey * so same-dataKey children across different parent groups are not confused. */ export const compareNestedCheckedData = ( nestedRow: DataItem, checkedBox: DataItem, dataKey: keyof DataItem, parentKey: unknown, ): boolean => { if (!compareCheckedData(nestedRow, checkedBox, dataKey)) return false const boxParentKey = (checkedBox as Record)[ NESTED_PARENT_KEY ] return boxParentKey === undefined || boxParentKey === parentKey } type HandleCheck = { checkboxValue: boolean checkedItemData: DataItemExtended name?: string } export const useHandleCheck = ({ checkedBoxes, setCheckedBoxes, setDisplayData, data, dataKey, totalRowKey, setCheckAll, setCheckAllLoaded, enableCheckAll, setMakeCallout, mounted, }: { checkedBoxes: (DataItem & { checked?: boolean })[] setCheckedBoxes: React.Dispatch< React.SetStateAction<(DataItem & { checked?: boolean })[]> > setDisplayData: React.Dispatch< React.SetStateAction<(DataItem & { checked?: boolean })[]> > data: (DataItem & { checked?: boolean })[] dataKey: keyof DataItem totalRowKey: keyof DataItem setCheckAll: React.Dispatch> setCheckAllLoaded: React.Dispatch> enableCheckAll?: boolean setMakeCallout: React.Dispatch< React.SetStateAction<'' | 'single' | 'checkAll'> > mounted: boolean }) => { const handleCheck = ({ checkboxValue, checkedItemData, name, }: HandleCheck) => { if (!mounted) return const updateCheckedBoxes = ( newCheckedBoxes: (DataItem & { checked?: boolean })[], ) => { setCheckedBoxes(newCheckedBoxes?.filter((item) => !item[totalRowKey])) setDisplayData((displayData) => displayData.map((item) => ({ ...item, checked: newCheckedBoxes.some((checkedBox) => compareCheckedData(item, checkedBox, dataKey), ), })), ) } if (name === 'all' || name === 'allLoaded') { if (checkboxValue) { setCheckAllLoaded(true) const updatedCheckedBoxes = [...checkedBoxes, ...data] updateCheckedBoxes(updatedCheckedBoxes) } else { setCheckAll(false) setCheckAllLoaded(false) const updatedCheckedBoxes = checkedBoxes.filter( (checkedBox) => !data.some((dataItem) => compareCheckedData(dataItem, checkedBox, dataKey), ), ) updateCheckedBoxes(updatedCheckedBoxes) } if (enableCheckAll) { setMakeCallout(name === 'all' ? 'checkAll' : 'single') } else { setMakeCallout('checkAll') } } else { if (checkboxValue) { const updatedCheckedBoxes = [ ...checkedBoxes, { ...checkedItemData, checked: checkboxValue }, ] updateCheckedBoxes(updatedCheckedBoxes) } else { const index = checkedBoxes.findIndex((element) => compareCheckedData(checkedItemData, element, dataKey), ) const updatedCheckedBoxes = [ ...checkedBoxes.slice(0, index), ...checkedBoxes.slice(index + 1), ] updateCheckedBoxes(updatedCheckedBoxes) setCheckAll(false) setCheckAllLoaded(false) } setMakeCallout('single') } } return handleCheck } type CheckedDataItem = { data: DataItemExtended[] checkedBoxes: DataItemExtended[] totalRowKey: keyof DataItem dataKey: keyof DataItem } export const checkedData = ({ data, checkedBoxes, totalRowKey, dataKey, }: CheckedDataItem): { someChecked: boolean allChecked: boolean } => { const hasData = data?.length > 0 const hasCheckedBoxes = checkedBoxes?.length > 0 let allChecked = false let someChecked = false if (hasData && hasCheckedBoxes) { allChecked = data ?.filter((dataElement) => !dataElement[totalRowKey]) .every((dataElement) => checkedBoxes.find((checkedBox) => compareCheckedData(dataElement, checkedBox, dataKey), ), ) someChecked = checkedBoxes ?.filter((checkbox: DataItem) => !checkbox[totalRowKey]) .some((checkedBox) => data ?.filter((data: DataItem) => !data[totalRowKey]) .find((dataElement: DataItem & { name?: string }) => compareCheckedData(dataElement, checkedBox, dataKey), ), ) } return { allChecked, someChecked: someChecked && !allChecked } } export const useIsChecked = ({ checkedBoxes, dataKey, }: { checkedBoxes: DataItemExtended[] dataKey: keyof DataItem }) => { const isChecked = (data: DataItemExtended) => checkedBoxes.find((checkedBox) => { return dataKey ? checkedBox?.[dataKey] === data?.[dataKey] : checkedBox.name === data.name }) return isChecked }