import { checkedData, compareCheckedData, compareNestedCheckedData, NESTED_PARENT_KEY, } from './helperCheckboxFunctions' import styles from './_table.module.scss' /** * Adjusts the right visual border of the end column when customColumns && nestedRows are used together. * If rightStickyCountFromConfig is set, padding is not added to the last cell of Custom or Nested Columns. */ export const getCustomColumnStickyStyles = ( nthCellFromRight: number, stickyRightCount: number, rightStickyCountFromConfig?: number, ): string => { return nthCellFromRight === stickyRightCount && !rightStickyCountFromConfig ? `${styles.firstStickyCellRight} ${styles.extraLeftPadding}` : '' } type IsParentPartiallyCheckedParams = { nestedDataKey: keyof DataItem nestedRowProps: { nestedData: { [key: string]: DataItem[] } showCheckboxes?: boolean } checkedBoxes: DataItem[] dataKey: keyof DataItem } /** * Returns whether a parent row should show a partially-checked (indeterminate) state * when the new nested row toggle is on and it has some but not all children checked. */ export const isParentPartiallyChecked = ( parentData: DataItem, params: IsParentPartiallyCheckedParams, ): boolean => { const { nestedDataKey, nestedRowProps, checkedBoxes, dataKey } = params if (nestedRowProps.showCheckboxes === false) { return false } const parentKey = parentData[nestedDataKey] const nestedRows = nestedRowProps.nestedData[parentKey as unknown as string] if (!nestedRows || nestedRows.length === 0) { return false } const checkedChildrenCount = nestedRows.filter((nestedRow) => checkedBoxes.some((checkedBox) => compareNestedCheckedData(nestedRow, checkedBox, dataKey, parentKey), ), ).length const isParentChecked = checkedBoxes.some((checkedBox) => compareCheckedData(parentData, checkedBox, dataKey), ) return ( checkedChildrenCount > 0 && checkedChildrenCount < nestedRows.length && !isParentChecked ) } type ComputeSomeCheckedAllCheckedParams = { data: DataItem[] checkedBoxes: DataItem[] totalRowKey: keyof DataItem dataKey: keyof DataItem newNestedRowToggle: boolean nestedRowProps: { nestedData: { [key: string]: DataItem[] } showCheckboxes?: boolean } | null nestedDataKey: keyof DataItem | undefined isParentPartiallyCheckedFn: (parentData: DataItem) => boolean } /** * Computes someChecked and allChecked for table header checkbox state, * including nested rows and partially-checked parents when the new nested row toggle is on. */ export const computeSomeCheckedAllChecked = ({ data, checkedBoxes, totalRowKey, dataKey, newNestedRowToggle, nestedRowProps, nestedDataKey, isParentPartiallyCheckedFn, }: ComputeSomeCheckedAllCheckedParams): { someChecked: boolean allChecked: boolean } => { if (checkedBoxes.length === 0) { return { someChecked: false, allChecked: false } } const { someChecked, allChecked } = checkedData({ data, checkedBoxes, totalRowKey, dataKey, }) let hasSomeChecked = someChecked let hasAllChecked = allChecked // this will remove after toggle- 'standardtable_new_nested_rows_update' clean up — use block when nestedRowProps && nestedDataKey only (drop newNestedRowToggle). if ( newNestedRowToggle && nestedRowProps && nestedDataKey && (nestedRowProps.showCheckboxes === undefined ? false : nestedRowProps.showCheckboxes) ) { let hasCheckedNestedRows = false let totalNestedRowCount = 0 let checkedNestedRowCount = 0 for (const row of data.filter((item) => !item[totalRowKey])) { const parentKey = row[nestedDataKey] const nestedRows = nestedRowProps.nestedData[parentKey as unknown as string] if (!nestedRows?.length) continue totalNestedRowCount += nestedRows.length for (const nestedRow of nestedRows) { const isChecked = checkedBoxes.some((checkedBox) => compareNestedCheckedData(nestedRow, checkedBox, dataKey, parentKey), ) if (isChecked) { hasCheckedNestedRows = true checkedNestedRowCount++ } } } const allNestedRowsChecked = totalNestedRowCount > 0 && checkedNestedRowCount === totalNestedRowCount const hasPartiallyCheckedParents = data.some((row) => isParentPartiallyCheckedFn(row), ) if (hasAllChecked && totalNestedRowCount > 0) { hasAllChecked = hasAllChecked && allNestedRowsChecked } else if (!hasAllChecked && totalNestedRowCount > 0) { const allParentsChecked = data .filter((item) => !item[totalRowKey]) .every((parentRow) => checkedBoxes.some((checkedBox) => compareCheckedData(parentRow, checkedBox, dataKey), ), ) hasAllChecked = allParentsChecked && allNestedRowsChecked } if (!hasAllChecked) { hasSomeChecked = hasSomeChecked || hasCheckedNestedRows || hasPartiallyCheckedParents } else { hasSomeChecked = false } } return { someChecked: hasSomeChecked, allChecked: hasAllChecked } } type GetDisplayCheckedCountParams = { checkedBoxes: DataItem[] nestedRowProps: { nestedData: { [key: string]: DataItem[] } showCheckboxes?: boolean } | null nestedDataKey: keyof DataItem | undefined dataKey: keyof DataItem newNestedRowToggle: boolean } /** * Returns the count of selected items to display in the selection banner. * * this will remove after toggle- 'standardtable_new_nested_rows_update' clean up — remove * newNestedRowToggle guard and keep the nested logic unconditionally. * * - Toggle OFF: raw checkedBoxes.length (legacy behavior, no change) * - Toggle ON + showCheckboxes !== true: raw checkedBoxes.length (children not in checkedBoxes) * - Toggle ON + showCheckboxes === true: child rows only; parent rows with no children are * also counted as leaf-level selections */ export const getDisplayCheckedCount = ({ checkedBoxes, nestedRowProps, nestedDataKey, newNestedRowToggle, }: GetDisplayCheckedCountParams): number => { // this will remove after toggle- 'standardtable_new_nested_rows_update' clean up — remove guard. if (!newNestedRowToggle || !nestedRowProps || !nestedDataKey) { return checkedBoxes.length } if (nestedRowProps.showCheckboxes !== true) { // showCheckboxes false or undefined — children are never stored in checkedBoxes, // so raw length is correct (same as pre-toggle behavior). return checkedBoxes.length } // showCheckboxes: true only — count child rows (have _nestedParentKey) + // parent rows that have no children (leaf-level, nothing to expand into) const parentKeysWithChildren = new Set() for (const [parentKey, rows] of Object.entries(nestedRowProps.nestedData)) { if (rows?.length > 0) { parentKeysWithChildren.add(parentKey) } } return checkedBoxes.filter((cb) => { const cbParentKey = (cb as Record)[NESTED_PARENT_KEY] if (cbParentKey !== undefined) return true const ownParentKey = cb[nestedDataKey] as unknown as string return !parentKeysWithChildren.has(ownParentKey) }).length }