Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(NavigationManager): adjust isFullyOnScreen and isComponentOnScreen #417

Merged
merged 2 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ declare class Base<
/**
* returns true if this component is fully within the stage and boundsMargin
*/
isFullyOnScreen(): boolean;
isFullyOnScreen(offsets: { offsetX: number; offsetY: number }): boolean;

// TODO: for future reference these accessors should technically be public
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ class Base extends lng.Component {
return this.mode === 'focused';
}

isFullyOnScreen() {
return isComponentOnScreen(this);
isFullyOnScreen(offsets) {
return isComponentOnScreen(this, offsets);
}

getFocusScale() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,4 +499,17 @@ export default class NavigationManager extends FocusManager {
? this._scrollIndex
: this.style.scrollIndex;
}

isFullyOnScreen({ offsetX = 0, offsetY = 0 } = {}) {
// if the NavigationManager is nested in another Focus Manager
// (like a Row inside of a Column),
// the `isComponentOnScreen` method needs to account for
// how much the Items container is moving as it scrolls
const focusmanager = this.parent?.parent;
if (focusmanager instanceof FocusManager) {
offsetX += focusmanager.Items.transition('x').targetValue || 0;
offsetY += focusmanager.Items.transition('y').targetValue || 0;
}
return super.isFullyOnScreen({ offsetX, offsetY });
}
}
2 changes: 1 addition & 1 deletion packages/@lightningjs/ui-components/src/docs/Base.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Any component which extends the Base component and uses the `withThemeStyles` mi
This method accepts a target component, patch object, and optional smooth object. If the component is visible, it will smooth in the smooth object, or fall back to
the patch object, if not it will apply the patch.

#### isFullyOnScreen(): bool
#### isFullyOnScreen(\{ offsetX: number, offsetY: number \}): bool

Returns a boolean for whether or not the entirety of the component is rendered within the bounds of the screen size.

Expand Down
36 changes: 29 additions & 7 deletions packages/@lightningjs/ui-components/src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export function getShortestDistance(coordinate, element) {
return Math.min(distanceToStart, distanceToMiddle, distanceToEnd);
}

export function isComponentOnScreen(component) {
export function isComponentOnScreen(component, offsets = {}) {
if (!component) return false;

const {
Expand All @@ -143,8 +143,29 @@ export function isComponentOnScreen(component) {
const stageH = component.stage.h / component.stage.getRenderPrecision();
const stageW = component.stage.w / component.stage.getRenderPrecision();

const wVis = px >= 0 && px + w <= stageW;
const hVis = py >= 0 && py + h <= stageH;
let finalX = px;
let finalY = py;
// keep track of the different between the the absolute world position and relative position
const relativeOffsetX = px - component.x;
const relativeOffsetY = py - component.y;
const offsetX = offsets.offsetX - relativeOffsetX || 0;
const offsetY = offsets.offsetY - relativeOffsetY || 0;
// if the current component is animating, apply the relative offset to the transition value
if (component.transition('x')) {
finalX = px - component.x + component.transition('x').targetValue;
}
if (component.transition('y')) {
finalY = py - component.y + component.transition('y').targetValue;
}
// apply any offset passed into the function
// this is mainly used to parent components that are transitioning,
// like in the case of Rows nested inside of Columns where the Rows themselves do not animate,
// but their parent container does
finalX += offsetX;
finalY += offsetY;

const wVis = finalX >= 0 && finalX + w <= stageW;
const hVis = finalY >= 0 && finalY + h <= stageH;

if (!wVis || !hVis) return false;

Expand All @@ -157,12 +178,13 @@ export function isComponentOnScreen(component) {
] = scissor;

const withinLeftClippingBounds =
Math.round(px + w) >= Math.round(leftBounds);
Math.round(finalX + w) >= Math.round(leftBounds);
const withinRightClippingBounds =
Math.round(px) <= Math.round(leftBounds + clipWidth);
const withinTopClippingBounds = Math.round(py + h) >= Math.round(topBounds);
Math.round(finalX) <= Math.round(leftBounds + clipWidth);
const withinTopClippingBounds =
Math.round(finalY + h) >= Math.round(topBounds);
const withinBottomClippingBounds =
Math.round(py + h) <= Math.round(topBounds + clipHeight);
Math.round(finalY + h) <= Math.round(topBounds + clipHeight);

return (
withinLeftClippingBounds &&
Expand Down
Loading