1 import type { MaybeNull } from '@proton/pass/types';
3 export type InterpolationItem<Value, Cluster> =
4 | { type: 'entry'; entry: Value }
5 | { type: 'interpolation'; cluster: Cluster };
7 export type Interpolation<Value, Cluster> = {
10 interpolation: InterpolationItem<Value, Cluster>[];
11 interpolationIndexes: number[];
14 type InterpolationOptions<T, Cluster> = {
16 shouldInterpolate: (entry: T, cluster: Cluster) => boolean;
17 fallbackCluster?: Cluster;
20 /* Returns an interpolated array based on clusters and
21 * interpolation rules. Interpolations are between adjacent
22 * clusters where "shouldInterpolate" is true. `entries` &
23 * `clusters` must be sorted for best results. */
24 export const arrayInterpolate = <Value, Cluster>(
26 { clusters, shouldInterpolate, fallbackCluster }: InterpolationOptions<Value, Cluster>
27 ): Interpolation<Value, Cluster> => {
28 const initial: Interpolation<Value, Cluster> = {
30 interpolationIndexes: [],
35 return entries.reduce<Interpolation<Value, Cluster>>((acc, entry) => {
36 if (acc.clusters.length === 0) {
37 acc.interpolation.push({ type: 'entry', entry });
41 const cluster = acc.clusters[0];
43 let interpolate: MaybeNull<Cluster> = null;
45 if (shouldInterpolate(entry, cluster)) {
46 interpolate = !acc.interpolated ? cluster : null;
48 const rest = acc.clusters.slice(1);
49 const nextClusterIdx = rest.findIndex((_cluster) => shouldInterpolate(entry, _cluster));
50 const nextCluster = rest?.[nextClusterIdx];
52 acc.clusters = nextCluster ? rest.slice(nextClusterIdx) : [];
53 interpolate = nextCluster ?? fallbackCluster ?? null;
57 acc.interpolated = true;
58 acc.interpolation.push({ type: 'interpolation', cluster: interpolate });
59 acc.interpolationIndexes.push(acc.interpolation.length - 1);
62 acc.interpolation.push({ type: 'entry', entry });