/** * WordPress dependencies */ import { useEffect, useRef, useState } from '@safe-wordpress/element'; import { _x, sprintf } from '@safe-wordpress/i18n'; /** * External dependencies */ import { findIndex, findLastIndex, zip } from 'lodash'; import { usePluginSetting } from '@nab/data'; import { getLetter, sum, transformAlternativeChancesToAllocatedSlots, } from '@nab/utils'; import type { Alternative, Maybe } from '@nab/types'; /** * Internal dependencies */ import './style.scss'; import { Tooltip } from '../tooltip'; export type AlternativeDistributionSettingProps = { readonly alternatives: ReadonlyArray< Alternative >; readonly updateCuts?: ( alternatives: ReadonlyArray< number > ) => void; }; export const AlternativeDistributionSetting = ( { alternatives, updateCuts, }: AlternativeDistributionSettingProps ): JSX.Element | null => { const visible = usePluginSetting( 'isAlternativeDistributionAllowed' ); const maxCombinations = usePluginSetting( 'maxCombinations' ); const barRef = useRef< HTMLDivElement | null >( null ); const [ barWidth, setBarWidth ] = useState( 0 ); useEffect( () => { const element = barRef.current; if ( ! element ) { return; } const updateWidth = () => { setBarWidth( element.getBoundingClientRect().width ); }; updateWidth(); const resizeObserver = new ResizeObserver( updateWidth ); resizeObserver.observe( element ); return () => { resizeObserver.disconnect(); }; }, [] ); const sizes = getDistribution( alternatives, maxCombinations ); const allocatedSlots = transformAlternativeChancesToAllocatedSlots( sizes, maxCombinations ); if ( ! visible || allocatedSlots.length !== alternatives.length ) { return null; } const alternativeIds = alternatives.map( ( a ) => a.id ); const data = zip( sizes, alternativeIds, allocatedSlots ); const onMoveLeft = ( index: number ) => { if ( ! updateCuts ) { return undefined; } if ( index >= data.length - 1 ) { return undefined; } const left = allocatedSlots.slice( 0, index + 1 ); if ( sum( left ) <= left.length ) { return undefined; } return () => { const lastIndex = findLastIndex( left, ( v ) => v > 1 ); const newAllocatedSlots = [ ...allocatedSlots ]; // @ts-expect-error Index is within range. newAllocatedSlots[ lastIndex ]--; // @ts-expect-error Index is within range. newAllocatedSlots[ index + 1 ]++; updateCuts( newAllocatedSlots ); }; }; const onMoveRightIndex = ( index: number ) => { if ( ! updateCuts ) { return undefined; } if ( index >= data.length - 1 ) { return undefined; } const right = allocatedSlots.slice( index + 1, allocatedSlots.length ); if ( sum( right ) <= right.length ) { return undefined; } return () => { const firstIndex = findIndex( right, ( v ) => v > 1 ); const newAllocatedSlots = [ ...allocatedSlots ]; // @ts-expect-error Index is within range. newAllocatedSlots[ index ]++; // @ts-expect-error Index is within range. newAllocatedSlots[ index + firstIndex + 1 ]--; updateCuts( newAllocatedSlots ); }; }; return (