Skip to content

Commit

Permalink
Merge pull request #174 from flexn-io/feat/view-group-child-focus
Browse files Browse the repository at this point in the history
feat: focus viewgroup childs by focus group key
  • Loading branch information
aurimasmi committed Sep 22, 2023
2 parents 85111f3 + 224ae79 commit 4a7e205
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 8 deletions.
21 changes: 19 additions & 2 deletions packages/app-harness/src/screens/tests/viewGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ const ViewGroup = () => {
<Screen style={{ backgroundColor: '#222222' }}>
<View
style={{ top: Ratio(20), flex: 1 }}
focusOptions={{ nextFocusDown: 'test-group-btn2', focusKey: 'test-group', group: 'test-group' }}
focusOptions={{
nextFocusDown: 'test-group2',
focusKey: 'test-group',
group: 'test-group',
allowFocusOutsideGroup: false,
}}
>
<Button
focusOptions={{ focusKey: 'test-group-btn' }}
Expand All @@ -22,7 +27,12 @@ const ViewGroup = () => {
</View>
<View
style={{ top: Ratio(20), flex: 1 }}
focusOptions={{ nextFocusUp: 'test-group-btn', group: 'test-group2' }}
focusOptions={{
nextFocusUp: 'test-group',
group: 'test-group2',
focusKey: 'test-group2',
allowFocusOutsideGroup: true,
}}
>
<Button
focusOptions={{ focusKey: 'test-group-btn2' }}
Expand All @@ -31,6 +41,13 @@ const ViewGroup = () => {
textStyle={styles.buttonTextStyle}
/>
</View>

<Button
// focusOptions={{ nextFocusUp: 'test-group' }}
style={{ ...styles.button, ...styles.button2 }}
title="Button something"
textStyle={styles.buttonTextStyle}
/>
</Screen>
);
};
Expand Down
13 changes: 12 additions & 1 deletion packages/create/src/focusManager/model/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,17 @@ class View extends FocusModel {
}
}

if (value) {
let parent = this.getParent();
while (parent) {
if (parent instanceof ViewGroup) {
parent.setCurrentFocus(this);
break;
}
parent = parent?.getParent();
}
}

return this;
}

Expand Down Expand Up @@ -261,7 +272,7 @@ class View extends FocusModel {
let group;

while (parent) {
if (parent instanceof ViewGroup) {
if (parent instanceof ViewGroup && !parent.isFocusAllowedOutsideGroup()) {
group = parent.getGroup();
parent = null;
} else {
Expand Down
43 changes: 40 additions & 3 deletions packages/create/src/focusManager/model/viewGroup.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { View } from 'react-native';
import FocusModel from './abstractFocusModel';
import FocusModel, { MODEL_TYPES } from './abstractFocusModel';
import Event, { EVENT_TYPES } from '../events';
import { CoreManager } from '../..';
import { MutableRefObject } from 'react';
import { ViewGroupProps } from '../types';
import View from './view';
import RecyclerView from './recycler';

class ViewGroup extends FocusModel {
private _group?: string;
private _focusKey?: string;
private _currentFocus: View | null = null;
private _allowFocusOutsideGroup = false;

constructor(params: Omit<ViewGroupProps & ViewGroupProps['focusOptions'], 'ref' | 'focusOptions'>) {
super(params);

const { focusContext, group, focusKey } = params;
const { focusContext, group, focusKey, allowFocusOutsideGroup = false } = params;

const id = CoreManager.generateID(8);
this._id = focusContext?.getId() ? `${focusContext.getId()}:viewGroup-${id}` : `viewGroup-${id}`;
this._parent = focusContext;
this._type = 'viewGroup';
this._group = group;
this._focusKey = focusKey;
this._allowFocusOutsideGroup = allowFocusOutsideGroup;

this._onMount = this._onMount.bind(this);
this._onUnmount = this._onUnmount.bind(this);
Expand Down Expand Up @@ -48,6 +52,24 @@ class ViewGroup extends FocusModel {

// END EVENTS

public getFirstFocusableInViewGroup = (): View | null => {
if (CoreManager.isFocusManagerEnabled()) {
if (this._currentFocus) return this._currentFocus;

const firstChildren = this._children.find((ch) => [MODEL_TYPES.ROW, MODEL_TYPES.GRID, MODEL_TYPES.VIEW].includes(ch.getType() as never))

if (firstChildren && firstChildren.getType() === MODEL_TYPES.VIEW) {
return firstChildren as View;
} else if (firstChildren) {
const recycler = firstChildren as RecyclerView;
if (recycler.getFocusedView()) return recycler.getFocusedView();
return recycler.getChildren()[0] as View | null;
}
}

return null;
};

public getGroup() {
return this._group;
}
Expand All @@ -71,6 +93,21 @@ class ViewGroup extends FocusModel {
public getNode(): MutableRefObject<View> {
return this.node;
}

public setCurrentFocus(model: View | null): this {
this._currentFocus = model;

return this;
}

public getCurrentFocus(): View | null {
return this._currentFocus;
}

public isFocusAllowedOutsideGroup(): boolean {
return this._allowFocusOutsideGroup;
}

}

export default ViewGroup;
20 changes: 18 additions & 2 deletions packages/create/src/focusManager/service/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import Scroller from './scroller';
import Logger from './logger';
import FocusModel, { MODEL_TYPES } from '../model/abstractFocusModel';
import { DIRECTIONS } from '../constants';
import { ClosestNodeOutput, FocusDirection, ScreenType, ViewType } from '../types';
import { ClosestNodeOutput, FocusDirection, ScreenType, ViewType, ViewGroupType } from '../types';

class CoreManager {
private _focusAwareElements: Record<string, FocusModel> = {};
private _views: Record<string, ViewType> = {};
private _viewGroups: Record<string, ViewGroupType> = {};
private _screens: Record<string, ScreenType> = {};
private _currentFocus: ViewType | null = null;
private _debuggerEnabled = false;
Expand Down Expand Up @@ -60,6 +61,8 @@ class CoreManager {
this._screens[model.getId()] = model as ScreenType;
} else if (model.getType() === MODEL_TYPES.VIEW) {
this._views[model.getId()] = model as ViewType;
} else if (model.getType() === MODEL_TYPES.VIEW_GROUP) {
this._viewGroups[model.getId()] = model as ViewGroupType;
}

Object.keys(this._focusAwareElements).forEach((k) => {
Expand Down Expand Up @@ -150,8 +153,21 @@ class CoreManager {
const screen = Object.values(this._screens).find(
(model) => model.getFocusKey() === focusKey && model.isInForeground()
);

if (screen) {
screen.setFocus(screen.getFirstFocusableOnScreen());
return;
}

const viewGroup = Object.values(this._viewGroups).find(
(model) => model.getFocusKey() === focusKey && model.isInForeground()
);

if (viewGroup) {
const element = viewGroup.getFirstFocusableInViewGroup();
if (element) {
this.executeFocus(element);
}
}
}
};
Expand Down Expand Up @@ -310,7 +326,7 @@ class CoreManager {

public pickActiveForcedFocusContext(nextForcedFocusKey: string | string[]): string | null {
const isActive = (focusKey: string) =>
Object.values({ ...this._views, ...this._screens }).find(
Object.values({ ...this._views, ...this._screens, ...this._viewGroups }).find(
(model) => model.getFocusKey() === focusKey && model.isInForeground()
);

Expand Down
4 changes: 4 additions & 0 deletions packages/create/src/focusManager/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import View from './model/view';
import { SCREEN_STATES, VIEWPORT_ALIGNMENT } from './model/screen';
import Screen from './model/screen';
import { DIRECTIONS } from './constants';
import ViewGroup from './model/viewGroup';

export type FocusDirection = typeof DIRECTIONS[keyof typeof DIRECTIONS];
export type WindowAlignment = 'both-edge' | 'low-edge';
export type ScreenStates = 'background' | 'foreground';
export type FocusContext = FocusModel;
export type ScreenType = Screen;
export type ViewType = View;
export type ViewGroupType = ViewGroup;
type AnimatorTypeScale = 'scale';
type AnimatorTypeScaleWithBorder = 'scale_with_border';
type AnimatorTypeAnimatorBorder = 'border';
Expand Down Expand Up @@ -132,6 +134,7 @@ export interface ViewProps extends RNViewProps, MouseEvents {
nextFocusRight?: string | string[];
nextFocusUp?: string | string[];
nextFocusDown?: string | string[];
allowFocusOutsideGroup?: boolean;
};
focusContext?: FocusModel;
focusRepeatContext?: CreateListRenderItemInfo<any>['focusRepeatContext'];
Expand All @@ -146,6 +149,7 @@ export interface ViewGroupProps extends RNViewProps {
nextFocusRight?: string | string[];
nextFocusUp?: string | string[];
nextFocusDown?: string | string[];
allowFocusOutsideGroup?: boolean;
};
focusContext?: FocusContext;
ref?: React.ForwardedRef<RNView> | React.MutableRefObject<RNView>;
Expand Down

0 comments on commit 4a7e205

Please sign in to comment.