UNPKG

@screensdev/styles

Version:

Cross-platform styles for React Native without the complexity.

107 lines (104 loc) 3.23 kB
"use strict"; import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { isWithinTheWidth } from "./media.js"; import { ContainerContext } from "./container-context.js"; const extractBreakpoints = responsiveKeys => { const breakpoints = new Set(); responsiveKeys.forEach(query => { const queryString = query.description; if (!queryString) { return; } if (!isContainerQuery(queryString)) { return; } const parsedQuery = parseContainerQuery(queryString); if (!isValidContainerQuery(parsedQuery)) { return; } if (parsedQuery.width?.from && parsedQuery.width?.from > 0) { breakpoints.add(parsedQuery.width.from); } if (parsedQuery.width?.to && parsedQuery.width?.to !== Infinity) { breakpoints.add(parsedQuery.width.to); } }); return breakpoints; }; /** * An optimized hook that returns the closest container dimensions only when needed. * @returns - containerWidth */ function useCachedContainerWidth(containerQueries) { const { registerBreakpointListener, removeBreakpointListener } = useContext(ContainerContext); const [currentWidth, setCurrentWidth] = useState(0); const handleBreakpointUpdate = useCallback(width => { setCurrentWidth(width); }, []); const breakpoints = useMemo(() => { return extractBreakpoints(containerQueries); }, [containerQueries]); useEffect(() => { registerBreakpointListener(breakpoints, handleBreakpointUpdate); return () => removeBreakpointListener(breakpoints, handleBreakpointUpdate); }, [breakpoints, handleBreakpointUpdate, registerBreakpointListener, removeBreakpointListener]); return currentWidth; } const CONTAINER_QUERY_REGEX = /:c:(w)\[(\d+)(?:,\s*(\d+|Infinity))?]/; const isContainerQuery = mq => CONTAINER_QUERY_REGEX.test(mq); const getContainerValue = value => { if (typeof value === 'number') { return value; } if (value === null) { return 0; } return 0; }; /** * Utility to create cross-platform container queries * @returns - JavaScript symbol to be used in your stylesheet */ const container = { width: (wMin = 0, wMax = Infinity) => Symbol(`:c:w[${getContainerValue(wMin)}, ${getContainerValue(wMax)}]`) }; const isValidContainerQuery = parsedContainerQuery => { const { width } = parsedContainerQuery; if (width) { return width.from <= width.to; } return false; }; const parseContainerQuery = mq => { const [, width, fromW, toW] = CONTAINER_QUERY_REGEX.exec(mq) || []; return { width: width ? { from: Number(fromW), to: Number(toW) } : undefined }; }; const matchContainerQuery = (queryKey, containerWidth) => { const queryString = queryKey.description; if (!queryString) { return false; } if (!isContainerQuery(queryString)) { return false; } const parsedQuery = parseContainerQuery(queryString); if (!isValidContainerQuery(parsedQuery)) { return false; } if (!parsedQuery?.width) { return false; } return isWithinTheWidth(parsedQuery.width, containerWidth); }; export { container, isContainerQuery, isValidContainerQuery, matchContainerQuery, useCachedContainerWidth }; //# sourceMappingURL=container.js.map