/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import {clamp} from './clamp';
/*** Single-axis offset and length state.** ```* contentStart containerStart containerEnd contentEnd* |<----------offset| | |* |<-------------------length------------------->|* ```*/export type ScrollState = {
offset: number,
length: number,
};function clampOffset(state: ScrollState, containerLength: number): ScrollState {
return {
offset: clamp(-(state.length - containerLength), 0, state.offset),
length: state.length,
};}function clampLength({
state,minContentLength,maxContentLength,containerLength,}: {state: ScrollState,minContentLength: number,maxContentLength: number,containerLength: number,}): ScrollState {
return {
offset: state.offset,
length: clamp(
Math.max(minContentLength, containerLength),
Math.max(containerLength, maxContentLength),
state.length,
),};}/*** Returns `state` clamped such that:* - `length`: you won't be able to zoom in/out such that the content is* shorter than the `containerLength`.* - `offset`: content remains in `containerLength`.*/export function clampState({
state,
minContentLength,
maxContentLength,
containerLength,
}: {state: ScrollState,
minContentLength: number,
maxContentLength: number,
containerLength: number,
}): ScrollState {
return clampOffset(
clampLength({
state,
minContentLength,
maxContentLength,
containerLength,
}),containerLength,
);}export function translateState({
state,
delta,
containerLength,
}: {state: ScrollState,
delta: number,
containerLength: number,
}): ScrollState {
return clampOffset(
{offset: state.offset + delta,
length: state.length,
},containerLength,
);}/*** Returns a new clamped `state` zoomed by `multiplier`.** The provided fixed point will also remain stationary relative to* `containerStart`.** ```* contentStart containerStart fixedPoint containerEnd* |<---------offset-| x |* |-fixedPoint-------------------------------->x |* |-fixedPointFromContainer->x |* |<----------containerLength----------->|* ```*/export function zoomState({
state,
multiplier,
fixedPoint,
minContentLength,
maxContentLength,
containerLength,
}: {state: ScrollState,
multiplier: number,
fixedPoint: number,
minContentLength: number,
maxContentLength: number,
containerLength: number,
}): ScrollState {
// Length and offset must be computed separately, so that if the length is
// clamped the offset will still be correct (unless it gets clamped too).
const zoomedState = clampLength({
state: {offset: state.offset,
length: state.length * multiplier,
},minContentLength,
maxContentLength,
containerLength,
});// Adjust offset so that distance between containerStart<->fixedPoint is fixed
const fixedPointFromContainer = fixedPoint + state.offset;
const scaledFixedPoint = fixedPoint * (zoomedState.length / state.length);
const offsetAdjustedState = clampOffset(
{offset: fixedPointFromContainer - scaledFixedPoint,
length: zoomedState.length,
},containerLength,
);return offsetAdjustedState;
}export function moveStateToRange({
state,
rangeStart,
rangeEnd,
contentLength,
minContentLength,
maxContentLength,
containerLength,
}: {state: ScrollState,
rangeStart: number,
rangeEnd: number,
contentLength: number,
minContentLength: number,
maxContentLength: number,
containerLength: number,
}): ScrollState {
// Length and offset must be computed separately, so that if the length is
// clamped the offset will still be correct (unless it gets clamped too).
const lengthClampedState = clampLength({
state: {offset: state.offset,
length: contentLength * (containerLength / (rangeEnd - rangeStart)),
},minContentLength,
maxContentLength,
containerLength,
});const offsetAdjustedState = clampOffset(
{offset: -rangeStart * (lengthClampedState.length / contentLength),
length: lengthClampedState.length,
},containerLength,
);return offsetAdjustedState;
}export function areScrollStatesEqual(
state1: ScrollState,
state2: ScrollState,
): boolean {
return state1.offset === state2.offset && state1.length === state2.length;
}