import { Arr, Optional } from '@ephox/katamari'; import * as PositionArray from '../alien/PositionArray'; const output = (within: T[], extra: T[], withinWidth: number): Widths => ({ within, extra, withinWidth }); interface Pos { readonly element: T; readonly start: number; readonly finish: number; readonly width: number; } interface Widths { readonly within: T[]; readonly extra: T[]; readonly withinWidth: number; } type GetLengthFunc = (comp: T) => number; const apportion = (units: T[], total: number, len: GetLengthFunc): Widths> => { const parray: Pos[] = PositionArray.generate(units, (unit, current) => { const width = len(unit); return Optional.some({ element: unit, start: current, finish: current + width, width }); }); const within = Arr.filter(parray, (unit) => unit.finish <= total); const withinWidth = Arr.foldr(within, (acc, el) => acc + el.width, 0); const extra = parray.slice(within.length); return { within, extra, withinWidth }; }; const toUnit = (parray: Pos[]) => Arr.map(parray, (unit) => unit.element); const fitLast = (within: Pos[], extra: Pos[], withinWidth: number) => { const fits = toUnit(within.concat(extra)); return output(fits, [] as T[], withinWidth); }; const overflow = (within: Pos[], extra: Pos[], overflower: T, withinWidth: number) => { const fits = toUnit(within).concat([ overflower ]); return output(fits, toUnit(extra), withinWidth); }; const fitAll = (within: Pos[], extra: Pos[], withinWidth: number) => output(toUnit(within), [], withinWidth); const tryFit = (total: number, units: T[], len: GetLengthFunc): Optional>> => { const divide = apportion(units, total, len); return divide.extra.length === 0 ? Optional.some(divide) : Optional.none(); }; const partition = (total: number, units: T[], len: GetLengthFunc, overflower: T): Widths => { // Firstly, we try without the overflower. const divide = tryFit(total, units, len).getOrThunk(() => // If that doesn't work, overflow apportion(units, total - len(overflower), len) ); const within = divide.within; const extra = divide.extra; const withinWidth = divide.withinWidth; if (extra.length === 1 && extra[0].width <= len(overflower)) { return fitLast(within, extra, withinWidth); } else if (extra.length >= 1) { return overflow(within, extra, overflower, withinWidth); } else { return fitAll(within, extra, withinWidth); } }; export { partition };