Skip to content
Draft
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
7 changes: 5 additions & 2 deletions VirtualList/VirtualList.js
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,8 @@ const virtualGridListDefaultProps = {
role: 'list',
scrollMode: 'native',
verticalScrollbar: 'auto',
wrap: false
wrap: false,
fixedFocus: false,
};

/**
Expand Down Expand Up @@ -1032,7 +1033,9 @@ VirtualGridList.propTypes = /** @lends limestone/VirtualList.VirtualGridList.pro
wrap: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.oneOf(['noAnimation'])
])
]),

fixedFocus: PropTypes.bool
};

VirtualGridList = Skinnable(
Expand Down
19 changes: 16 additions & 3 deletions VirtualList/useThemeVirtualList.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const useSpottable = (props, instances) => {

mutableRef.current.lastFocusedIndex = nextIndex;

if (start >= startBoundary && end <= endBoundary) {
if ((start >= startBoundary && end <= endBoundary) || props.fixedFocus) {
// The next item could be still out of viewport. So we need to prevent scrolling into view with `isScrolledBy5way` flag.
mutableRef.current.isScrolledBy5way = true;
focusByIndex(nextIndex, direction);
Expand Down Expand Up @@ -433,13 +433,26 @@ const useThemeVirtualList = (props) => {
'data-webos-voice-disabled': voiceDisabled
},
getAffordance,
itemRenderer: ({index, ...itemRest}) => (
itemRenderer: ({index, ...itemRest}) => {
const hiddenItem = itemRenderer({
...itemRest,
[dataIndexAttribute]: index,
index,
spotlightDisabled: true
});
hiddenItem.props.style.visibility = "hidden";
const clientSize = scrollContentHandle.current.primary.clientSize;
const itemSize = scrollContentHandle.current.primary.itemSize;
if (props.fixedFocus && (index === 0 || index > props.dataSize - clientSize/itemSize)) {
return (hiddenItem);
}
return (
itemRenderer({
...itemRest,
[dataIndexAttribute]: index,
index
})
),
)},
placeholderRenderer: (primary) => {
return placeholderRenderer({
handlePlaceholderFocus,
Expand Down
8 changes: 8 additions & 0 deletions useScroll/HoverToScroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ const HoverToScrollBase = (props) => {
mutableRef.current.stopScrollByHover = false;

const scrollByHover = (currentTime) => {
if (props.fixedFocus) {
if (directionToFocus[direction][mutableRef.current.hoveredPosition] === 'right' && scrollContainer[scrollPosition] < 270) {
return nop;
}
if (directionToFocus[direction][mutableRef.current.hoveredPosition] === 'left' && scrollContainer[scrollPosition] > 1880) {
return nop;
}
}
if (!mutableRef.current.stopScrollByHover) {
const elapsed = (currentTime - startTime) / 1000;
const distanceMultiplier = elapsed < hoverToScrollDelay ? 0 : Math.min(elapsed - hoverToScrollDelay / 1.5, 1);
Expand Down
19 changes: 19 additions & 0 deletions useScroll/useEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,25 @@ const useEventFocus = (props, instances) => {
}
}

if (props.fixedFocus && !spottable.current.isWheeling && scrollContainerHandle.current.lastInputType === 'arrowKey') {
const itemWidth = scrollContentRef.current.scrollWidth / props.dataSize;

// 'getPositionForScrollTo' should be included in 'themeScrollContainerHandle' in ui/useScroll/useScroll/useScrollBase
// if you want to use it
// but is this necessary?
// const opt = {
// index: Number(ev.target.dataset.index),
// stickTo: 'start',
// offset: itemWidth/2,
// };
// const pos = scrollContainerHandle.current.getPositionForScrollTo(opt);
// console.log('pos', pos);
// startScrollOnFocus(pos);

// We can just calculate the position
startScrollOnFocus({left: itemWidth*ev.target.dataset.index-itemWidth/2, top: 0});
}

if (!(shouldPreventScrollByFocus || Spotlight.getPointerMode() || scrollContainerHandle.current.isDragging || spottable.current.indexToFocus)) {
const
item = ev.target,
Expand Down
4 changes: 3 additions & 1 deletion useScroll/useScroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ const useScroll = (props) => {
snapToCenter,
style,
verticalScrollThumbAriaLabel,
fixedFocus,
...rest
} = props;

Expand Down Expand Up @@ -488,7 +489,7 @@ const useScroll = (props) => {
};

assignProperties('scrollContentProps', {
...(props.itemRenderer ? {itemRefs, noAffordance, snapToCenter} : {editable, fadeOut}),
...(props.itemRenderer ? {itemRefs, noAffordance, snapToCenter, fixedFocus} : {editable, fadeOut}),
...voiceProps,
className: [
(props.direction === 'both' || props.direction === 'vertical') ? overscrollCss.vertical : overscrollCss.horizontal,
Expand Down Expand Up @@ -522,6 +523,7 @@ const useScroll = (props) => {
});

assignProperties('hoverToScrollProps', {
fixedFocus,
scrollContainerHandle,
scrollObserver
});
Expand Down