packages/next/src/form-layout/useResponsiveFormLayout.ts (95 lines of code) (raw):

import { useRef, useState, useEffect } from 'react' import { isArr, isValid } from '@formily/shared' interface IProps { breakpoints?: number[] layout?: | 'vertical' | 'horizontal' | 'inline' | ('vertical' | 'horizontal' | 'inline')[] labelCol?: number | number[] wrapperCol?: number | number[] labelAlign?: 'right' | 'left' | ('right' | 'left')[] wrapperAlign?: 'right' | 'left' | ('right' | 'left')[] [props: string]: any } interface ICalcBreakpointIndex { (originalBreakpoints: number[], width: number): number } interface ICalculateProps { (target: HTMLElement, props: IProps): IProps } interface IUseResponsiveFormLayout { (props: IProps): { ref: React.MutableRefObject<HTMLDivElement> props: any } } const calcBreakpointIndex: ICalcBreakpointIndex = (breakpoints, width) => { for (let i = 0; i < breakpoints.length; i++) { if (width <= breakpoints[i]) { return i } } } const calcFactor = <T>(value: T | T[], breakpointIndex: number): T => { if (Array.isArray(value)) { if (breakpointIndex === -1) return value[0] return value[breakpointIndex] ?? value[value.length - 1] } else { return value } } const factor = <T>(value: T | T[], breakpointIndex: number): T => isValid(value) ? calcFactor(value as any, breakpointIndex) : value const calculateProps: ICalculateProps = (target, props) => { const { clientWidth } = target const { breakpoints, layout, labelAlign, wrapperAlign, labelCol, wrapperCol, ...otherProps } = props const breakpointIndex = calcBreakpointIndex(breakpoints, clientWidth) return { layout: factor(layout, breakpointIndex), labelAlign: factor(labelAlign, breakpointIndex), wrapperAlign: factor(wrapperAlign, breakpointIndex), labelCol: factor(labelCol, breakpointIndex), wrapperCol: factor(wrapperCol, breakpointIndex), ...otherProps, } } export const useResponsiveFormLayout: IUseResponsiveFormLayout = (props) => { const ref = useRef<HTMLDivElement>(null) const { breakpoints } = props if (!isArr(breakpoints)) { return { ref, props } } const [layoutProps, setLayout] = useState<IProps>(props) const updateUI = () => { if (ref.current) { setLayout(calculateProps(ref.current, props)) } } useEffect(() => { const observer = () => { updateUI() } const resizeObserver = new ResizeObserver(observer) if (ref.current) { resizeObserver.observe(ref.current) } updateUI() return () => { resizeObserver.disconnect() } }, []) return { ref, props: layoutProps, } }