Skip to content

Commit

Permalink
fix(NavigationManager): adjust isFullyOnScreen and isComponentOnScree…
Browse files Browse the repository at this point in the history
…n methods to include transitions of parent FocusManagers
  • Loading branch information
erautenberg authored and ImCoolNowRight committed Nov 21, 2023
1 parent d2c29dd commit 498d73a
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 11 deletions.
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

0 comments on commit 498d73a

Please sign in to comment.