ant-design/components/_util/responsiveObserve.ts
azro352 18400fbdee
Make breakpoints use the token feature (#39105)
* Add XXXL size

* Update index.zh-CN.md

* Update components/list/index.zh-CN.md

Co-authored-by: Amumu <yoyo837@hotmail.com>

* Try to use `useToken` for building responsiveMap

* Try useResponsiveObserve

* Fix useResponsiveObserve

* Some try

* Some try

* Some try

* Remove impossible test

* Remove impossible test

* Fix token.screenXSMax

* Try to put test back as token.screenXSMax is fixed

* Remove impossible test now

* Subscribers, subuid and screen are no longer static

* reorganize def

* chore: not affect no-related file

* chore: not affect no-related file

* test: update test case

* chore: adjust memo logic

Co-authored-by: Amumu <yoyo837@hotmail.com>
Co-authored-by: 二货机器人 <smith3816@gmail.com>
2022-12-15 14:14:07 +08:00

86 lines
2.8 KiB
TypeScript

import React from 'react';
import type { GlobalToken } from '../theme/interface';
import { useToken } from '../theme/internal';
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
export type BreakpointMap = Record<Breakpoint, string>;
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
export type ScreenSizeMap = Partial<Record<Breakpoint, number>>;
export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
type SubscribeFunc = (screens: ScreenMap) => void;
const getResponsiveMap = (token: GlobalToken): BreakpointMap => ({
xs: `(max-width: ${token.screenXSMax}px)`,
sm: `(min-width: ${token.screenSM}px)`,
md: `(min-width: ${token.screenMD}px)`,
lg: `(min-width: ${token.screenLG}px)`,
xl: `(min-width: ${token.screenXL}px)`,
xxl: `(min-width: ${token.screenXXL}px)`,
});
export default function useResponsiveObserver() {
const [, token] = useToken();
const responsiveMap: BreakpointMap = getResponsiveMap(token);
// To avoid repeat create instance, we add `useMemo` here.
return React.useMemo(() => {
const subscribers = new Map<Number, SubscribeFunc>();
let subUid = -1;
let screens = {};
return {
matchHandlers: {} as {
[prop: string]: {
mql: MediaQueryList;
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
};
},
dispatch(pointMap: ScreenMap) {
screens = pointMap;
subscribers.forEach((func) => func(screens));
return subscribers.size >= 1;
},
subscribe(func: SubscribeFunc): number {
if (!subscribers.size) this.register();
subUid += 1;
subscribers.set(subUid, func);
func(screens);
return subUid;
},
unsubscribe(paramToken: number) {
subscribers.delete(paramToken);
if (!subscribers.size) this.unregister();
},
unregister() {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
const matchMediaQuery = responsiveMap[screen];
const handler = this.matchHandlers[matchMediaQuery];
handler?.mql.removeListener(handler?.listener);
});
subscribers.clear();
},
register() {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
const matchMediaQuery = responsiveMap[screen];
const listener = ({ matches }: { matches: boolean }) => {
this.dispatch({
...screens,
[screen]: matches,
});
};
const mql = window.matchMedia(matchMediaQuery);
mql.addListener(listener);
this.matchHandlers[matchMediaQuery] = {
mql,
listener,
};
listener(mql);
});
},
responsiveMap,
};
}, [token]);
}