Skip to content

Commit

Permalink
feat(ui): add listener for scrollview change
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Jan 25, 2022
1 parent f102c5a commit d4ea59e
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 44 deletions.
14 changes: 8 additions & 6 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nrwl/nx", "import", "markdown", "jsdoc"],
"extends": ["plugin:prettier/recommended", "plugin:jsdoc/recommended"],
"plugins": ["@nrwl/nx", "import", "markdown", "jsdoc", "eslint-plugin-tsdoc"],
"extends": ["plugin:prettier/recommended"],
"overrides": [
{
"files": ["**/*.md"],
Expand Down Expand Up @@ -43,14 +43,14 @@
},
"warnOnUnassignedImports": true
}
],
"jsdoc/require-jsdoc": "off"
]
}
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
"rules": {
"tsdoc/syntax": "warn",
"no-unreachable": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/ban-types": [
Expand Down Expand Up @@ -118,8 +118,10 @@
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"rules": {}
"extends": ["plugin:@nrwl/nx/javascript", "plugin:jsdoc/recommended"],
"rules": {
"jsdoc/require-jsdoc": "off"
}
},
{
"files": ["**/*.md/*.ts", "**/*.md/*.tsx"],
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"regenerator-runtime": "^0.13.9",
"rfs": "^9.0.6",
"rxjs": "^7.5.2",
"scrollview-resize": "^1.0.0",
"tslib": "^2.3.1"
},
"devDependencies": {
Expand Down Expand Up @@ -81,6 +82,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-tsdoc": "^0.2.14",
"fs-extra": "^10.0.0",
"husky": "^7.0.4",
"jest": "^27.4.7",
Expand Down
2 changes: 1 addition & 1 deletion packages/site/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function App() {
);

return (
<DRoot theme={theme} i18n={{ lang: i18n.language as DLang }} icons={icons} contentSelector="main .app-route-article">
<DRoot theme={theme} i18n={{ lang: i18n.language as DLang }} icons={icons} contentSelector="main">
<AppContext.Provider value={contextValue}>
<AppHeader />
<AppSidebar />
Expand Down
29 changes: 18 additions & 11 deletions packages/ui/src/components/_popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import {
useRefSelector,
useImmer,
useRefCallback,
useContentRefConfig,
useDTransition,
useMaxIndex,
useContentSVChangeConfig,
} from '../../hooks';
import { getClassName, getPopupPlacementStyle, mergeStyle } from '../../utils';
import { checkOutEl } from './utils';
Expand Down Expand Up @@ -93,7 +93,7 @@ const Popup: React.ForwardRefRenderFunction<DPopupRef, DPopupProps> = (props, re

//#region Context
const dPrefix = usePrefixConfig();
const rootContentRef = useContentRefConfig();
const contentSVChange = useContentSVChangeConfig();
//#endregion

//#region Ref
Expand Down Expand Up @@ -508,19 +508,16 @@ const Popup: React.ForwardRefRenderFunction<DPopupRef, DPopupProps> = (props, re
}, [asyncCapture, dEscClosable, dVisible, changeVisible]);

useEffect(() => {
const [asyncGroup, asyncId] = asyncCapture.createGroup();
if (popupRendered) {
const [asyncGroup, asyncId] = asyncCapture.createGroup();
if (popupEl) {
asyncGroup.onResize(popupEl, updatePosition);
}
if (triggerRef.current) {
asyncGroup.onResize(triggerRef.current, updatePosition);
}
if (!isFixed && rootContentRef.current) {
asyncGroup.onResize(rootContentRef.current, updatePosition);
}

asyncGroup.onGlobalScroll(updatePosition, () => {
const skipUpdate = () => {
if (triggerRef.current) {
const { top, left } = triggerRef.current.getBoundingClientRect();

Expand All @@ -530,12 +527,22 @@ const Popup: React.ForwardRefRenderFunction<DPopupRef, DPopupProps> = (props, re
}

return false;
};
const ob = contentSVChange?.subscribe({
next: () => {
if (!skipUpdate()) {
updatePosition();
}
},
});
asyncGroup.onGlobalScroll(updatePosition, skipUpdate);

return () => {
ob?.unsubscribe();
asyncCapture.deleteGroup(asyncId);
};
}
return () => {
asyncCapture.deleteGroup(asyncId);
};
}, [asyncCapture, isFixed, popupEl, popupRendered, rootContentRef, triggerRef, updatePosition]);
}, [asyncCapture, popupEl, popupRendered, contentSVChange, triggerRef, updatePosition]);

useImperativeHandle(
ref,
Expand Down
28 changes: 21 additions & 7 deletions packages/ui/src/components/affix/Affix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { isString, isUndefined } from 'lodash';
import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import ReactDOM from 'react-dom';

import { usePrefixConfig, useComponentConfig, useAsync, useRefSelector, useImmer, useRefCallback, useContentRefConfig } from '../../hooks';
import {
usePrefixConfig,
useComponentConfig,
useAsync,
useRefSelector,
useImmer,
useRefCallback,
useContentSVChangeConfig,
} from '../../hooks';
import { getClassName, toPx, mergeStyle, generateComponentMate } from '../../utils';

export interface DAffixRef {
Expand Down Expand Up @@ -36,7 +44,7 @@ const Affix: React.ForwardRefRenderFunction<DAffixRef, DAffixProps> = (props, re

//#region Context
const dPrefix = usePrefixConfig();
const rootContentRef = useContentRefConfig();
const contentSVChange = useContentSVChangeConfig();
//#endregion

//#region Ref
Expand Down Expand Up @@ -149,10 +157,7 @@ const Affix: React.ForwardRefRenderFunction<DAffixRef, DAffixProps> = (props, re

useEffect(() => {
const [asyncGroup, asyncId] = asyncCapture.createGroup();
if (rootContentRef.current) {
asyncGroup.onResize(rootContentRef.current, updatePosition);
}
asyncGroup.onGlobalScroll(updatePosition, () => {
const skipUpdate = () => {
const el = fixed ? referenceEl : affixEl;
if (el) {
const { top, left } = el.getBoundingClientRect();
Expand All @@ -163,11 +168,20 @@ const Affix: React.ForwardRefRenderFunction<DAffixRef, DAffixProps> = (props, re
}

return false;
};
const ob = contentSVChange?.subscribe({
next: () => {
if (!skipUpdate()) {
updatePosition();
}
},
});
asyncGroup.onGlobalScroll(updatePosition, skipUpdate);
return () => {
ob?.unsubscribe();
asyncCapture.deleteGroup(asyncId);
};
}, [affixEl, asyncCapture, fixed, referenceEl, rootContentRef, updatePosition]);
}, [affixEl, asyncCapture, fixed, referenceEl, contentSVChange, updatePosition]);

useEffect(() => {
updatePosition();
Expand Down
28 changes: 20 additions & 8 deletions packages/ui/src/components/anchor/Anchor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import type { DElementSelector } from '../../hooks/element-ref';
import { isUndefined } from 'lodash';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { usePrefixConfig, useComponentConfig, useRefSelector, useImmer, useAsync, useRefCallback, useContentRefConfig } from '../../hooks';
import {
usePrefixConfig,
useComponentConfig,
useRefSelector,
useImmer,
useAsync,
useRefCallback,
useContentSVChangeConfig,
} from '../../hooks';
import { getClassName, CustomScroll, generateComponentMate } from '../../utils';

export interface DAnchorContextData {
Expand Down Expand Up @@ -40,7 +48,7 @@ export const DAnchor = (props: DAnchorProps) => {

//#region Context
const dPrefix = usePrefixConfig();
const rootContentRef = useContentRefConfig();
const contentSVChange = useContentSVChangeConfig();
//#endregion

//#region Ref
Expand Down Expand Up @@ -151,10 +159,12 @@ export const DAnchor = (props: DAnchorProps) => {

useEffect(() => {
const [asyncGroup, asyncId] = asyncCapture.createGroup();
if (rootContentRef.current) {
asyncGroup.onResize(rootContentRef.current, updateAnchor);
}
asyncGroup.onGlobalScroll(updateAnchor, () => {
const ob = contentSVChange?.subscribe({
next: () => {
updateAnchor();
},
});
const skipUpdate = () => {
const data = Array.from(links.values())[0];
if (data) {
const el = document.getElementById(data.href.slice(1));
Expand All @@ -168,11 +178,13 @@ export const DAnchor = (props: DAnchorProps) => {
}

return false;
});
};
asyncGroup.onGlobalScroll(updateAnchor, skipUpdate);
return () => {
ob?.unsubscribe();
asyncCapture.deleteGroup(asyncId);
};
}, [asyncCapture, links, rootContentRef, updateAnchor]);
}, [asyncCapture, links, contentSVChange, updateAnchor]);

const stateBackflow = useMemo<Pick<DAnchorContextData, 'updateLinks' | 'removeLinks'>>(
() => ({
Expand Down
35 changes: 33 additions & 2 deletions packages/ui/src/components/root/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import type { DConfigContextData } from '../../hooks/d-config';
import type { DElementSelector } from '../../hooks/element-ref';

import { isUndefined } from 'lodash';
import { useEffect } from 'react';
import { useState } from 'react';
import { Subject } from 'rxjs';
import { SVResizeObserver } from 'scrollview-resize';

import { useRefSelector } from '../../hooks';
import { DConfigContext } from '../../hooks/d-config';
import { Notification } from './Notification';
import { Toast } from './Toast';

export interface DRootProps extends DConfigContextData {
export interface DRootProps extends Omit<DConfigContextData, 'scrollViewChange'> {
contentSelector?: DElementSelector;
children: React.ReactNode;
}

export function DRoot(props: DRootProps) {
const { theme, i18n, children, ...restProps } = props;
const { contentSelector, children, theme, i18n, ...restProps } = props;

const lang = i18n?.lang ?? 'en-US';

const [scrollViewChange] = useState(() => new Subject<void>());
const contentRef = useRefSelector(contentSelector ?? null);

useEffect(() => {
document.body.classList.toggle('CJK', lang === 'zh-Hant');
}, [lang]);
Expand All @@ -30,11 +40,32 @@ export function DRoot(props: DRootProps) {
}
}, [theme]);

useEffect(() => {
if (isUndefined(contentSelector)) {
const observer = new ResizeObserver(() => {
scrollViewChange.next();
});
observer.observe(document.documentElement);
return () => {
observer.disconnect();
};
} else if (contentRef.current) {
const observer = new SVResizeObserver(() => {
scrollViewChange.next();
});
observer.observe(contentRef.current);
return () => {
observer.disconnect();
};
}
}, [contentRef, contentSelector, scrollViewChange]);

return (
<DConfigContext.Provider
value={{
theme,
i18n,
scrollViewChange,
...restProps,
}}
>
Expand Down
11 changes: 4 additions & 7 deletions packages/ui/src/hooks/d-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ import type {
DTooltipProps,
} from '../components';
import type { DBreakpoints } from '../components/grid';
import type { Subject } from 'rxjs';

import { isUndefined } from 'lodash';
import React, { useContext, useMemo } from 'react';

import { getFragmentChildren } from '../utils';
import { useRefSelector } from './element-ref';

interface Resources {
[index: string]: string | Resources;
Expand Down Expand Up @@ -128,7 +128,7 @@ export interface DConfigContextData {
type?: string;
}[];
}[];
contentSelector?: string;
scrollViewChange?: Subject<void>;
}
export const DConfigContext = React.createContext<DConfigContextData>({});

Expand Down Expand Up @@ -187,9 +187,6 @@ export function useComponentConfig<T>(component: keyof DComponentConfig, props:
return { ...customConfig, ...(noUndefinedProps as T), children };
}

export function useContentRefConfig() {
const contentSelector = useContext(DConfigContext).contentSelector ?? null;
const contentRef = useRefSelector(contentSelector);

return contentRef;
export function useContentSVChangeConfig() {
return useContext(DConfigContext).scrollViewChange;
}
2 changes: 1 addition & 1 deletion packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export { useAsync } from './async';
export { useTranslation } from './i18n';
export { useDTransition, useDCollapseTransition } from './transition';
export { useCustomContext } from './context';
export { usePrefixConfig, useThemeConfig, useGridConfig, useContentRefConfig, useComponentConfig } from './d-config';
export { usePrefixConfig, useThemeConfig, useGridConfig, useContentSVChangeConfig, useComponentConfig } from './d-config';
export { useRefSelector } from './element-ref';
export { useGeneralState, DGeneralStateContext } from './general-state';
export { useImmer, useImmerReducer } from './immer';
Expand Down
Loading

0 comments on commit d4ea59e

Please sign in to comment.