mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
9cdf8a99ed
* Facebook -> Meta in copyright rg --files | xargs sed -i 's#Copyright (c) Facebook, Inc. and its affiliates.#Copyright (c) Meta Platforms, Inc. and affiliates.#g' * Manual tweaks
207 lines
4.7 KiB
JavaScript
207 lines
4.7 KiB
JavaScript
/**
|
|
* 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;
|
|
}
|