Skip to content

Commit

Permalink
feat: add useNavId (#5743)
Browse files Browse the repository at this point in the history
- closed #1653
  • Loading branch information
SevereCloud authored and actions-user committed Sep 6, 2023
1 parent 52e3a82 commit 1a58493
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 134 deletions.
4 changes: 4 additions & 0 deletions packages/vkui/src/components/NavIdContext/NavIdContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React from 'react';

export const NavViewIdContext = React.createContext<string | undefined>(undefined);
export const NavPanelIdContext = React.createContext<string | undefined>(undefined);
45 changes: 45 additions & 0 deletions packages/vkui/src/components/NavIdContext/useNavId.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { Panel } from '../Panel/Panel';
import { View } from '../View/View';
import { useNavId } from './useNavId';

describe(useNavId, () => {
it('Panel with id', () => {
const { result } = renderHook(useNavId, {
wrapper: (props) => <Panel id="test" {...props} />,
});
expect(result.current.panel).toEqual('test');
});

it('Panel with nav', () => {
const { result } = renderHook(useNavId, {
wrapper: (props) => <Panel nav="test" {...props} />,
});
expect(result.current.panel).toEqual('test');
});

it('View with id', () => {
const { result } = renderHook(useNavId, {
wrapper: (props) => (
<View activePanel="panel" id="test">
<Panel id="panel" {...props} />
</View>
),
});
expect(result.current.view).toEqual('test');
expect(result.current.panel).toEqual('panel');
});

it('View with nav', () => {
const { result } = renderHook(useNavId, {
wrapper: (props) => (
<View activePanel="panel" nav="test">
<Panel id="panel" {...props} />
</View>
),
});
expect(result.current.view).toEqual('test');
expect(result.current.panel).toEqual('panel');
});
});
7 changes: 7 additions & 0 deletions packages/vkui/src/components/NavIdContext/useNavId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';
import { NavPanelIdContext, NavViewIdContext } from './NavIdContext';

export const useNavId = () => ({
view: React.useContext(NavViewIdContext),
panel: React.useContext(NavPanelIdContext),
});
39 changes: 21 additions & 18 deletions packages/vkui/src/components/Panel/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { NavIdProps } from '../../lib/getNavId';
import { Platform } from '../../lib/platform';
import { HTMLAttributesWithRootRef } from '../../types';
import { AppRootContext } from '../AppRoot/AppRootContext';
import { NavPanelIdContext } from '../NavIdContext/NavIdContext';
import { RootComponent } from '../RootComponent/RootComponent';
import { TooltipContainer } from '../Tooltip/TooltipContainer';
import { Touch } from '../Touch/Touch';
Expand All @@ -31,24 +32,26 @@ export const Panel = ({ centered = false, children, nav, ...restProps }: PanelPr
const { layout } = React.useContext(AppRootContext);

return (
<RootComponent
{...restProps}
baseClassName={classNames(
styles['Panel'],
sizeXClassNames[sizeX],
centered && 'vkuiInternalPanel--centered',
layout && styles['Panel--layoutSetting'],
)}
>
<Touch
Component={TooltipContainer}
className={classNames(styles['Panel__in'], 'vkuiInternalPanel__in')}
<NavPanelIdContext.Provider value={restProps.id || nav}>
<RootComponent
{...restProps}
baseClassName={classNames(
styles['Panel'],
sizeXClassNames[sizeX],
centered && 'vkuiInternalPanel--centered',
layout && styles['Panel--layoutSetting'],
)}
>
<div className={styles['Panel__in-before']} />
{centered ? <div className={styles['Panel__centered']}>{children}</div> : children}
{platform === Platform.IOS && <div className="vkuiInternalPanel__fade" />}
<div className={styles['Panel__in-after']} />
</Touch>
</RootComponent>
<Touch
Component={TooltipContainer}
className={classNames(styles['Panel__in'], 'vkuiInternalPanel__in')}
>
<div className={styles['Panel__in-before']} />
{centered ? <div className={styles['Panel__centered']}>{children}</div> : children}
{platform === Platform.IOS && <div className="vkuiInternalPanel__fade" />}
<div className={styles['Panel__in-after']} />
</Touch>
</RootComponent>
</NavPanelIdContext.Provider>
);
};
121 changes: 62 additions & 59 deletions packages/vkui/src/components/View/View.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { warnOnce } from '../../lib/warnOnce';
import { HTMLAttributesWithRootRef } from '../../types';
import { useScroll } from '../AppRoot/ScrollContext';
import { useConfigProvider } from '../ConfigProvider/ConfigProviderContext';
import { NavViewIdContext } from '../NavIdContext/NavIdContext';
import { NavTransitionProvider } from '../NavTransitionContext/NavTransitionContext';
import { NavTransitionDirectionProvider } from '../NavTransitionDirectionContext/NavTransitionDirectionContext';
import { useSplitCol } from '../SplitCol/SplitColContext';
Expand Down Expand Up @@ -95,11 +96,11 @@ export const View = ({
className,
...restProps
}: ViewProps) => {
const scrolls = React.useRef(scrollsCache[getNavId({ nav, id: restProps.id }) as string] || {});
const id = getNavId({ nav, id: restProps.id });
const scrolls = React.useRef(scrollsCache[id as string] || {});
const afterTransition = React.useRef(noop);

React.useEffect(() => () => {
const id = getNavId({ nav, id: restProps.id });
if (id) {
scrollsCache[id] = scrolls.current;
}
Expand Down Expand Up @@ -490,65 +491,67 @@ export const View = ({
]);

return (
<Touch
Component="section"
{...restProps}
className={classNames(
styles['View'],
platform === Platform.IOS && classNames(styles['View--ios'], 'vkuiInternalView--ios'),
!disableAnimation && animated && styles['View--animated'],
!disableAnimation && swipingBack && styles['View--swiping-back'],
disableAnimation && styles['View--no-motion'],
className,
)}
onMoveX={onMoveX}
onEnd={onEnd}
>
<div className={styles['View__panels']}>
{panels.map((panel: React.ReactElement) => {
const panelId = getNavId(panel.props, warn);
const isPrev = panelId === prevPanel || panelId === swipeBackPrevPanel;
const isTransitionTarget = animated && panelId === (isBack ? prevPanel : nextPanel);
const compensateScroll =
isPrev || panelId === swipeBackNextPanel || (panelId === nextPanel && isBack);

return (
<div
className={classNames(
styles['View__panel'],
panelId === activePanel && styles['View__panel--active'],
panelId === prevPanel && styles['View__panel--prev'],
panelId === nextPanel && styles['View__panel--next'],
panelId === swipeBackPrevPanel && styles['View__panel--swipe-back-prev'],
panelId === swipeBackNextPanel && styles['View__panel--swipe-back-next'],
swipeBackResult === SwipeBackResults.success &&
styles['View__panel--swipe-back-success'],
swipeBackResult === SwipeBackResults.fail &&
styles['View__panel--swipe-back-failed'],
)}
onAnimationEnd={isTransitionTarget ? transitionEndHandler : undefined}
ref={(el) => panelId !== undefined && (panelNodes.current[panelId] = el)}
style={calcPanelSwipeStyles(panelId)}
key={panelId}
>
<NavViewIdContext.Provider value={id}>
<Touch
Component="section"
{...restProps}
className={classNames(
styles['View'],
platform === Platform.IOS && classNames(styles['View--ios'], 'vkuiInternalView--ios'),
!disableAnimation && animated && styles['View--animated'],
!disableAnimation && swipingBack && styles['View--swiping-back'],
disableAnimation && styles['View--no-motion'],
className,
)}
onMoveX={onMoveX}
onEnd={onEnd}
>
<div className={styles['View__panels']}>
{panels.map((panel: React.ReactElement) => {
const panelId = getNavId(panel.props, warn);
const isPrev = panelId === prevPanel || panelId === swipeBackPrevPanel;
const isTransitionTarget = animated && panelId === (isBack ? prevPanel : nextPanel);
const compensateScroll =
isPrev || panelId === swipeBackNextPanel || (panelId === nextPanel && isBack);

return (
<div
className={styles['View__panel-in']}
style={{
marginTop: compensateScroll ? -(scrolls.current[panelId] ?? 0) : undefined,
}}
className={classNames(
styles['View__panel'],
panelId === activePanel && styles['View__panel--active'],
panelId === prevPanel && styles['View__panel--prev'],
panelId === nextPanel && styles['View__panel--next'],
panelId === swipeBackPrevPanel && styles['View__panel--swipe-back-prev'],
panelId === swipeBackNextPanel && styles['View__panel--swipe-back-next'],
swipeBackResult === SwipeBackResults.success &&
styles['View__panel--swipe-back-success'],
swipeBackResult === SwipeBackResults.fail &&
styles['View__panel--swipe-back-failed'],
)}
onAnimationEnd={isTransitionTarget ? transitionEndHandler : undefined}
ref={(el) => panelId !== undefined && (panelNodes.current[panelId] = el)}
style={calcPanelSwipeStyles(panelId)}
key={panelId}
>
<NavTransitionDirectionProvider isBack={swipingBack || isBack}>
<NavTransitionProvider
entering={panelId === nextPanel || panelId === swipeBackNextPanel}
>
{panel}
</NavTransitionProvider>
</NavTransitionDirectionProvider>
<div
className={styles['View__panel-in']}
style={{
marginTop: compensateScroll ? -(scrolls.current[panelId] ?? 0) : undefined,
}}
>
<NavTransitionDirectionProvider isBack={swipingBack || isBack}>
<NavTransitionProvider
entering={panelId === nextPanel || panelId === swipeBackNextPanel}
>
{panel}
</NavTransitionProvider>
</NavTransitionDirectionProvider>
</div>
</div>
</div>
);
})}
</div>
</Touch>
);
})}
</div>
</Touch>
</NavViewIdContext.Provider>
);
};
117 changes: 60 additions & 57 deletions packages/vkui/src/components/View/ViewInfinite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ConfigProviderContext,
ConfigProviderContextInterface,
} from '../ConfigProvider/ConfigProviderContext';
import { NavViewIdContext } from '../NavIdContext/NavIdContext';
import { NavTransitionProvider } from '../NavTransitionContext/NavTransitionContext';
import { NavTransitionDirectionProvider } from '../NavTransitionDirectionContext/NavTransitionDirectionContext';
import { SplitColContext, SplitColContextProps } from '../SplitCol/SplitColContext';
Expand Down Expand Up @@ -618,66 +619,68 @@ class ViewInfiniteComponent extends React.Component<
const disableAnimation = this.shouldDisableTransitionMotion();

return (
<Touch
Component="section"
{...restProps}
className={classNames(
styles['View'],
platform === Platform.IOS && classNames(styles['View--ios'], 'vkuiInternalView--ios'),
!disableAnimation && this.state.animated && styles['View--animated'],
!disableAnimation && this.state.swipingBack && styles['View--swiping-back'],
disableAnimation && styles['View--no-motion'],
className,
)}
onMoveX={this.onMoveX}
onEnd={this.onEnd}
>
<div className={styles['View__panels']}>
{panels.map((panel: React.ReactElement) => {
const panelId = getNavId(panel.props, warn);
const isPrev = panelId === prevPanel || panelId === swipeBackPrevPanel;
const compensateScroll =
isPrev || panelId === swipeBackNextPanel || (panelId === nextPanel && isBack);
const isTransitionTarget = animated && panelId === (isBack ? prevPanel : nextPanel);
const scrollList = (panelId && this.scrolls[panelId]) || [];
const scroll = scrollList[scrollList.length - 1] || 0;

return (
<div
className={classNames(
styles['View__panel'],
panelId === activePanel && styles['View__panel--active'],
panelId === prevPanel && styles['View__panel--prev'],
panelId === nextPanel && styles['View__panel--next'],
panelId === swipeBackPrevPanel && styles['View__panel--swipe-back-prev'],
panelId === swipeBackNextPanel && styles['View__panel--swipe-back-next'],
swipeBackResult === SwipeBackResults.success &&
styles['View__panel--swipe-back-success'],
swipeBackResult === SwipeBackResults.fail &&
styles['View__panel--swipe-back-failed'],
)}
onAnimationEnd={isTransitionTarget ? this.transitionEndHandler : undefined}
ref={(el) => panelId !== undefined && (this.panelNodes[panelId] = el)}
style={this.calcPanelSwipeStyles(panelId)}
key={panelId}
>
<NavViewIdContext.Provider value={id || nav}>
<Touch
Component="section"
{...restProps}
className={classNames(
styles['View'],
platform === Platform.IOS && classNames(styles['View--ios'], 'vkuiInternalView--ios'),
!disableAnimation && this.state.animated && styles['View--animated'],
!disableAnimation && this.state.swipingBack && styles['View--swiping-back'],
disableAnimation && styles['View--no-motion'],
className,
)}
onMoveX={this.onMoveX}
onEnd={this.onEnd}
>
<div className={styles['View__panels']}>
{panels.map((panel: React.ReactElement) => {
const panelId = getNavId(panel.props, warn);
const isPrev = panelId === prevPanel || panelId === swipeBackPrevPanel;
const compensateScroll =
isPrev || panelId === swipeBackNextPanel || (panelId === nextPanel && isBack);
const isTransitionTarget = animated && panelId === (isBack ? prevPanel : nextPanel);
const scrollList = (panelId && this.scrolls[panelId]) || [];
const scroll = scrollList[scrollList.length - 1] || 0;

return (
<div
className={styles['View__panel-in']}
style={{ marginTop: compensateScroll ? -scroll : undefined }}
className={classNames(
styles['View__panel'],
panelId === activePanel && styles['View__panel--active'],
panelId === prevPanel && styles['View__panel--prev'],
panelId === nextPanel && styles['View__panel--next'],
panelId === swipeBackPrevPanel && styles['View__panel--swipe-back-prev'],
panelId === swipeBackNextPanel && styles['View__panel--swipe-back-next'],
swipeBackResult === SwipeBackResults.success &&
styles['View__panel--swipe-back-success'],
swipeBackResult === SwipeBackResults.fail &&
styles['View__panel--swipe-back-failed'],
)}
onAnimationEnd={isTransitionTarget ? this.transitionEndHandler : undefined}
ref={(el) => panelId !== undefined && (this.panelNodes[panelId] = el)}
style={this.calcPanelSwipeStyles(panelId)}
key={panelId}
>
<NavTransitionDirectionProvider isBack={swipingBack || isBack}>
<NavTransitionProvider
entering={panelId === nextPanel || panelId === swipeBackNextPanel}
>
{panel}
</NavTransitionProvider>
</NavTransitionDirectionProvider>
<div
className={styles['View__panel-in']}
style={{ marginTop: compensateScroll ? -scroll : undefined }}
>
<NavTransitionDirectionProvider isBack={swipingBack || isBack}>
<NavTransitionProvider
entering={panelId === nextPanel || panelId === swipeBackNextPanel}
>
{panel}
</NavTransitionProvider>
</NavTransitionDirectionProvider>
</div>
</div>
</div>
);
})}
</div>
</Touch>
);
})}
</div>
</Touch>
</NavViewIdContext.Provider>
);
}
}
Expand Down
Loading

0 comments on commit 1a58493

Please sign in to comment.